Decompiled source of DSPTaskMaster v1.0.0

DSPTaskMaster.dll

Decompiled 11 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using Microsoft.CodeAnalysis;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Events;
using UnityEngine.UI;
using crecheng.DSPModSave;

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

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace DSPTaskMaster
{
	public class Alerts
	{
		private enum State
		{
			Below,
			Met,
			Behind
		}

		private class Track
		{
			public State Confirmed;

			public State Pending;

			public float PendingSince;
		}

		private const float ReachedDelay = 2f;

		private const float ProblemDelay = 10f;

		private readonly Dictionary<TodoItem, Track> _tracks = new Dictionary<TodoItem, Track>();

		private float _clock;

		public void Reset()
		{
			_tracks.Clear();
		}

		public void Check(List<TodoItem> items)
		{
			_clock += 1f;
			if (_tracks.Count > 0)
			{
				HashSet<TodoItem> hashSet = new HashSet<TodoItem>(items);
				List<TodoItem> list = new List<TodoItem>();
				foreach (TodoItem key in _tracks.Keys)
				{
					if (!hashSet.Contains(key))
					{
						list.Add(key);
					}
				}
				foreach (TodoItem item in list)
				{
					_tracks.Remove(item);
				}
			}
			foreach (TodoItem item2 in items)
			{
				if (item2.Type == TaskType.Manual)
				{
					continue;
				}
				State state = (item2.IsCompleted ? ((!item2.IsBehind()) ? State.Met : State.Behind) : State.Below);
				if (!_tracks.TryGetValue(item2, out var value))
				{
					_tracks[item2] = new Track
					{
						Confirmed = state,
						Pending = state,
						PendingSince = _clock
					};
					continue;
				}
				if (state == value.Confirmed)
				{
					value.Pending = state;
					continue;
				}
				if (state != value.Pending)
				{
					value.Pending = state;
					value.PendingSince = _clock;
				}
				float num = ((state == State.Met) ? 2f : 10f);
				if (!(_clock - value.PendingSince < num))
				{
					State confirmed = value.Confirmed;
					value.Confirmed = state;
					Announce(item2, confirmed, state);
				}
			}
		}

		private static void Announce(TodoItem item, State old, State now)
		{
			Plugin instance = Plugin.Instance;
			if (!instance.AlertsEnabled.Value)
			{
				return;
			}
			string text = null;
			if (now == State.Met && old == State.Below)
			{
				text = ((item.Type == TaskType.Research) ? ("TaskMaster: " + item.Description + " done") : ("TaskMaster: " + item.Description + " reached (" + TaskText.ShortValue(item) + ")"));
			}
			else if (item.Type == TaskType.Surplus && instance.AlertBehind.Value)
			{
				if (now == State.Below)
				{
					text = "TaskMaster: " + item.Description + " dropped below target (" + TaskText.FmtSigned(item.CurrentAmount) + " /min)";
				}
			}
			else if (item.Type == TaskType.ProductionTarget && instance.AlertBehind.Value)
			{
				if (now == State.Met && old == State.Behind)
				{
					text = "TaskMaster: " + item.Description + " is back on track";
				}
				else
				{
					switch (now)
					{
					case State.Behind:
						text = "TaskMaster: " + item.Description + " is behind: " + TaskText.Fmt(item.ActualAmount) + " / " + TaskText.Fmt(item.TargetAmount) + " /min";
						break;
					case State.Below:
						text = "TaskMaster: " + item.Description + " dropped below target (" + TaskText.ShortValue(item) + " /min)";
						break;
					}
				}
			}
			if (text == null)
			{
				return;
			}
			try
			{
				UIRealtimeTip.Popup(text, true, 0);
			}
			catch (Exception ex)
			{
				instance.Log.LogWarning((object)("Could not show alert: " + ex.Message));
			}
		}
	}
	public static class GameStyle
	{
		public static readonly Color OverlayPanel = new Color(0.08f, 0.1f, 0.14f, 1f);

		public static readonly Color ButtonColor = new Color(1f, 1f, 1f, 0.12f);

		public static readonly Color SubtleButtonColor = new Color(1f, 1f, 1f, 0.07f);

		public static readonly Color AccentButtonColor = new Color(0.35f, 0.72f, 1f, 0.45f);

		public static readonly Color FieldColor = new Color(0f, 0f, 0f, 0.35f);

		public static readonly Color RowColor = new Color(0f, 0f, 0f, 0.28f);

		public static readonly Color RowDoneColor = new Color(0f, 0f, 0f, 0.14f);

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

		public static readonly Color TextColor = new Color(0.92f, 0.95f, 1f, 1f);

		public static readonly Color MutedText = new Color(0.62f, 0.68f, 0.76f, 1f);

		public static readonly Color IconColor = new Color(0.85f, 0.9f, 1f, 0.9f);

		private static bool _initialized;

		private static Font _fallbackFont;

		private static bool _naviSearched;

		private static Sprite _naviSprite;

		public static Font Font { get; private set; }

		public static Sprite PanelSprite { get; private set; }

		public static Sprite ButtonSprite { get; private set; }

		public static Sprite CloseSprite { get; private set; }

		public static void Init()
		{
			if (_initialized)
			{
				return;
			}
			_initialized = true;
			try
			{
				UIGame val = UIRoot.instance?.uiGame;
				if ((Object)(object)val != (Object)null)
				{
					Text[] componentsInChildren = ((Component)val).GetComponentsInChildren<Text>(true);
					foreach (Text val2 in componentsInChildren)
					{
						if ((Object)(object)val2.font != (Object)null && ((Object)val2.font).name != "Arial")
						{
							Font = val2.font;
							break;
						}
					}
					UIItemPicker itemPicker = val.itemPicker;
					if ((Object)(object)itemPicker != (Object)null)
					{
						RectTransform pickerTrans = itemPicker.pickerTrans;
						object panelSprite;
						if (pickerTrans == null)
						{
							panelSprite = null;
						}
						else
						{
							Image component = ((Component)pickerTrans).GetComponent<Image>();
							panelSprite = ((component != null) ? component.sprite : null);
						}
						PanelSprite = (Sprite)panelSprite;
						UIButton typeButton = itemPicker.typeButton1;
						object buttonSprite;
						if (typeButton == null)
						{
							buttonSprite = null;
						}
						else
						{
							Image component2 = ((Component)typeButton).GetComponent<Image>();
							buttonSprite = ((component2 != null) ? component2.sprite : null);
						}
						ButtonSprite = (Sprite)buttonSprite;
					}
					FindWindowAssets((Component)(object)val.controlPanelWindow);
					if ((Object)(object)CloseSprite == (Object)null)
					{
						FindWindowAssets((Component)(object)val.statWindow);
					}
				}
			}
			catch (Exception ex)
			{
				Plugin.Instance.Log.LogWarning((object)("GameStyle: could not read game UI assets: " + ex.Message));
			}
			ManualLogSource log = Plugin.Instance.Log;
			string[] obj = new string[8] { "GameStyle: font=", null, null, null, null, null, null, null };
			Font font = Font;
			obj[1] = ((font != null) ? ((Object)font).name : null) ?? "Arial (fallback)";
			obj[2] = ", panel=";
			Sprite panelSprite2 = PanelSprite;
			obj[3] = ((panelSprite2 != null) ? ((Object)panelSprite2).name : null) ?? "none";
			obj[4] = ", button=";
			Sprite buttonSprite2 = ButtonSprite;
			obj[5] = ((buttonSprite2 != null) ? ((Object)buttonSprite2).name : null) ?? "none";
			obj[6] = ", close=";
			Sprite closeSprite = CloseSprite;
			obj[7] = ((closeSprite != null) ? ((Object)closeSprite).name : null) ?? "none";
			log.LogInfo((object)string.Concat(obj));
		}

		private static void FindWindowAssets(Component root)
		{
			if ((Object)(object)root == (Object)null || !((Object)(object)CloseSprite == (Object)null))
			{
				return;
			}
			Button[] componentsInChildren = root.GetComponentsInChildren<Button>(true);
			foreach (Button val in componentsInChildren)
			{
				if (((Object)val).name.IndexOf("close", StringComparison.OrdinalIgnoreCase) < 0)
				{
					continue;
				}
				Image[] componentsInChildren2 = ((Component)val).GetComponentsInChildren<Image>(true);
				foreach (Image val2 in componentsInChildren2)
				{
					if ((Object)(object)val2.sprite != (Object)null)
					{
						CloseSprite = val2.sprite;
						break;
					}
				}
				if ((Object)(object)CloseSprite != (Object)null)
				{
					break;
				}
			}
		}

		public static Image CreateWindowBackground(GameObject go)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			Image obj = go.AddComponent<Image>();
			((Graphic)obj).color = OverlayPanel;
			((Graphic)obj).raycastTarget = true;
			return obj;
		}

		public static void StyleIconButton(Button btn, float iconSize)
		{
			//IL_0021: 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_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b2: 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_00b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
			Image component = ((Component)btn).GetComponent<Image>();
			component.sprite = null;
			((Graphic)component).color = new Color(0f, 0f, 0f, 0f);
			Text componentInChildren = ((Component)btn).GetComponentInChildren<Text>();
			if ((Object)(object)CloseSprite != (Object)null)
			{
				if ((Object)(object)componentInChildren != (Object)null)
				{
					((Behaviour)componentInChildren).enabled = false;
				}
				GameObject val = new GameObject("Icon");
				val.transform.SetParent(((Component)btn).transform, false);
				Image val2 = val.AddComponent<Image>();
				val2.sprite = CloseSprite;
				((Graphic)val2).color = IconColor;
				((Graphic)val2).raycastTarget = false;
				RectTransform rectTransform = ((Graphic)val2).rectTransform;
				Vector2 val3 = default(Vector2);
				((Vector2)(ref val3))..ctor(0.5f, 0.5f);
				rectTransform.pivot = val3;
				Vector2 anchorMin = (rectTransform.anchorMax = val3);
				rectTransform.anchorMin = anchorMin;
				rectTransform.sizeDelta = new Vector2(iconSize, iconSize);
				((Selectable)btn).targetGraphic = (Graphic)(object)val2;
			}
			else if ((Object)(object)componentInChildren != (Object)null)
			{
				componentInChildren.text = "×";
				componentInChildren.fontSize = Mathf.RoundToInt(iconSize * 1.4f);
				((Graphic)componentInChildren).color = IconColor;
				((Selectable)btn).targetGraphic = (Graphic)(object)componentInChildren;
			}
		}

		public static Sprite GetNavigationSprite()
		{
			if (_naviSearched)
			{
				return _naviSprite;
			}
			_naviSearched = true;
			try
			{
				UIControlPanelPlanetEntry[] array = Resources.FindObjectsOfTypeAll<UIControlPanelPlanetEntry>();
				for (int i = 0; i < array.Length; i++)
				{
					UIButton navigationButton = array[i].navigationButton;
					if ((Object)(object)navigationButton == (Object)null)
					{
						continue;
					}
					Image[] componentsInChildren = ((Component)navigationButton).GetComponentsInChildren<Image>(true);
					foreach (Image val in componentsInChildren)
					{
						if (!((Object)(object)((Component)val).gameObject == (Object)(object)((Component)navigationButton).gameObject) && !((Object)(object)val.sprite == (Object)null))
						{
							_naviSprite = val.sprite;
							break;
						}
					}
					if ((Object)(object)_naviSprite != (Object)null)
					{
						break;
					}
				}
			}
			catch (Exception ex)
			{
				Plugin.Instance.Log.LogWarning((object)("GameStyle: could not find the navigation icon: " + ex.Message));
			}
			ManualLogSource log = Plugin.Instance.Log;
			Sprite naviSprite = _naviSprite;
			log.LogInfo((object)("GameStyle: navigation icon=" + (((naviSprite != null) ? ((Object)naviSprite).name : null) ?? "none (using text)")));
			return _naviSprite;
		}

		public static Font GetFont()
		{
			if ((Object)(object)Font != (Object)null)
			{
				return Font;
			}
			if ((Object)(object)_fallbackFont == (Object)null)
			{
				_fallbackFont = Resources.GetBuiltinResource<Font>("Arial.ttf");
			}
			return _fallbackFont;
		}

		public static void ApplyPanel(Image img)
		{
			ApplySprite(img, PanelSprite);
		}

		public static void ApplyButton(Image img)
		{
			ApplySprite(img, ButtonSprite);
		}

		private static void ApplySprite(Image img, Sprite sprite)
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)img == (Object)null) && !((Object)(object)sprite == (Object)null))
			{
				img.sprite = sprite;
				img.type = (Type)((sprite.border != Vector4.zero) ? 1 : 0);
			}
		}
	}
	public enum TaskType
	{
		Manual,
		InventoryTarget,
		ProductionTarget,
		Research,
		Building,
		Sphere,
		Surplus
	}
	[Serializable]
	public class TodoItem
	{
		public string Description;

		public bool IsCompleted;

		public TaskType Type;

		public int ItemId;

		public int TargetAmount;

		public long CurrentAmount;

		public long ActualAmount;

		public int PlanetId;

		[NonSerialized]
		public long TotalAmount;

		[NonSerialized]
		public string StatusText;

		public const float ActualOkRatio = 0.9f;

		public bool IsBehind()
		{
			if (Type == TaskType.ProductionTarget && IsCompleted)
			{
				return (float)ActualAmount < (float)TargetAmount * 0.9f;
			}
			return false;
		}

		public bool IsBelowCapacity()
		{
			if (Type == TaskType.ProductionTarget && CurrentAmount > 0)
			{
				return (float)ActualAmount < (float)Math.Min(CurrentAmount, TargetAmount) * 0.9f;
			}
			return false;
		}

		public TodoItem(string desc, TaskType type, int itemId, int target, int planetId = 0)
		{
			Description = desc;
			Type = type;
			ItemId = itemId;
			TargetAmount = target;
			CurrentAmount = 0L;
			PlanetId = planetId;
		}
	}
	public class ItemDefinition
	{
		public int Id;

		public Sprite Icon;

		public string Name
		{
			get
			{
				ItemProto val = ((ProtoSet<ItemProto>)(object)LDB.items)?.Select(Id);
				if (val != null)
				{
					try
					{
						string text = Localization.Translate(((Proto)val).name);
						if (!string.IsNullOrEmpty(text))
						{
							return text;
						}
					}
					catch
					{
					}
					string name = ((Proto)val).name;
					if (!string.IsNullOrEmpty(name))
					{
						return name;
					}
					return "Unknown Item";
				}
				return "Unknown Item";
			}
		}
	}
	[BepInPlugin("com.jules.dsp.taskmaster", "DSP TaskMaster", "1.0.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class Plugin : BaseUnityPlugin, IModCanSave
	{
		public static Plugin Instance;

		private List<TodoItem> _todoItems = new List<TodoItem>();

		private Type _inputTypeCache;

		private MethodInfo _getKeyMethodCache;

		private MethodInfo _getKeyDownMethodCache;

		private bool _inputReflectionFailed;

		private float _checkTimer;

		private bool _itemsLoaded;

		private List<ItemDefinition> _cachedItems;

		private int _lastPlanetId;

		private int _lastLocalId = -1;

		public ConfigEntry<float> WindowX;

		public ConfigEntry<float> WindowY;

		public ConfigEntry<float> WindowWidth;

		public ConfigEntry<float> WindowHeight;

		public ConfigEntry<bool> AlertsEnabled;

		public ConfigEntry<bool> AlertBehind;

		public ConfigEntry<bool> OverlayEnabled;

		public ConfigEntry<float> OverlayX;

		public ConfigEntry<float> OverlayY;

		private readonly Alerts _alerts = new Alerts();

		public ManualLogSource Log => ((BaseUnityPlugin)this).Logger;

		private void Awake()
		{
			Instance = this;
			WindowX = ((BaseUnityPlugin)this).Config.Bind<float>("Window", "X", 0f, "Left edge relative to the screen center (UI units). Set Width to 0 to reset.");
			WindowY = ((BaseUnityPlugin)this).Config.Bind<float>("Window", "Y", 0f, "Top edge relative to the screen center (UI units).");
			WindowWidth = ((BaseUnityPlugin)this).Config.Bind<float>("Window", "Width", 0f, "Window width in UI units. 0 = automatic (fits screen size).");
			WindowHeight = ((BaseUnityPlugin)this).Config.Bind<float>("Window", "Height", 0f, "Window height in UI units. 0 = automatic (fits screen size).");
			AlertsEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Alerts", "Enabled", true, "Show an on-screen message when a tracked task reaches its target.");
			AlertBehind = ((BaseUnityPlugin)this).Config.Bind<bool>("Alerts", "Behind", true, "Also alert when a production task falls behind or drops below its target.");
			OverlayEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Overlay", "Enabled", true, "Show a small list of open tasks at the screen edge while the window is closed.");
			OverlayX = ((BaseUnityPlugin)this).Config.Bind<float>("Overlay", "X", 0f, "Right edge offset from the screen's right edge (UI units, negative = left). 0/0 = default position.");
			OverlayY = ((BaseUnityPlugin)this).Config.Bind<float>("Overlay", "Y", 0f, "Top edge offset from the screen's top edge (UI units, negative = down). 0/0 = default position.");
			((Component)this).gameObject.AddComponent<UIManager>();
			DSPModSavePlugin.AddModSaveManually((BaseUnityPlugin)(object)this);
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Plugin com.jules.dsp.taskmaster is loaded!");
		}

		public void TryLoadItems()
		{
			if (_itemsLoaded || GameMain.mainPlayer == null)
			{
				return;
			}
			_cachedItems = new List<ItemDefinition>();
			bool flag = false;
			try
			{
				if ((Object)(object)LDB.items != (Object)null && ((ProtoSet<ItemProto>)(object)LDB.items).dataArray != null)
				{
					ItemProto[] dataArray = ((ProtoSet<ItemProto>)(object)LDB.items).dataArray;
					foreach (ItemProto val in dataArray)
					{
						if (val != null && ((Proto)val).ID > 0)
						{
							flag = true;
							Sprite val2 = val.iconSprite;
							if ((Object)(object)val2 == (Object)null && !string.IsNullOrEmpty(val.IconPath))
							{
								val2 = Resources.Load<Sprite>(val.IconPath);
							}
							_cachedItems.Add(new ItemDefinition
							{
								Id = ((Proto)val).ID,
								Icon = val2
							});
						}
					}
				}
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("Error loading item database: " + ex.Message));
			}
			if (!flag)
			{
				_itemsLoaded = true;
				_cachedItems.Add(new ItemDefinition
				{
					Id = 1101
				});
				_cachedItems.Add(new ItemDefinition
				{
					Id = 1104
				});
				_cachedItems.Add(new ItemDefinition
				{
					Id = 1105
				});
				_cachedItems.Add(new ItemDefinition
				{
					Id = 1131
				});
			}
			else
			{
				_itemsLoaded = true;
			}
		}

		public List<ItemDefinition> GetCachedItems()
		{
			return _cachedItems;
		}

		public int GetCurrentPlanetId()
		{
			if (GameMain.localPlanet != null)
			{
				return GameMain.localPlanet.id;
			}
			return 0;
		}

		public bool IsOnPlanet()
		{
			return GameMain.localPlanet != null;
		}

		public int GetDisplayPlanetId()
		{
			int currentPlanetId = GetCurrentPlanetId();
			if (currentPlanetId == 0)
			{
				return _lastPlanetId;
			}
			return currentPlanetId;
		}

		public string GetPlanetName(int planetId)
		{
			if (planetId == 0)
			{
				return null;
			}
			try
			{
				GameData data = GameMain.data;
				object obj;
				if (data == null)
				{
					obj = null;
				}
				else
				{
					GalaxyData galaxy = data.galaxy;
					obj = ((galaxy != null) ? galaxy.PlanetById(planetId) : null);
				}
				PlanetData val = (PlanetData)obj;
				if (val != null)
				{
					return val.displayName;
				}
			}
			catch
			{
			}
			return "Planet " + planetId;
		}

		public void Export(BinaryWriter w)
		{
			SaveManager.Write(w, _todoItems, GetDisplayPlanetId());
		}

		public void Import(BinaryReader r)
		{
			try
			{
				int lastPlanetId;
				List<TodoItem> list = SaveManager.Read(r, out lastPlanetId);
				SetTodoItems(list, lastPlanetId);
				((BaseUnityPlugin)this).Logger.LogInfo((object)$"Loaded {list.Count} tasks from save.");
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("Could not read tasks from save: " + ex.Message));
				SetTodoItems(new List<TodoItem>());
			}
		}

		public void IntoOtherSave()
		{
			SetTodoItems(new List<TodoItem>());
		}

		public List<TodoItem> GetTodoItems()
		{
			return _todoItems;
		}

		public void SetTodoItems(List<TodoItem> items, int lastPlanetId = 0)
		{
			_todoItems = items ?? new List<TodoItem>();
			_lastPlanetId = lastPlanetId;
			_lastLocalId = -1;
			_alerts.Reset();
			if ((Object)(object)UIManager.Instance != (Object)null)
			{
				UIManager.Instance.RefreshUI();
			}
		}

		private void Update()
		{
			bool flag = false;
			if (!_inputReflectionFailed)
			{
				try
				{
					if (_inputTypeCache == null)
					{
						_inputTypeCache = Type.GetType("UnityEngine.Input, UnityEngine.InputLegacyModule");
						if (_inputTypeCache == null)
						{
							_inputTypeCache = Type.GetType("UnityEngine.Input, UnityEngine.CoreModule");
						}
						if (_inputTypeCache == null)
						{
							_inputTypeCache = Type.GetType("UnityEngine.Input, UnityEngine");
						}
					}
					if (_inputTypeCache != null)
					{
						if (_getKeyMethodCache == null)
						{
							_getKeyMethodCache = _inputTypeCache.GetMethod("GetKey", new Type[1] { typeof(KeyCode) });
						}
						if (_getKeyDownMethodCache == null)
						{
							_getKeyDownMethodCache = _inputTypeCache.GetMethod("GetKeyDown", new Type[1] { typeof(KeyCode) });
						}
						if (_getKeyMethodCache != null && _getKeyDownMethodCache != null)
						{
							bool flag2 = (bool)_getKeyMethodCache.Invoke(null, new object[1] { (object)(KeyCode)308 });
							bool flag3 = (bool)_getKeyMethodCache.Invoke(null, new object[1] { (object)(KeyCode)307 });
							if ((flag2 || flag3) && (bool)_getKeyDownMethodCache.Invoke(null, new object[1] { (object)(KeyCode)116 }))
							{
								flag = true;
							}
						}
						else
						{
							((BaseUnityPlugin)this).Logger.LogError((object)"Input reflection failed: method cache is null");
							_inputReflectionFailed = true;
						}
					}
					else
					{
						((BaseUnityPlugin)this).Logger.LogError((object)"Input reflection failed: _inputTypeCache is null");
						_inputReflectionFailed = true;
					}
				}
				catch (Exception ex)
				{
					((BaseUnityPlugin)this).Logger.LogError((object)("Error reading input via reflection: " + ex.Message));
					_inputReflectionFailed = true;
				}
			}
			if (flag && (Object)(object)UIManager.Instance != (Object)null)
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)"ToggleWindow called via shortcut!");
				UIManager.Instance.ToggleWindow();
			}
			int currentPlanetId = GetCurrentPlanetId();
			if (currentPlanetId != _lastLocalId)
			{
				_lastLocalId = currentPlanetId;
				if (currentPlanetId != 0)
				{
					_lastPlanetId = currentPlanetId;
				}
				CheckTargets();
				if ((Object)(object)UIManager.Instance != (Object)null)
				{
					UIManager.Instance.OnLocalPlanetChanged();
					UIManager.Instance.RefreshUI();
				}
			}
			_checkTimer += Time.deltaTime;
			if (_checkTimer >= 1f)
			{
				_checkTimer = 0f;
				if (!_itemsLoaded)
				{
					TryLoadItems();
				}
				CheckTargets();
				_alerts.Check(_todoItems);
				if ((Object)(object)UIManager.Instance != (Object)null)
				{
					UIManager.Instance.UpdateValues();
					UIManager.Instance.UpdateOverlay();
				}
			}
		}

		private void CheckTargets()
		{
			if (_todoItems.Count == 0)
			{
				return;
			}
			bool flag = false;
			bool flag2 = false;
			HashSet<int> hashSet = new HashSet<int>();
			foreach (TodoItem todoItem in _todoItems)
			{
				try
				{
					switch (todoItem.Type)
					{
					case TaskType.InventoryTarget:
						UpdateInventoryTask(todoItem);
						break;
					case TaskType.Research:
						UpdateResearchTask(todoItem);
						break;
					case TaskType.Building:
						UpdateBuildingTask(todoItem);
						break;
					case TaskType.Sphere:
						UpdateSphereTask(todoItem);
						break;
					case TaskType.Surplus:
						UpdateSurplusTask(todoItem);
						break;
					case TaskType.ProductionTarget:
						flag = true;
						if (todoItem.PlanetId == 0)
						{
							flag2 = true;
						}
						else
						{
							hashSet.Add(todoItem.PlanetId);
						}
						break;
					}
				}
				catch (Exception ex)
				{
					((BaseUnityPlugin)this).Logger.LogWarning((object)("Could not update task '" + todoItem.Description + "': " + ex.Message));
				}
			}
			if (!flag || GameMain.data == null || GameMain.data.factories == null)
			{
				return;
			}
			Dictionary<int, Dictionary<int, long>> dictionary = new Dictionary<int, Dictionary<int, long>>();
			Dictionary<int, Dictionary<int, long>> dictionary2 = new Dictionary<int, Dictionary<int, long>>();
			PlanetFactory[] factories = GameMain.data.factories;
			FactoryProductionStat[] array = GameMain.data.statistics?.production?.factoryStatPool;
			for (int i = 0; i < factories.Length && i < GameMain.data.factoryCount; i++)
			{
				PlanetFactory val = factories[i];
				if (val == null)
				{
					continue;
				}
				int planetId = val.planetId;
				if (!flag2 && !hashSet.Contains(planetId))
				{
					continue;
				}
				try
				{
					dictionary[planetId] = CalculateCapacity(val);
				}
				catch (Exception ex2)
				{
					((BaseUnityPlugin)this).Logger.LogWarning((object)$"Capacity calculation failed for planet {planetId}: {ex2.Message}");
					dictionary[planetId] = new Dictionary<int, long>();
				}
				Dictionary<int, long> dictionary3 = (dictionary2[planetId] = new Dictionary<int, long>());
				if (array == null || val.index < 0 || val.index >= array.Length || array[val.index] == null)
				{
					continue;
				}
				foreach (TodoItem todoItem2 in _todoItems)
				{
					if (todoItem2.Type == TaskType.ProductionTarget && !dictionary3.ContainsKey(todoItem2.ItemId) && (todoItem2.PlanetId == 0 || todoItem2.PlanetId == planetId))
					{
						dictionary3[todoItem2.ItemId] = GetProducedLastMinute(array[val.index], todoItem2.ItemId);
					}
				}
			}
			foreach (TodoItem todoItem3 in _todoItems)
			{
				if (todoItem3.Type == TaskType.ProductionTarget)
				{
					todoItem3.CurrentAmount = SumForItem(dictionary, todoItem3);
					todoItem3.ActualAmount = SumForItem(dictionary2, todoItem3);
					todoItem3.IsCompleted = todoItem3.TargetAmount > 0 && todoItem3.CurrentAmount >= todoItem3.TargetAmount;
				}
			}
		}

		private void UpdateInventoryTask(TodoItem item)
		{
			if (GameMain.mainPlayer != null && GameMain.mainPlayer.package != null)
			{
				item.CurrentAmount = GameMain.mainPlayer.package.GetItemCount(item.ItemId);
				item.IsCompleted = item.TargetAmount > 0 && item.CurrentAmount >= item.TargetAmount;
			}
		}

		private static void UpdateResearchTask(TodoItem item)
		{
			//IL_0028: 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_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			GameHistoryData history = GameMain.history;
			if (history != null && history.techStates != null)
			{
				if (history.techStates.TryGetValue(item.ItemId, out var value))
				{
					item.IsCompleted = value.unlocked;
					item.TotalAmount = value.hashNeeded;
					item.CurrentAmount = (value.unlocked ? value.hashNeeded : value.hashUploaded);
				}
				if (item.IsCompleted)
				{
					item.StatusText = "Done";
				}
				else if (history.currentTech == item.ItemId)
				{
					item.StatusText = "Researching";
				}
				else if (IsInTechQueue(history, item.ItemId))
				{
					item.StatusText = "In queue";
				}
				else
				{
					item.StatusText = "Not queued";
				}
			}
		}

		private static bool IsInTechQueue(GameHistoryData history, int techId)
		{
			int[] techQueue = history.techQueue;
			if (techQueue == null)
			{
				return false;
			}
			for (int i = 0; i < history.techQueueLength && i < techQueue.Length; i++)
			{
				if (techQueue[i] == techId)
				{
					return true;
				}
			}
			return false;
		}

		private static void UpdateBuildingTask(TodoItem item)
		{
			long num = 0L;
			foreach (PlanetFactory factory in GetFactories(item.PlanetId))
			{
				EntityData[] entityPool = factory.entityPool;
				if (entityPool == null)
				{
					continue;
				}
				for (int i = 1; i < factory.entityCursor && i < entityPool.Length; i++)
				{
					if (entityPool[i].id == i && entityPool[i].protoId == item.ItemId)
					{
						num++;
					}
				}
			}
			item.CurrentAmount = num;
			item.IsCompleted = item.TargetAmount > 0 && num >= item.TargetAmount;
		}

		private static void UpdateSphereTask(TodoItem item)
		{
			DysonSphere dysonSphere = GetDysonSphere(item.ItemId);
			long num = ((dysonSphere != null) ? (dysonSphere.energyGenCurrentTick * 60) : 0);
			item.CurrentAmount = num / 1000000;
			item.IsCompleted = item.TargetAmount > 0 && item.CurrentAmount >= item.TargetAmount;
		}

		private static void UpdateSurplusTask(TodoItem item)
		{
			GetMadeAndUsed(item.PlanetId, item.ItemId, out var made, out var used);
			item.ActualAmount = made;
			item.TotalAmount = used;
			item.CurrentAmount = made - used;
			item.IsCompleted = item.TargetAmount > 0 && item.CurrentAmount >= item.TargetAmount;
		}

		public static void GetMadeAndUsed(int planetId, int itemId, out long made, out long used)
		{
			made = 0L;
			used = 0L;
			FactoryProductionStat[] array = GameMain.data?.statistics?.production?.factoryStatPool;
			if (array == null)
			{
				return;
			}
			foreach (PlanetFactory factory in GetFactories(planetId))
			{
				if (factory.index >= 0 && factory.index < array.Length && array[factory.index] != null)
				{
					made += GetProducedLastMinute(array[factory.index], itemId);
					used += GetConsumedLastMinute(array[factory.index], itemId);
				}
			}
		}

		public static DysonSphere GetDysonSphere(int starId)
		{
			DysonSphere[] array = GameMain.data?.dysonSpheres;
			GameData data = GameMain.data;
			object obj;
			if (data == null)
			{
				obj = null;
			}
			else
			{
				GalaxyData galaxy = data.galaxy;
				obj = ((galaxy != null) ? galaxy.StarById(starId) : null);
			}
			StarData val = (StarData)obj;
			if (array == null || val == null || val.index < 0 || val.index >= array.Length)
			{
				return null;
			}
			return array[val.index];
		}

		public static string GetStarName(int starId)
		{
			GameData data = GameMain.data;
			object obj;
			if (data == null)
			{
				obj = null;
			}
			else
			{
				GalaxyData galaxy = data.galaxy;
				obj = ((galaxy != null) ? galaxy.StarById(starId) : null);
			}
			StarData val = (StarData)obj;
			if (val == null)
			{
				return "Star " + starId;
			}
			return val.displayName;
		}

		public static IEnumerable<PlanetFactory> GetFactories(int planetId)
		{
			GameData data = GameMain.data;
			if (data == null || data.factories == null)
			{
				yield break;
			}
			for (int i = 0; i < data.factories.Length && i < data.factoryCount; i++)
			{
				PlanetFactory val = data.factories[i];
				if (val != null && (planetId == 0 || val.planetId == planetId))
				{
					yield return val;
				}
			}
		}

		private static long SumForItem(Dictionary<int, Dictionary<int, long>> perPlanet, TodoItem item)
		{
			long num = 0L;
			foreach (KeyValuePair<int, Dictionary<int, long>> item2 in perPlanet)
			{
				if ((item.PlanetId == 0 || item2.Key == item.PlanetId) && item2.Value.TryGetValue(item.ItemId, out var value))
				{
					num += value;
				}
			}
			return num;
		}

		internal static long GetProducedLastMinute(FactoryProductionStat stat, int itemId)
		{
			return GetStatTotal(stat, itemId, 1);
		}

		internal static long GetConsumedLastMinute(FactoryProductionStat stat, int itemId)
		{
			return GetStatTotal(stat, itemId, 8);
		}

		private static long GetStatTotal(FactoryProductionStat stat, int itemId, int totalIndex)
		{
			int[] productIndices = stat.productIndices;
			if (productIndices == null || itemId <= 0 || itemId >= productIndices.Length)
			{
				return 0L;
			}
			int num = productIndices[itemId];
			if (num <= 0 || stat.productPool == null || num >= stat.productPool.Length)
			{
				return 0L;
			}
			ProductStat val = stat.productPool[num];
			if (val == null || val.total == null || val.total.Length <= totalIndex)
			{
				return 0L;
			}
			return val.total[totalIndex];
		}

		private static void AddRate(Dictionary<int, double> totals, int itemId, double perMinute)
		{
			if (itemId > 0 && !(perMinute <= 0.0))
			{
				if (totals.ContainsKey(itemId))
				{
					totals[itemId] += perMinute;
				}
				else
				{
					totals[itemId] = perMinute;
				}
			}
		}

		private static Dictionary<int, long> CalculateCapacity(PlanetFactory factory)
		{
			Dictionary<int, double> dictionary = new Dictionary<int, double>();
			FactorySystem factorySystem = factory.factorySystem;
			if (factorySystem == null)
			{
				return new Dictionary<int, long>();
			}
			if (factorySystem.assemblerPool != null)
			{
				for (int i = 1; i < factorySystem.assemblerCursor; i++)
				{
					ref AssemblerComponent reference = ref factorySystem.assemblerPool[i];
					if (reference.id == i && reference.recipeId > 0 && reference.recipeExecuteData != null)
					{
						AddRecipeOutput(dictionary, reference.recipeExecuteData, Math.Max(reference.speed, reference.speedOverride), reference.extraSpeed);
					}
				}
			}
			if (factorySystem.labPool != null)
			{
				for (int j = 1; j < factorySystem.labCursor; j++)
				{
					ref LabComponent reference2 = ref factorySystem.labPool[j];
					if (reference2.id == j && !reference2.researchMode && reference2.recipeId > 0 && reference2.recipeExecuteData != null)
					{
						AddRecipeOutput(dictionary, reference2.recipeExecuteData, Math.Max(reference2.speed, reference2.speedOverride), reference2.extraSpeed);
					}
				}
			}
			if (factorySystem.minerPool != null)
			{
				float miningSpeed = ((GameMain.history != null) ? GameMain.history.miningSpeedScale : 1f);
				for (int k = 1; k < factorySystem.minerCursor; k++)
				{
					ref MinerComponent reference3 = ref factorySystem.minerPool[k];
					if (reference3.id == k)
					{
						int productId;
						double perMinute = MinerRatePerMinute(ref reference3, factory.veinPool, miningSpeed, out productId);
						AddRate(dictionary, productId, perMinute);
					}
				}
			}
			BeltComponent[] array = factory.cargoTraffic?.beltPool;
			if (factorySystem.fractionatorPool != null && array != null)
			{
				for (int l = 1; l < factorySystem.fractionatorCursor; l++)
				{
					ref FractionatorComponent reference4 = ref factorySystem.fractionatorPool[l];
					if (reference4.id == l && reference4.productId > 0)
					{
						int num = 0;
						if (reference4.belt0 > 0 && !reference4.isOutput0 && reference4.belt0 < array.Length)
						{
							num = Math.Max(num, array[reference4.belt0].speed);
						}
						if (reference4.belt1 > 0 && !reference4.isOutput1 && reference4.belt1 < array.Length)
						{
							num = Math.Max(num, array[reference4.belt1].speed);
						}
						if (reference4.belt2 > 0 && !reference4.isOutput2 && reference4.belt2 < array.Length)
						{
							num = Math.Max(num, array[reference4.belt2].speed);
						}
						if (num > 0)
						{
							double num2 = Math.Min((double)num * 6.0, 30.0);
							double num3 = ((reference4.fluidInputCount > 0 && reference4.fluidInputCargoCount > 0.0001f) ? Math.Max(1.0, (double)reference4.fluidInputCount / (double)reference4.fluidInputCargoCount) : 1.0);
							int num4 = ((reference4.fluidInputCount > 0) ? (reference4.fluidInputInc / reference4.fluidInputCount) : 0);
							double num5 = Cargo.accTableMilli[Mathf.Clamp(num4, 0, 10)];
							AddRate(dictionary, reference4.productId, num2 * num3 * 60.0 * (double)reference4.produceProb * (1.0 + num5));
						}
					}
				}
			}
			Dictionary<int, long> dictionary2 = new Dictionary<int, long>(dictionary.Count);
			foreach (KeyValuePair<int, double> item in dictionary)
			{
				long num6 = (long)(item.Value + 0.5);
				if (num6 > 0)
				{
					dictionary2[item.Key] = num6;
				}
			}
			return dictionary2;
		}

		private static void AddRecipeOutput(Dictionary<int, double> totals, RecipeExecuteData recipe, int speed, int extraSpeed)
		{
			if (recipe.products == null || recipe.productCounts == null)
			{
				return;
			}
			double num = CyclesPerMinute(recipe, speed, extraSpeed);
			if (!(num <= 0.0))
			{
				for (int i = 0; i < recipe.products.Length && i < recipe.productCounts.Length; i++)
				{
					AddRate(totals, recipe.products[i], num * (double)recipe.productCounts[i]);
				}
			}
		}

		internal static double MinerRatePerMinute(ref MinerComponent m, VeinData[] veinPool, float miningSpeed, out int productId)
		{
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Expected I4, but got Unknown
			productId = m.productId;
			if (m.period <= 0)
			{
				return 0.0;
			}
			int num = ((m.veinCount > 0 && m.veins != null && m.veins.Length != 0) ? m.veins[0] : 0);
			bool flag = veinPool != null && num > 0 && num < veinPool.Length;
			if (productId == 0 && flag)
			{
				productId = veinPool[num].productId;
			}
			if (productId <= 0)
			{
				return 0.0;
			}
			EMinerType type = m.type;
			double num2;
			switch (type - 1)
			{
			case 1:
				num2 = m.veinCount;
				break;
			case 2:
				num2 = (flag ? ((float)veinPool[num].amount * VeinData.oilSpeedMultiplier) : 0f);
				break;
			case 0:
				num2 = 1.0;
				break;
			default:
				return 0.0;
			}
			return (double)m.speed * (double)miningSpeed * num2 / (double)m.period * 3600.0;
		}

		internal static double CyclesPerMinute(RecipeExecuteData recipe, int speed, int extraSpeed)
		{
			if (recipe == null || recipe.timeSpend <= 0 || speed <= 0)
			{
				return 0.0;
			}
			double num = 3600.0 * (double)speed / (double)recipe.timeSpend;
			if (extraSpeed > 0 && recipe.extraTimeSpend > 0)
			{
				num += 3600.0 * (double)extraSpeed / (double)recipe.extraTimeSpend;
			}
			return num;
		}
	}
	public class MachineGroup
	{
		public int BuildingId;

		public int Count;

		public double TotalRate;

		public RecipeExecuteData Recipe;

		public int Veins;

		public bool Planned;

		public double RatePerMachine
		{
			get
			{
				if (Count <= 0)
				{
					return 0.0;
				}
				return TotalRate / (double)Count;
			}
		}

		public double RatePerVein
		{
			get
			{
				if (Veins <= 0)
				{
					return 0.0;
				}
				return TotalRate / (double)Veins;
			}
		}
	}
	public class InputReport
	{
		public int ItemId;

		public double NeededPerMin;

		public long MadePerMin;

		public long UsedPerMin;

		public int ImportStations;

		public long ImportStock;

		public long ImportIncoming;

		public MachineGroup Makers;

		public int MakersPlaced;

		public bool IsImported => ImportStations > 0;

		public long AvailablePerMin => Math.Max(MadePerMin, UsedPerMin);
	}
	public class ProductionReport
	{
		public int Machines;

		public int Working;

		public int MissingInput;

		public int OutputFull;

		public int LowPower;

		public int NoPower;

		public int Idle;

		public int Depleted;

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

		public MachineGroup Main;

		public RecipeProto PlannedRecipe;

		public readonly List<InputReport> Inputs = new List<InputReport>();
	}
	public static class ProductionInsight
	{
		public static ProductionReport Analyze(TodoItem task)
		{
			//IL_02eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f1: Invalid comparison between Unknown and I4
			ProductionReport productionReport = new ProductionReport();
			List<PlanetFactory> list = Plugin.GetFactories(task.PlanetId).ToList();
			Dictionary<int, Dictionary<long, MachineGroup>> dictionary = new Dictionary<int, Dictionary<long, MachineGroup>>();
			Dictionary<int, int> dictionary2 = new Dictionary<int, int>();
			foreach (PlanetFactory item in list)
			{
				FactorySystem factorySystem = item.factorySystem;
				if (factorySystem == null)
				{
					continue;
				}
				if (factorySystem.assemblerPool != null)
				{
					for (int i = 1; i < factorySystem.assemblerCursor; i++)
					{
						ref AssemblerComponent reference = ref factorySystem.assemblerPool[i];
						if (reference.id == i && reference.recipeId > 0 && reference.recipeExecuteData != null)
						{
							int buildingId = GetBuildingId(item, reference.entityId);
							dictionary2[buildingId] = ((!dictionary2.TryGetValue(buildingId, out var value)) ? 1 : (value + 1));
							AddMachine(dictionary, buildingId, reference.recipeId, reference.recipeExecuteData, Math.Max(reference.speed, reference.speedOverride), reference.extraSpeed);
							if (Makes(reference.recipeExecuteData, task.ItemId))
							{
								Classify(productionReport, item, reference.recipeExecuteData, reference.replicating, reference.time, reference.served, reference.pcId);
							}
						}
					}
				}
				if (factorySystem.labPool != null)
				{
					for (int j = 1; j < factorySystem.labCursor; j++)
					{
						ref LabComponent reference2 = ref factorySystem.labPool[j];
						if (reference2.id == j && !reference2.researchMode && reference2.recipeId > 0 && reference2.recipeExecuteData != null)
						{
							int buildingId2 = GetBuildingId(item, reference2.entityId);
							dictionary2[buildingId2] = ((!dictionary2.TryGetValue(buildingId2, out var value2)) ? 1 : (value2 + 1));
							AddMachine(dictionary, buildingId2, reference2.recipeId, reference2.recipeExecuteData, Math.Max(reference2.speed, reference2.speedOverride), reference2.extraSpeed);
							if (Makes(reference2.recipeExecuteData, task.ItemId))
							{
								Classify(productionReport, item, reference2.recipeExecuteData, reference2.replicating, reference2.time, reference2.served, reference2.pcId);
							}
						}
					}
				}
				if (factorySystem.minerPool == null)
				{
					continue;
				}
				float miningSpeed = ((GameMain.history != null) ? GameMain.history.miningSpeedScale : 1f);
				for (int k = 1; k < factorySystem.minerCursor; k++)
				{
					ref MinerComponent reference3 = ref factorySystem.minerPool[k];
					if (reference3.id != k)
					{
						continue;
					}
					int productId;
					double num = Plugin.MinerRatePerMinute(ref reference3, item.veinPool, miningSpeed, out productId);
					if (productId > 0)
					{
						int buildingId3 = GetBuildingId(item, reference3.entityId);
						MachineGroup machineGroup = GetGroup(dictionary, productId, buildingId3, 0, null);
						machineGroup.Count++;
						machineGroup.TotalRate += num;
						if ((int)reference3.type == 2)
						{
							machineGroup.Veins += reference3.veinCount;
						}
						if (productId == task.ItemId)
						{
							ClassifyMiner(productionReport, item, ref reference3);
						}
					}
				}
			}
			productionReport.Main = MainGroup(dictionary, task.ItemId);
			if (productionReport.Main == null && productionReport.Machines == 0 && task.CurrentAmount <= 0)
			{
				productionReport.PlannedRecipe = PickRecipe(task.ItemId);
				productionReport.Main = PlannedGroup(productionReport.PlannedRecipe, task.ItemId, dictionary2);
				if (productionReport.Main == null)
				{
					productionReport.PlannedRecipe = null;
				}
			}
			if (productionReport.Main == null || task.TargetAmount <= 0)
			{
				return productionReport;
			}
			RecipeExecuteData recipe = productionReport.Main.Recipe;
			if (recipe == null)
			{
				return productionReport;
			}
			int num2 = ProductCount(recipe, task.ItemId);
			if (num2 <= 0 || recipe.requires == null || recipe.requireCounts == null)
			{
				return productionReport;
			}
			FactoryProductionStat[] array = GameMain.data?.statistics?.production?.factoryStatPool;
			for (int l = 0; l < recipe.requires.Length && l < recipe.requireCounts.Length; l++)
			{
				int num3 = recipe.requires[l];
				InputReport inputReport = new InputReport
				{
					ItemId = num3,
					NeededPerMin = (double)task.TargetAmount * (double)recipe.requireCounts[l] / (double)num2,
					Makers = MainGroup(dictionary, num3)
				};
				if (dictionary.TryGetValue(num3, out var value3))
				{
					inputReport.MakersPlaced = value3.Values.Sum((MachineGroup g) => g.Count);
				}
				foreach (PlanetFactory item2 in list)
				{
					if (array != null && item2.index >= 0 && item2.index < array.Length && array[item2.index] != null)
					{
						inputReport.MadePerMin += Plugin.GetProducedLastMinute(array[item2.index], num3);
						inputReport.UsedPerMin += Plugin.GetConsumedLastMinute(array[item2.index], num3);
					}
					AddImports(inputReport, item2);
				}
				if (inputReport.Makers == null && !inputReport.IsImported)
				{
					inputReport.Makers = PlannedGroup(PickRecipe(num3), num3, dictionary2);
				}
				productionReport.Inputs.Add(inputReport);
			}
			return productionReport;
		}

		private static void AddImports(InputReport input, PlanetFactory factory)
		{
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Invalid comparison between Unknown and I4
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: 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)
			StationComponent[] array = factory.transport?.stationPool;
			if (array == null)
			{
				return;
			}
			for (int i = 1; i < factory.transport.stationCursor && i < array.Length; i++)
			{
				StationComponent val = array[i];
				if (val == null || val.id != i || !val.isStellar || val.storage == null)
				{
					continue;
				}
				StationStore[] storage = val.storage;
				foreach (StationStore val2 in storage)
				{
					if (val2.itemId == input.ItemId && (int)val2.remoteLogic == 2)
					{
						input.ImportStations++;
						input.ImportStock += val2.count;
						if (val2.remoteOrder > 0)
						{
							input.ImportIncoming += val2.remoteOrder;
						}
					}
				}
			}
		}

		private static RecipeProto PickRecipe(int itemId)
		{
			ItemProto item = ((ProtoSet<ItemProto>)(object)LDB.items)?.Select(itemId);
			if (item == null)
			{
				return null;
			}
			List<RecipeProto> list = new List<RecipeProto>();
			if (item.maincraft != null)
			{
				list.Add(item.maincraft);
			}
			if (item.recipes != null)
			{
				list.AddRange(item.recipes.Where((RecipeProto r) => r != null && r != item.maincraft));
			}
			list = list.Where((RecipeProto r) => RunsInMachine(r.Type) && ProductCount(r.Results, r.ResultCounts, itemId) > 0).ToList();
			if (list.Count == 0)
			{
				return null;
			}
			GameHistoryData history = GameMain.history;
			return ((IEnumerable<RecipeProto>)list).FirstOrDefault((Func<RecipeProto, bool>)((RecipeProto r) => history == null || history.RecipeUnlocked(((Proto)r).ID))) ?? list[0];
		}

		private static MachineGroup PlannedGroup(RecipeProto recipe, int itemId, Dictionary<int, int> buildings)
		{
			//IL_015b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0208: Unknown result type (might be due to invalid IL or missing references)
			//IL_0215: Expected O, but got Unknown
			if (recipe == null || (Object)(object)LDB.items == (Object)null)
			{
				return null;
			}
			ItemProto val = (from kv in buildings
				select (proto: ((ProtoSet<ItemProto>)(object)LDB.items).Select(kv.Key), count: kv.Value) into b
				where BuildingSpeed(b.proto, recipe.Type) > 0
				orderby b.count descending
				select b.proto).FirstOrDefault();
			if (val == null && ((ProtoSet<ItemProto>)(object)LDB.items).dataArray != null)
			{
				GameHistoryData history = GameMain.history;
				List<ItemProto> source = ((ProtoSet<ItemProto>)(object)LDB.items).dataArray.Where((ItemProto p) => p != null && p.CanBuild && BuildingSpeed(p, recipe.Type) > 0).ToList();
				val = (from p in source
					where history == null || history.ItemUnlocked(((Proto)p).ID)
					orderby BuildingSpeed(p, recipe.Type) descending
					select p).FirstOrDefault() ?? source.OrderBy((ItemProto p) => BuildingSpeed(p, recipe.Type)).FirstOrDefault();
			}
			if (val == null)
			{
				return null;
			}
			if (recipe.TimeSpend <= 0)
			{
				return null;
			}
			double num = 3600.0 * (double)BuildingSpeed(val, recipe.Type) / ((double)recipe.TimeSpend * 10000.0);
			RecipeExecuteData value;
			return new MachineGroup
			{
				BuildingId = ((Proto)val).ID,
				Count = 1,
				Planned = true,
				Recipe = (RecipeExecuteData)((RecipeProto.recipeExecuteData != null && RecipeProto.recipeExecuteData.TryGetValue(((Proto)recipe).ID, out value)) ? ((object)value) : ((object)new RecipeExecuteData(recipe.Items, recipe.ItemCounts, recipe.Results, recipe.ResultCounts, recipe.TimeSpend * 10000, 0, recipe.productive))),
				TotalRate = num * (double)ProductCount(recipe.Results, recipe.ResultCounts, itemId)
			};
		}

		private static bool RunsInMachine(ERecipeType type)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Invalid comparison between Unknown and I4
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Invalid comparison between Unknown and I4
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Invalid comparison between Unknown and I4
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Invalid comparison between Unknown and I4
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Invalid comparison between Unknown and I4
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Invalid comparison between Unknown and I4
			if ((int)type != 1 && (int)type != 2 && (int)type != 3 && (int)type != 4 && (int)type != 5)
			{
				return (int)type == 15;
			}
			return true;
		}

		private static int BuildingSpeed(ItemProto building, ERecipeType type)
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Invalid comparison between Unknown and I4
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			PrefabDesc val = building?.prefabDesc;
			if (val == null)
			{
				return 0;
			}
			if ((int)type == 15)
			{
				if (!val.isLab)
				{
					return 0;
				}
				return val.labAssembleSpeed;
			}
			if (!val.isAssembler || val.assemblerRecipeType != type)
			{
				return 0;
			}
			return val.assemblerSpeed;
		}

		public static int BuildingsNeeded(double perMinute, MachineGroup group)
		{
			if (group == null || group.RatePerMachine <= 0.0)
			{
				return 0;
			}
			return (int)Math.Ceiling(perMinute / group.RatePerMachine - 0.001);
		}

		private static int GetBuildingId(PlanetFactory factory, int entityId)
		{
			EntityData[] entityPool = factory.entityPool;
			if (entityPool == null || entityId <= 0 || entityId >= entityPool.Length)
			{
				return 0;
			}
			return entityPool[entityId].protoId;
		}

		private static bool Makes(RecipeExecuteData recipe, int itemId)
		{
			return ProductCount(recipe, itemId) > 0;
		}

		private static int ProductCount(RecipeExecuteData recipe, int itemId)
		{
			return ProductCount(recipe.products, recipe.productCounts, itemId);
		}

		private static int ProductCount(int[] products, int[] productCounts, int itemId)
		{
			if (products == null || productCounts == null)
			{
				return 0;
			}
			for (int i = 0; i < products.Length && i < productCounts.Length; i++)
			{
				if (products[i] == itemId)
				{
					return productCounts[i];
				}
			}
			return 0;
		}

		private static void AddMachine(Dictionary<int, Dictionary<long, MachineGroup>> makers, int buildingId, int recipeId, RecipeExecuteData recipe, int speed, int extraSpeed)
		{
			if (recipe.products != null && recipe.productCounts != null)
			{
				double num = Plugin.CyclesPerMinute(recipe, speed, extraSpeed);
				for (int i = 0; i < recipe.products.Length && i < recipe.productCounts.Length; i++)
				{
					MachineGroup machineGroup = GetGroup(makers, recipe.products[i], buildingId, recipeId, recipe);
					machineGroup.Count++;
					machineGroup.TotalRate += num * (double)recipe.productCounts[i];
				}
			}
		}

		private static MachineGroup GetGroup(Dictionary<int, Dictionary<long, MachineGroup>> makers, int itemId, int buildingId, int recipeId, RecipeExecuteData recipe)
		{
			long key = ((long)buildingId << 32) | (uint)recipeId;
			if (!makers.TryGetValue(itemId, out var value))
			{
				value = (makers[itemId] = new Dictionary<long, MachineGroup>());
			}
			if (!value.TryGetValue(key, out var value2))
			{
				MachineGroup obj = new MachineGroup
				{
					BuildingId = buildingId,
					Recipe = recipe
				};
				value2 = (value[key] = obj);
			}
			return value2;
		}

		private static void ClassifyMiner(ProductionReport report, PlanetFactory factory, ref MinerComponent m)
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Invalid comparison between Unknown and I4
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Invalid comparison between Unknown and I4
			report.Machines++;
			double num = PowerRatio(factory, m.pcId);
			if ((int)m.type != 1 && m.veinCount == 0)
			{
				report.Depleted++;
			}
			else if ((int)m.workstate == -2 || m.speedDamper < 0.99f)
			{
				report.OutputFull++;
			}
			else if (num <= 0.1)
			{
				report.NoPower++;
			}
			else if (num < 1.0)
			{
				report.LowPower++;
			}
			else
			{
				report.Working++;
			}
		}

		private static MachineGroup MainGroup(Dictionary<int, Dictionary<long, MachineGroup>> makers, int itemId)
		{
			if (!makers.TryGetValue(itemId, out var value) || value.Count == 0)
			{
				return null;
			}
			return value.Values.OrderByDescending((MachineGroup g) => g.Count).First();
		}

		private static void Classify(ProductionReport report, PlanetFactory factory, RecipeExecuteData recipe, bool replicating, int time, int[] served, int pcId)
		{
			report.Machines++;
			double num = PowerRatio(factory, pcId);
			if (replicating)
			{
				if (num >= 1.0)
				{
					report.Working++;
				}
				else if (num > 0.1)
				{
					report.LowPower++;
				}
				else
				{
					report.NoPower++;
				}
				return;
			}
			if (time >= recipe.timeSpend)
			{
				report.OutputFull++;
				return;
			}
			bool flag = false;
			if (served != null && recipe.requires != null && recipe.requireCounts != null)
			{
				for (int i = 0; i < recipe.requireCounts.Length && i < served.Length && i < recipe.requires.Length; i++)
				{
					if (served[i] < recipe.requireCounts[i])
					{
						flag = true;
						int key = recipe.requires[i];
						report.MissingItems[key] = ((!report.MissingItems.TryGetValue(key, out var value)) ? 1 : (value + 1));
					}
				}
			}
			if (flag)
			{
				report.MissingInput++;
			}
			else if (num >= 1.0)
			{
				report.Idle++;
			}
			else if (num > 0.1)
			{
				report.LowPower++;
			}
			else
			{
				report.NoPower++;
			}
		}

		private static double PowerRatio(PlanetFactory factory, int pcId)
		{
			PowerSystem powerSystem = factory.powerSystem;
			if (powerSystem == null || powerSystem.consumerPool == null || pcId <= 0 || pcId >= powerSystem.consumerPool.Length)
			{
				return 0.0;
			}
			int networkId = powerSystem.consumerPool[pcId].networkId;
			if (networkId <= 0 || powerSystem.netPool == null || networkId >= powerSystem.netPool.Length || powerSystem.netPool[networkId] == null)
			{
				return 0.0;
			}
			return powerSystem.netPool[networkId].consumerRatio;
		}
	}
	public static class SaveManager
	{
		private const int FormatVersion = 1;

		public static void Write(BinaryWriter w, List<TodoItem> tasks, int lastPlanetId)
		{
			w.Write(1);
			w.Write(lastPlanetId);
			w.Write(tasks.Count);
			foreach (TodoItem task in tasks)
			{
				w.Write(task.Description ?? "");
				w.Write(task.IsCompleted);
				w.Write((int)task.Type);
				w.Write(task.ItemId);
				w.Write(task.TargetAmount);
				w.Write(task.CurrentAmount);
				w.Write(task.ActualAmount);
				w.Write(task.PlanetId);
			}
		}

		public static List<TodoItem> Read(BinaryReader r, out int lastPlanetId)
		{
			int num = r.ReadInt32();
			if (num < 1 || num > 1)
			{
				throw new InvalidDataException($"Unsupported task data version {num}");
			}
			lastPlanetId = r.ReadInt32();
			int num2 = r.ReadInt32();
			List<TodoItem> list = new List<TodoItem>(num2);
			for (int i = 0; i < num2; i++)
			{
				string desc = r.ReadString();
				bool isCompleted = r.ReadBoolean();
				TaskType type = (TaskType)r.ReadInt32();
				int itemId = r.ReadInt32();
				int target = r.ReadInt32();
				TodoItem item = new TodoItem(desc, type, itemId, target)
				{
					IsCompleted = isCompleted,
					CurrentAmount = r.ReadInt64(),
					ActualAmount = r.ReadInt64(),
					PlanetId = r.ReadInt32()
				};
				list.Add(item);
			}
			return list;
		}
	}
	public class TaskOverlay
	{
		private enum Tone
		{
			Info,
			Problem,
			Done,
			Muted
		}

		private class Row
		{
			public TodoItem Task;

			public GameObject Go;

			public CanvasGroup Group;

			public Image Edge;

			public Image Icon;

			public Text Name;

			public Text Status;

			public RectTransform Fill;

			public Image FillImage;

			public RectTransform Tick;

			public Text Left;

			public Text Right;

			public bool Problem;

			public float DoneSince;
		}

		private const int MaxRows = 5;

		private const float Width = 270f;

		private const float HeaderHeight = 22f;

		private const float RowHeight = 42f;

		private const float DoneShowSeconds = 4f;

		private const float DoneFadeSeconds = 1f;

		private const float ReasonRefreshSeconds = 3f;

		private static readonly Color Accent = new Color(0.35f, 0.72f, 1f, 0.9f);

		private static readonly Color AccentSoft = new Color(0.35f, 0.72f, 1f, 0.16f);

		private static readonly Color HeaderText = new Color(0.81f, 0.9f, 1f, 1f);

		private static readonly Color Line = new Color(0.63f, 0.8f, 1f, 0.18f);

		private static readonly Color RowSeparator = new Color(1f, 1f, 1f, 0.05f);

		private static readonly Color Lag = new Color(0.95f, 0.6f, 0.2f, 1f);

		private static readonly Color Met = new Color(0.35f, 0.8f, 0.4f, 1f);

		private static readonly Color Below = new Color(0.55f, 0.65f, 0.8f, 1f);

		private static readonly Color ResearchColor = new Color(0.88f, 0.76f, 0.25f, 1f);

		private GameObject _root;

		private RectTransform _rootRt;

		private Canvas _canvas;

		private bool _dragged;

		private Text _headerRight;

		private readonly Dictionary<TodoItem, Row> _rows = new Dictionary<TodoItem, Row>();

		private readonly Dictionary<TodoItem, (long value, float time, double rate)> _research = new Dictionary<TodoItem, (long, float, double)>();

		private readonly Dictionary<TodoItem, (float time, string tag)> _reasons = new Dictionary<TodoItem, (float, string)>();

		public void Hide()
		{
			if ((Object)(object)_root != (Object)null && _root.activeSelf)
			{
				_root.SetActive(false);
			}
		}

		public void Update()
		{
			Plugin instance = Plugin.Instance;
			if (!instance.OverlayEnabled.Value || !IsInGame() || UIManager.Instance.IsWindowOpen)
			{
				Hide();
				ClearDoneRows();
				return;
			}
			int planetId = instance.GetDisplayPlanetId();
			List<TodoItem> visible = (from t in instance.GetTodoItems()
				where t.Type != TaskType.Manual
				where t.PlanetId == 0 || (planetId != 0 && t.PlanetId == planetId)
				select t).ToList();
			List<TodoItem> list = (from t in visible
				where !t.IsCompleted
				orderby t.IsBelowCapacity() descending, TaskText.Progress(t)
				select t).ToList();
			foreach (TodoItem item in _reasons.Keys.Where((TodoItem t) => !visible.Contains(t)).ToList())
			{
				_reasons.Remove(item);
			}
			foreach (TodoItem item2 in _research.Keys.Where((TodoItem t) => !visible.Contains(t)).ToList())
			{
				_research.Remove(item2);
			}
			List<TodoItem> shown = list.Take(5).ToList();
			foreach (Row value2 in _rows.Values)
			{
				if (value2.Task.IsCompleted && visible.Contains(value2.Task) && !shown.Contains(value2.Task))
				{
					shown.Add(value2.Task);
				}
			}
			if (shown.Count == 0)
			{
				Hide();
			}
			else
			{
				if ((Object)(object)_root == (Object)null && !Build())
				{
					return;
				}
				if (!_root.activeSelf)
				{
					_root.SetActive(true);
				}
				foreach (TodoItem item3 in _rows.Keys.Where((TodoItem t) => !shown.Contains(t)).ToList())
				{
					Object.Destroy((Object)(object)_rows[item3].Go);
					_rows.Remove(item3);
				}
				int num = 1;
				foreach (TodoItem item4 in shown)
				{
					if (!_rows.TryGetValue(item4, out var value))
					{
						value = CreateRow(item4);
						_rows[item4] = value;
					}
					value.Go.transform.SetSiblingIndex(num++);
					UpdateRow(value);
				}
				string arg = ((planetId != 0) ? instance.GetPlanetName(planetId) : "Global");
				_headerRight.text = $"{arg} · {list.Count} open";
			}
		}

		private void ClearDoneRows()
		{
			foreach (TodoItem item in _rows.Keys.Where((TodoItem t) => t.IsCompleted).ToList())
			{
				Object.Destroy((Object)(object)_rows[item].Go);
				_rows.Remove(item);
			}
		}

		private static bool IsInGame()
		{
			if (DSPGame.IsMenuDemo || !GameMain.isRunning || GameMain.mainPlayer == null)
			{
				return false;
			}
			UIGame val = UIRoot.instance?.uiGame;
			if ((Object)(object)val == (Object)null || val.hideAllUI0 || val.hideAllUI1)
			{
				return false;
			}
			if ((Object)(object)val.starmap != (Object)null && ((ManualBehaviour)val.starmap).active)
			{
				return false;
			}
			if ((Object)(object)val.dysonEditor != (Object)null && ((ManualBehaviour)val.dysonEditor).active)
			{
				return false;
			}
			return !UIItemPicker.isOpened;
		}

		private void UpdateRow(Row row)
		{
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0163: Unknown result type (might be due to invalid IL or missing references)
			//IL_015c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0155: Unknown result type (might be due to invalid IL or missing references)
			//IL_014e: Unknown result type (might be due to invalid IL or missing references)
			TodoItem task = row.Task;
			float unscaledTime = Time.unscaledTime;
			if (task.IsCompleted && row.DoneSince <= 0f)
			{
				row.DoneSince = unscaledTime;
			}
			if (!task.IsCompleted)
			{
				row.DoneSince = 0f;
			}
			float num = TaskText.Progress(task);
			row.Fill.anchorMax = new Vector2(num, 1f);
			row.Right.text = TaskText.OverlayValue(task);
			row.Left.text = LeftText(task);
			bool flag = task.Type == TaskType.ProductionTarget && task.TargetAmount > 0 && task.IsBelowCapacity();
			((Component)row.Tick).gameObject.SetActive(flag);
			if (flag)
			{
				float num2 = Mathf.Clamp01((float)task.ActualAmount / (float)task.TargetAmount);
				row.Tick.anchorMin = new Vector2(num2, 0f);
				row.Tick.anchorMax = new Vector2(num2, 1f);
			}
			string text;
			Tone tone;
			if (!task.IsCompleted)
			{
				(text, tone) = StatusTag(task);
			}
			else
			{
				text = "Done";
				tone = Tone.Done;
			}
			row.Problem = tone == Tone.Problem;
			SetStatus(row, text, tone);
			((Graphic)row.FillImage).color = (task.IsCompleted ? Met : ((task.Type == TaskType.Research) ? ResearchColor : (row.Problem ? Lag : Below)));
			((Behaviour)row.Edge).enabled = row.Problem;
		}

		private string LeftText(TodoItem task)
		{
			switch (task.Type)
			{
			case TaskType.ProductionTarget:
				return TaskText.Fmt(task.ActualAmount) + " actual";
			case TaskType.Research:
				return ResearchEta(task);
			case TaskType.Surplus:
				return TaskText.Fmt(task.ActualAmount) + " made, " + TaskText.Fmt(task.TotalAmount) + " used";
			case TaskType.Building:
			{
				long num = task.TargetAmount - task.CurrentAmount;
				if (num <= 0)
				{
					return "";
				}
				return TaskText.Fmt(num) + " to go";
			}
			default:
				return "";
			}
		}

		private (string, Tone) StatusTag(TodoItem task)
		{
			switch (task.Type)
			{
			case TaskType.ProductionTarget:
				if (task.CurrentAmount <= 0)
				{
					return ("No machines", Tone.Problem);
				}
				if (!task.IsBelowCapacity())
				{
					return ("Producing", Tone.Info);
				}
				return (ProblemReason(task), Tone.Problem);
			case TaskType.Research:
				if (task.StatusText == "Researching")
				{
					return ("Researching", Tone.Info);
				}
				return (task.StatusText ?? "Not queued", Tone.Muted);
			case TaskType.Building:
				return ("Building", Tone.Info);
			case TaskType.Surplus:
				if (task.CurrentAmount >= 0)
				{
					return ("Surplus", Tone.Info);
				}
				return ("Deficit", Tone.Problem);
			case TaskType.Sphere:
				return ("Sphere", Tone.Info);
			default:
				return ("Collecting", Tone.Info);
			}
		}

		private string ProblemReason(TodoItem task)
		{
			float unscaledTime = Time.unscaledTime;
			if (_reasons.TryGetValue(task, out (float, string) value) && unscaledTime - value.Item1 < 3f)
			{
				return value.Item2;
			}
			string text = "Slow";
			try
			{
				ProductionReport productionReport = ProductionInsight.Analyze(task);
				(int, string) tuple = new List<(int, string)>
				{
					(productionReport.MissingInput, "Missing input"),
					(productionReport.OutputFull, "Backed up"),
					(productionReport.NoPower, "No power"),
					(productionReport.LowPower, "Low power"),
					(productionReport.Depleted, "Ore depleted")
				}.OrderByDescending<(int, string), int>(((int count, string tag) x) => x.count).First();
				if (tuple.Item1 > 0)
				{
					text = tuple.Item2;
				}
			}
			catch (Exception ex)
			{
				Plugin.Instance.Log.LogWarning((object)("Overlay: could not analyze '" + task.Description + "': " + ex.Message));
			}
			_reasons[task] = (unscaledTime, text);
			return text;
		}

		private string ResearchEta(TodoItem task)
		{
			float unscaledTime = Time.unscaledTime;
			if (task.StatusText != "Researching" || task.TotalAmount <= 0)
			{
				_research.Remove(task);
				return "";
			}
			if (_research.TryGetValue(task, out (long, float, double) value))
			{
				float num = unscaledTime - value.Item2;
				if (num < 0.5f)
				{
					return FormatEta(task, value.Item3);
				}
				double num2 = (double)(task.CurrentAmount - value.Item1) / (double)num;
				if (num2 < 0.0)
				{
					num2 = 0.0;
				}
				double num3 = ((value.Item3 > 0.0) ? (value.Item3 * 0.8 + num2 * 0.2) : num2);
				_research[task] = (task.CurrentAmount, unscaledTime, num3);
				return FormatEta(task, num3);
			}
			_research[task] = (task.CurrentAmount, unscaledTime, 0.0);
			return "";
		}

		private static string FormatEta(TodoItem task, double hashesPerSecond)
		{
			if (hashesPerSecond <= 0.0)
			{
				return "";
			}
			double num = (double)(task.TotalAmount - task.CurrentAmount) / hashesPerSecond;
			if (num < 60.0)
			{
				return "<1 min left";
			}
			if (num < 3600.0)
			{
				return $"~{(int)Math.Ceiling(num / 60.0)} min left";
			}
			return $"~{num / 3600.0:0.#} h left";
		}

		private static void SetStatus(Row row, string text, Tone tone)
		{
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: 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)
			row.Status.text = text.ToUpperInvariant();
			switch (tone)
			{
			case Tone.Problem:
				((Graphic)row.Status).color = Lag;
				break;
			case Tone.Done:
				((Graphic)row.Status).color = Met;
				break;
			case Tone.Muted:
				((Graphic)row.Status).color = GameStyle.MutedText;
				break;
			default:
				((Graphic)row.Status).color = Accent;
				break;
			}
		}

		public void Animate()
		{
			if ((Object)(object)_root == (Object)null || !_root.activeSelf)
			{
				return;
			}
			float unscaledTime = Time.unscaledTime;
			List<TodoItem> list = null;
			foreach (Row value in _rows.Values)
			{
				if (value.DoneSince > 0f)
				{
					float num = unscaledTime - value.DoneSince;
					value.Group.alpha = ((num < 4f) ? 1f : Mathf.Clamp01(1f - (num - 4f) / 1f));
					if (num >= 5f)
					{
						(list ?? (list = new List<TodoItem>())).Add(value.Task);
					}
				}
				else
				{
					value.Group.alpha = 1f;
				}
			}
			if (list == null)
			{
				return;
			}
			foreach (TodoItem item in list)
			{
				Object.Destroy((Object)(object)_rows[item].Go);
				_rows.Remove(item);
			}
			if (_rows.Count == 0)
			{
				Hide();
			}
		}

		private bool Build()
		{
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Expected O, but got Unknown
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0100: Unknown result type (might be due to invalid IL or missing references)
			//IL_010a: Expected O, but got Unknown
			Canvas val = UIRoot.instance?.overlayCanvas;
			if ((Object)(object)val == (Object)null)
			{
				return false;
			}
			GameStyle.Init();
			_root = new GameObject("TaskMasterOverlay");
			_root.layer = LayerMask.NameToLayer("UI");
			_rootRt = _root.AddComponent<RectTransform>();
			((Transform)_rootRt).SetParent(((Component)val).transform, false);
			_canvas = val;
			RectTransform rootRt = _rootRt;
			RectTransform rootRt2 = _rootRt;
			Vector2 val2 = default(Vector2);
			((Vector2)(ref val2))..ctor(1f, 1f);
			rootRt2.anchorMax = val2;
			rootRt.anchorMin = val2;
			_rootRt.pivot = new Vector2(1f, 1f);
			_rootRt.sizeDelta = new Vector2(270f, 0f);
			_rootRt.anchoredPosition = LoadPosition();
			((Graphic)GameStyle.CreateWindowBackground(_root)).raycastTarget = true;
			VerticalLayoutGroup obj = _root.AddComponent<VerticalLayoutGroup>();
			((LayoutGroup)obj).padding = new RectOffset(0, 0, 0, 2);
			((HorizontalOrVerticalLayoutGroup)obj).spacing = 0f;
			((HorizontalOrVerticalLayoutGroup)obj).childControlHeight = true;
			((HorizontalOrVerticalLayoutGroup)obj).childControlWidth = true;
			((HorizontalOrVerticalLayoutGroup)obj).childForceExpandHeight = false;
			((HorizontalOrVerticalLayoutGroup)obj).childForceExpandWidth = true;
			_root.AddComponent<ContentSizeFitter>().verticalFit = (FitMode)2;
			BuildHeader();
			BuildFrame();
			EventTrigger et = _root.AddComponent<EventTrigger>();
			AddTrigger(et, (EventTriggerType)5, delegate(BaseEventData data)
			{
				//IL_0001: Unknown result type (might be due to invalid IL or missing references)
				//IL_0007: Expected O, but got Unknown
				//IL_0041: 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_004d: Unknown result type (might be due to invalid IL or missing references)
				//IL_0052: Unknown result type (might be due to invalid IL or missing references)
				PointerEventData val3 = (PointerEventData)data;
				float num = (((Object)(object)_canvas != (Object)null && _canvas.scaleFactor > 0f) ? _canvas.scaleFactor : 1f);
				RectTransform rootRt3 = _rootRt;
				rootRt3.anchoredPosition += val3.delta / num;
			});
			AddTrigger(et, (EventTriggerType)2, delegate
			{
				_dragged = false;
			});
			AddTrigger(et, (EventTriggerType)13, delegate
			{
				_dragged = true;
			});
			AddTrigger(et, (EventTriggerType)14, delegate
			{
				SavePosition();
			});
			AddTrigger(et, (EventTriggerType)4, delegate(BaseEventData data)
			{
				//IL_0001: Unknown result type (might be due to invalid IL or missing references)
				//IL_0007: Expected O, but got Unknown
				PointerEventData val3 = (PointerEventData)data;
				if (!_dragged && !val3.dragging && !IsOnHeader(val3))
				{
					UIManager.Instance.ToggleWindow();
				}
			});
			_root.AddComponent<OverlayAnimator>().Overlay = this;
			return true;
		}

		private void BuildHeader()
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Expected O, but got Unknown
			//IL_004a: 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_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_0134: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject("Header");
			val.transform.SetParent((Transform)(object)_rootRt, false);
			val.AddComponent<RectTransform>();
			LayoutElement obj = val.AddComponent<LayoutElement>();
			obj.minHeight = 22f;
			obj.preferredHeight = 22f;
			Stretch(((Graphic)NewImage("Strip", val.transform, AccentSoft)).rectTransform, 0f, 0f, 0f, 0f);
			RectTransform rectTransform = ((Graphic)NewImage("Line", val.transform, Line)).rectTransform;
			rectTransform.anchorMin = new Vector2(0f, 0f);
			rectTransform.anchorMax = new Vector2(1f, 0f);
			rectTransform.pivot = new Vector2(0.5f, 0f);
			rectTransform.sizeDelta = new Vector2(0f, 1f);
			Text obj2 = NewText("Title", val.transform, "TASKS", 11, HeaderText, (TextAnchor)3);
			obj2.fontStyle = (FontStyle)1;
			Stretch(((Graphic)obj2).rectTransform, 10f, 0f, 0f, 0f);
			_headerRight = NewText("Planet", val.transform, "", 10, GameStyle.MutedText, (TextAnchor)5);
			Stretch(((Graphic)_headerRight).rectTransform, 60f, 10f, 0f, 0f);
		}

		private void BuildFrame()
		{
			//IL_0012: 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_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: 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_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Expected O, but got Unknown
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_0108: Unknown result type (might be due to invalid IL or missing references)
			//IL_011d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0123: Unknown result type (might be due to invalid IL or missing references)
			//IL_0138: Unknown result type (might be due to invalid IL or missing references)
			//IL_013e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0153: Unknown result type (might be due to invalid IL or missing references)
			//IL_0159: Unknown result type (might be due to invalid IL or missing references)
			//IL_0172: Unknown result type (might be due to invalid IL or missing references)
			//IL_0187: Unknown result type (might be due to invalid IL or missing references)
			//IL_0197: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f1: Unknown result type (might be due to invalid IL or missing references)
			Vector2[] array = (Vector2[])(object)new Vector2[4]
			{
				new Vector2(0f, 1f),
				new Vector2(1f, 1f),
				new Vector2(0f, 0f),
				new Vector2(1f, 0f)
			};
			foreach (Vector2 val in array)
			{
				GameObject val2 = new GameObject("Corner");
				val2.transform.SetParent((Transform)(object)_rootRt, false);
				RectTransform obj = val2.AddComponent<RectTransform>();
				Vector2 val3 = (obj.pivot = val);
				Vector2 anchorMin = (obj.anchorMax = val3);
				obj.anchorMin = anchorMin;
				obj.anchoredPosition = new Vector2((float)((val.x != 0f) ? 1 : (-1)), (float)((val.y != 0f) ? 1 : (-1)));
				obj.sizeDelta = new Vector2(10f, 10f);
				val2.AddComponent<LayoutElement>().ignoreLayout = true;
				Image obj2 = NewImage("H", val2.transform, Accent);
				((Graphic)obj2).rectTransform.anchorMin = new Vector2(0f, val.y);
				((Graphic)obj2).rectTransform.anchorMax = new Vector2(1f, val.y);
				((Graphic)obj2).rectTransform.pivot = new Vector2(0.5f, val.y);
				((Graphic)obj2).rectTransform.sizeDelta = new Vector2(0f, 2f);
				Image obj3 = NewImage("V", val2.transform, Accent);
				((Graphic)obj3).rectTransform.anchorMin = new Vector2(val.x, 0f);
				((Graphic)obj3).rectTransform.anchorMax = new Vector2(val.x, 1f);
				((Graphic)obj3).rectTransform.pivot = new Vector2(val.x, 0.5f);
				((Graphic)obj3).rectTransform.sizeDelta = new Vector2(2f, 0f);
			}
		}

		private Row CreateRow(TodoItem task)
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Expected O, but got Unknown
			//IL_008e: 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_00c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_0106: Unknown result type (might be due to invalid IL or missing references)
			//IL_0120: Unknown result type (might be due to invalid IL or missing references)
			//IL_013a: Unknown result type (might be due to invalid IL or missing references)
			//IL_014f: 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_0178: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a6: 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)
			//IL_01d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_020c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0222: Unknown result type (might be due to invalid IL or missing references)
			//IL_027e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0277: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_02fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0312: Unknown result type (might be due to invalid IL or missing references)
			//IL_0326: Unknown result type (might be due to invalid IL or missing references)
			//IL_0348: Unknown result type (might be due to invalid IL or missing references)
			//IL_0382: Unknown result type (might be due to invalid IL or missing references)
			//IL_0389: Unknown result type (might be due to invalid IL or missing references)
			//IL_039b: Unknown result type (might be due to invalid IL or missing references)
			//IL_03b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_03c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_03de: Unknown result type (might be due to invalid IL or missing references)
			//IL_03fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_040f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0424: Unknown result type (might be due to invalid IL or missing references)
			//IL_0439: Unknown result type (might be due to invalid IL or missing references)
			//IL_044d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0463: Unknown result type (might be due to invalid IL or missing references)
			//IL_0489: Unknown result type (might be due to invalid IL or missing references)
			//IL_04a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_04b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_04be: Unknown result type (might be due to invalid IL or missing references)
			//IL_04bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_04c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_04d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0501: Unknown result type (might be due to invalid IL or missing references)
			//IL_051b: Unknown result type (might be due to invalid IL or missing references)
			//IL_052b: Unknown result type (might be due to invalid IL or missing references)
			//IL_054d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0573: Unknown result type (might be due to invalid IL or missing references)
			//IL_0588: Unknown result type (might be due to invalid IL or missing references)
			//IL_059d: Unknown result type (might be due to invalid IL or missing references)
			//IL_05b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_05c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_05e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_060e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0623: Unknown result type (might be due to invalid IL or missing references)
			//IL_0638: Unknown result type (might be due to invalid IL or missing references)
			//IL_064d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0661: Unknown result type (might be due to invalid IL or missing references)
			Row row = new Row
			{
				Task = task
			};
			row.Go = new GameObject("Row");
			row.Go.transform.SetParent((Transform)(object)_rootRt, false);
			row.Go.AddComponent<RectTransform>();
			LayoutElement obj = row.Go.AddComponent<LayoutElement>();
			obj.minHeight = 42f;
			obj.preferredHeight = 42f;
			row.Group = row.Go.AddComponent<CanvasGroup>();
			row.Group.blocksRaycasts = false;
			row.Edge = NewImage("Edge", row.Go.transform, Lag);
			RectTransform rectTransform = ((Graphic)row.Edge).rectTransform;
			rectTransform.anchorMin = new Vector2(0f, 0f);
			rectTransform.anchorMax = new Vector2(0f, 1f);
			rectTransform.pivot = new Vector2(0f, 0.5f);
			rectTransform.offsetMin = new Vector2(0f, 5f);
			rectTransform.offsetMax = new Vector2(2f, -5f);
			RectTransform rectTransform2 = ((Graphic)NewImage("Separator", row.Go.transform, RowSeparator)).rectTransform;
			rectTransform2.anchorMin = new Vector2(0f, 1f);
			rectTransform2.anchorMax = new Vector2(1f, 1f);
			rectTransform2.pivot = new Vector2(0.5f, 1f);
			rectTransform2.sizeDelta = new Vector2(-16f, 1f);
			Image val = NewImage("Slot", row.Go.transform, new Color(1f, 1f, 1f, 0.06f));
			RectTransform rectTransform3 = ((Graphic)val).rectTransform;
			Vector2 val2 = default(Vector2);
			((Vector2)(ref val2))..ctor(0f, 0.5f);
			rectTransform3.anchorMax = val2;
			rectTransform3.anchorMin = val2;
			rectTransform3.pivot = new Vector2(0f, 0.5f);
			rectTransform3.anchoredPosition = new Vector2(10f, 0f);
			rectTransform3.sizeDelta = new Vector2(28f, 28f);
			row.Icon = NewImage("Icon", ((Component)val).transform, Color.white);
			Stretch(((Graphic)row.Icon).rectTransform, 3f, 3f, 3f, 3f);
			Sprite taskIcon = UIManager.GetTaskIcon(task);
			row.Icon.sprite = taskIcon;
			((Graphic)row.Icon).color = (((Object)(object)taskIcon != (Object)null) ? Color.white : Color.gray);
			row.Name = NewText("Name", row.Go.transform, TaskText.ShortName(task), 12, GameStyle.TextColor, (TextAnchor)3);
			row.Name.horizontalOverflow = (HorizontalWrapMode)0;
			RectTransform rectTransform4 = ((Graphic)row.Name).rectTransform;
			rectTransform4.anchorMin = new Vector2(0f, 1f);
			rectTransform4.anchorMax = new Vector2(1f, 1f);
			rectTransform4.pivot = new Vector2(0f, 1f);
			rectTransform4.anchoredPosition = new Vector2(46f, -4f);
			rectTransform4.sizeDelta = new Vector2(-146f, 15f);
			row.Status = NewText("Status", row.Go.transform, "", 10, HeaderText, (TextAnchor)5);
			row.Status.fontStyle = (FontStyle)1;
			RectTransform rectTransform5 = ((Graphic)row.Status).rectTransform;
			((Vector2)(ref val2))..ctor(1f, 1f);
			rectTransform5.anchorMax = val2;
			rectTransform5.anchorMin = val2;
			rectTransform5.pivot = new Vector2(1f, 1f);
			rectTransform5.anchoredPosition = new Vector2(-10f, -4f);
			rectTransform5.sizeDelta = new Vector2(90f, 15f);
			Image val3 = NewImage("Bar", row.Go.transform, GameStyle.BarBackground);
			RectTransform rectTransform6 = ((Graphic)val3).rectTransform;
			rectTransform6.anchorMin = new Vector2(0f, 1f);
			rectTransform6.anchorMax = new Vector2(1f, 1f);
			rectTransform6.pivot = new Vector2(0f, 1f);
			rectTransform6.anchoredPosition = new Vector2(46f, -22f);
			rectTransform6.sizeDelta = new Vector2(-56f, 4f);
			row.FillImage = NewImage("Fill", ((Component)val3).transform, Below);
			row.Fill = ((Graphic)row.FillImage).rectTransform;
			row.Fill.anchorMin = Vector2.zero;
			row.Fill.anchorMax = new Vector2(0f, 1f);
			val2 = (row.Fill.offsetMin = (row.Fill.offsetMax = Vector2.zero));
			Image val5 = NewImage("Tick", ((Component)val3).transform, Color.white);
			row.Tick = ((Graphic)val5).rectTransform;
			row.Tick.pivot = new Vector2(0.5f, 0.5f);
			row.Tick.sizeDelta = new Vector2(2f, 4f);
			row.Tick.anchoredPosition = Vector2.zero;
			row.Left = NewText("Left", row.Go.transform, "", 10, GameStyle.MutedText, (TextAnchor)3);
			RectTransform rectTransform7 = ((Graphic)row.Left).rectTransform;
			rectTransform7.anchorMin = new Vector2(0f, 1f);
			rectTransform7.anchorMax = new Vector2(1f, 1f);
			rectTransform7.pivot = new Vector2(0f, 1f);
			rectTransform7.anchoredPosition = new Vector2(46f, -27f);
			rectTransform7.sizeDelta = new Vector2(-56f, 13f);
			row.Right = NewText("Right", row.Go.transform, "", 10, GameStyle.MutedText, (TextAnchor)5);
			RectTransform rectTransform8 = ((Graphic)row.Right).rectTransform;
			rectTransform8.anchorMin = new Vector2(0f, 1f);
			rectTransform8.anchorMax = new Vector2(1f, 1f);
			rectTransform8.pivot = new Vector2(0f, 1f);
			rectTransform8.anchoredPosition = new Vector2(46f, -27f);
			rectTransform8.sizeDelta = new Vector2(-56f, 13f);
			return row;
		}

		private static Image NewImage(string name, Transform parent, Color color)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject(name);
			val.transform.SetParent(parent, false);
			Image obj = val.AddComponent<Image>();
			((Graphic)obj).color = color;
			((Graphic)obj).raycastTarget = false;
			return obj;
		}

		private static Text NewText(string name, Transform parent, string text, int size, Color color, TextAnchor align)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: 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)
			GameObject val = new GameObject(name);
			val.transform.SetParent(parent, false);
			Text obj = val.AddComponent<Text>();
			obj.font = GameStyle.GetFont();
			obj.text = text;
			obj.fontSize = size;
			((Graphic)obj).color = color;
			obj.alignment = align;
			obj.horizontalOverflow = (HorizontalWrapMode)1;
			obj.verticalOverflow = (VerticalWrapMode)1;
			((Graphic)obj).raycastTarget = false;
			return obj;
		}

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

		private bool IsOnHeader(PointerEventData pd)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			Vector2 val = default(Vector2);
			if (!RectTransformUtility.ScreenPointToLocalPointInRectangle(_rootRt, pd.position, pd.pressEventCamera, ref val))
			{
				return false;
			}
			return val.y > -22f;
		}

		private static void AddTrigger(EventTrigger et, EventTriggerType type, UnityAction<BaseEventData> action)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Expected O, but got Unknown
			Entry val = new Entry
			{
				eventID = type
			};
			((UnityEvent<BaseEventData>)(object)val.callback).AddListener(action);
			et.triggers.Add(val);
		}

		private Vector2 LoadPosition()
		{
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: 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_0098: 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_0082: Unknown result type (might be due to invalid IL or missing references)
			Plugin instance = Plugin.Instance;
			if (instance.OverlayX.Value != 0f || instance.OverlayY.Value != 0f)
			{
				return new Vector2(instance.OverlayX.Value, instance.OverlayY.Value);
			}
			Transform transform = ((Component)_canvas).transform;
			RectTransform val = (RectTransform)(object)((transform is RectTransform) ? transform : null);
			float num;
			if ((Object)(object)val != (Object)null)
			{
				Rect rect = val.rect;
				if (((Rect)(ref rect)).height > 0f)
				{
					rect = val.rect;
					num = ((Rect)(ref rect)).height;
					goto IL_008a;
				}
			}
			num = 1080f;
			goto IL_008a;
			IL_008a:
			float num2 = num;
			return new Vector2(-12f, (0f - num2) * 0.3f);
		}

		private void SavePosition()
		{
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: 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)
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			Transform transform = ((Component)_canvas).transform;
			RectTransform val = (RectTransform)(object)((transform is RectTransform) ? transform : null);
			if ((Object)(object)val != (Object)null)
			{
				Rect rect = val.rect;
				Vector2 size = ((Rect)(ref rect)).size;
				Vector2 anchoredPosition = _rootRt.anchoredPosition;
				float x = anchoredPosition.x;
				float num = 0f - size.x;
				rect = _rootRt.rect;
				anchoredPosition.x = Mathf.Clamp(x, num + ((Rect)(ref rect)).width, 0f);
				float y = anchoredPosition.y;
				float num2 = 0f - size.y;
				rect = _rootRt.rect;
				anchoredPosition.y = Mathf.Clamp(y, num2 + ((Rect)(ref rect)).height, 0f);
				_rootRt.anchoredPosition = anchoredPosition;
			}
			Plugin.Instance.OverlayX.Value = _rootRt.anchoredPosition.x;
			Plugin.Instance.OverlayY.Value = _rootRt.anchoredPosition.y;
		}
	}
	public class OverlayAnimator : MonoBehaviour
	{
		public TaskOverlay Overlay;

		private void Update()
		{
			try
			{
				Overlay?.Animate();
			}
			catch (Exception ex)
			{
				Plugin.Instance.Log.LogWarning((object)("Overlay animation failed: " + ex.Message));
				((Behaviour)this).enabled = false;
			}
		}
	}
	public static class TaskText
	{
		private static readonly CultureInfo Inv = CultureInfo.InvariantCulture;

		public static string Fmt(long value)
		{
			if (value < 0)
			{
				return "-" + Fmt(-value);
			}
			if (value >= 1000000000)
			{
				return ((double)value / 1000000000.0).ToString("0.##", Inv) + "G";
			}
			if (value >= 1000000)
			{
				return ((double)value / 1000000.0).ToString((value >= 10000000) ? "0.#" : "0.##", Inv) + "M";
			}
			if (value >= 100000)
			{
				return ((double)value / 1000.0).ToString("0.#", Inv) + "k";
			}
			return value.ToString("N0", Inv);
		}

		public static string FmtSigned(long value)
		{
			if (value <= 0)
			{
				return Fmt(value);
			}
			return "+" + Fmt(value);
		}

		public static string FmtRate(double perMinute)
		{
			if (perMinute >= 100.0)
			{
				return Fmt((long)(perMinute + 0.5));
			}
			return perMinute.ToString("0.#", Inv);
		}

		public static string FmtPower(long megawatts)
		{
			if (megawatts >= 1000000)
			{
				return ((double)megawatts / 1000000.0).ToString("0.##", Inv) + " TW";
			}
			if (megawatts >= 1000)
			{
				return ((double)megawatts / 1000.0).ToString("0.##", Inv) + " GW";
			}
			return megawatts.ToString("N0", Inv) + " MW";
		}

		public static float Progress(TodoItem item)
		{
			if (item.Type == TaskType.Research)
			{
				if (item.IsCompleted)
				{
					return 1f;
				}
				if (item.TotalAmount <= 0)
				{
					return 0f;
				}
				return Mathf.Clamp01((float)item.CurrentAmount / (float)item.TotalAmount);
			}
			if (item.TargetAmount <= 0)
			{
				return 0f;
			}
			return Mathf.Clamp01((float)item.CurrentAmount / (float)item.TargetAmount);
		}

		public static string ProgressLine(TodoItem item)
		{
			int num = (int)(Progress(item) * 100f);
			switch (item.Type)
			{
			case TaskType.ProductionTarget:
				if (item.TargetAmount <= 0)
				{
					return "No target set";
				}
				return $"Cap {Fmt(item.CurrentAmount)} / {Fmt(item.TargetAmount)} /min [{num}%]";
			case TaskType.Research:
				if (item.IsCompleted)
				{
					return "Unlocked";
				}
				if (item.TotalAmount <= 0)
				{
					return "Not started";
				}
				return $"{Fmt(item.CurrentAmount)} / {Fmt(item.TotalAmount)} hashes [{num}%]";
			case TaskType.Building:
				return $"{Fmt(item.CurrentAmount)} / {Fmt(item.TargetAmount)} placed [{num}%]";
			case TaskType.Sphere:
				return $"{FmtPower(item.CurrentAmount)} / {FmtPower(item.TargetAmount)} [{num}%]";
			case TaskType.Surplus:
				return $"Surplus {FmtSigned(item.CurrentAmount)} / {FmtSigned(item.TargetAmount)} /min [{num}%]";
			default:
				if (item.TargetAmount <= 0)
				{
					return "No target set";
				}
				return $"{Fmt(item.CurrentAmount)} / {Fmt(item.TargetAmount)} [{num}%]";
			}
		}

		public static string ShortName(TodoItem item)
		{
			switch (item.Type)
			{
			case TaskType.InventoryTarget:
			case TaskType.ProductionTarget:
				return new ItemDefinition
				{
					Id = item.ItemId
				}.Name;
			case TaskType.Surplus:
				return new ItemDefinition
				{
					Id = item.ItemId
				}.Name + " surplus";
			case TaskType.Research:
				return StripPrefix(item.Description, "Research ");
			default:
				return item.Description;
			}
		}

		private static string StripPrefix(string text, string prefix)
		{
			string text2;
			if (text == null || !text.StartsWith(prefix))
			{
				text2 = text;
				if (text2 == null)
				{
					return "";
				}
			}
			else
			{
				text2 = text.Substring(prefix.Length);
			}
			return text2;
		}

		public static string OverlayValue(TodoItem item)
		{
			return item.Type switch
			{
				TaskType.ProductionTarget => Fmt(item.CurrentAmount) + " / " + Fmt(item.TargetAmount) + " /min", 
				TaskType.Research => (int)(Progress(item) * 100f) + "%", 
				TaskType.Building => Fmt(item.CurrentAmount) + " / " + Fmt(item.TargetAmount) + " placed", 
				TaskType.Sphere => FmtPower(item.CurrentAmount) + " / " + FmtPower(item.TargetAmount), 
				TaskType.Surplus => FmtSigned(item.CurrentAmount) + " / " + FmtSigned(item.TargetAmount) + " /min", 
				_ => Fmt(item.CurrentAmount) + " / " + Fmt(i