Decompiled source of XRays v0.1.0

plugins/XRays.dll

Decompiled 2 weeks ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security.Permissions;
using System.Text;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Jotunn.Managers;
using TMPro;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Events;
using UnityEngine.UI;
using YamlDotNet.Serialization;
using YamlDotNet.Serialization.NamingConventions;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("XRays")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("XRays")]
[assembly: AssemblyCopyright("Copyright ©  2021")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("e3243d22-4307-4008-ba36-9f326008cde5")]
[assembly: AssemblyFileVersion("0.1.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.1.0.0")]
namespace XRays;

internal class Category
{
	public const string NameKeyPrefix = "name:";

	public string Key;

	public string Label;

	public string Description;

	public Type[] Types;

	public string[] NameContains;

	public string[] NameStartsWith;

	public string[] NameExcludes;

	public string[] NameMatches;

	public string[] NameExcludesMatches;

	public Func<List<GameObject>> RootProvider;

	public bool SkipLocalPlayer;

	private static readonly Dictionary<string, Regex> NamePatterns = new Dictionary<string, Regex>(StringComparer.Ordinal);

	public bool IsNameCategory
	{
		get
		{
			if (Key != null)
			{
				return Key.StartsWith("name:", StringComparison.Ordinal);
			}
			return false;
		}
	}

	public string Name
	{
		get
		{
			if (!IsNameCategory)
			{
				return Label;
			}
			return Key.Substring("name:".Length);
		}
	}

	public bool HasNameMatch
	{
		get
		{
			if (!IsNameCategory && (NameContains == null || NameContains.Length == 0) && (NameStartsWith == null || NameStartsWith.Length == 0))
			{
				if (NameMatches != null)
				{
					return NameMatches.Length != 0;
				}
				return false;
			}
			return true;
		}
	}

	public bool MatchesName(string objectName)
	{
		if (string.IsNullOrEmpty(objectName))
		{
			return false;
		}
		if (MatchesExclude(objectName))
		{
			return false;
		}
		if (IsNameCategory)
		{
			return string.Equals(objectName, Name, StringComparison.Ordinal);
		}
		if (HasPrefix(objectName))
		{
			return true;
		}
		if (ContainsAny(objectName, NameContains))
		{
			return true;
		}
		return MatchesAnyPattern(objectName, NameMatches);
	}

	public bool MatchesExclude(string objectName)
	{
		if (!ContainsAny(objectName, NameExcludes))
		{
			return MatchesAnyPattern(objectName, NameExcludesMatches);
		}
		return true;
	}

	private static bool ContainsAny(string objectName, string[] patterns)
	{
		if (string.IsNullOrEmpty(objectName) || patterns == null)
		{
			return false;
		}
		foreach (string value in patterns)
		{
			if (!string.IsNullOrEmpty(value) && objectName.IndexOf(value, StringComparison.OrdinalIgnoreCase) >= 0)
			{
				return true;
			}
		}
		return false;
	}

	private static bool MatchesAnyPattern(string objectName, string[] patterns)
	{
		if (string.IsNullOrEmpty(objectName) || patterns == null)
		{
			return false;
		}
		foreach (string text in patterns)
		{
			if (string.IsNullOrEmpty(text))
			{
				continue;
			}
			if (!NamePatterns.TryGetValue(text, out var value))
			{
				try
				{
					value = new Regex(text, RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.CultureInvariant);
				}
				catch (ArgumentException)
				{
					value = null;
					if ((Object)(object)XRays.Instance != (Object)null)
					{
						XRays.Instance.Log.LogError((object)("XRays: bad name pattern, it matches nothing: " + text));
					}
				}
				NamePatterns[text] = value;
			}
			if (value != null && value.IsMatch(objectName))
			{
				return true;
			}
		}
		return false;
	}

	private bool HasPrefix(string objectName)
	{
		if (NameStartsWith == null)
		{
			return false;
		}
		string[] nameStartsWith = NameStartsWith;
		foreach (string value in nameStartsWith)
		{
			if (!string.IsNullOrEmpty(value) && objectName.StartsWith(value, StringComparison.OrdinalIgnoreCase))
			{
				return true;
			}
		}
		return false;
	}

	public static Category ForName(string objectName)
	{
		return new Category
		{
			Key = "name:" + objectName,
			Label = objectName,
			Types = Array.Empty<Type>()
		};
	}
}
internal static class Categories
{
	private class NameScan
	{
		public float Time;

		public readonly List<GameObject> Roots = new List<GameObject>();
	}

	public const string CreaturesKey = "creatures";

	public static readonly Category[] BuiltIn;

	public static readonly Category[] DisplayOrder;

	private static readonly Dictionary<string, Category> Registry;

	public const float DefaultNameScanInterval = 4f;

	private static readonly Dictionary<string, NameScan> NameScans;

	public static IEnumerable<Category> All => Registry.Values;

	private static Category[] SortByLabel(Category[] source)
	{
		Category[] obj = (Category[])source.Clone();
		Array.Sort(obj, (Category a, Category b) => string.CompareOrdinal(a.Label, b.Label));
		return obj;
	}

	public static float NameScanDuration()
	{
		XRays instance = XRays.Instance;
		if (!((Object)(object)instance != (Object)null) || instance.NameScanInterval == null)
		{
			return 4f;
		}
		return instance.NameScanInterval.Value;
	}

	static Categories()
	{
		BuiltIn = new Category[11]
		{
			new Category
			{
				Key = "trees",
				Label = "Trees",
				Description = "Every tree, plus the stubs, roots and branches that only carry a Destructible, matched by name.",
				Types = new Type[2]
				{
					typeof(TreeBase),
					typeof(TreeLog)
				}
			},
			new Category
			{
				Key = "ores",
				Label = "Ores",
				Description = "The ore veins themselves: copper, tin, iron, silver, obsidian, meteorite, flametal, sulfur, gold and coal, plus the pieces of the giant corpses (petrified bones, ancient armour, swords, soft tissue). Only the name tells them apart: the game marks an ore and a plain stone with the same components, and a broken giant piece carries MineRock5 like a plain stone."
			},
			new Category
			{
				Key = "rocks",
				Label = "Rocks",
				Description = "Boulders, stones and cliffs, plus every mineable rock that is no ore (MineRock5 and MineRock), matched by component and by name.",
				Types = new Type[2]
				{
					typeof(MineRock5),
					typeof(MineRock)
				}
			},
			new Category
			{
				Key = "pickable",
				Label = "Pickable resources",
				Description = "What can be gathered by hand: berries, mushrooms, feathers and the rest (Pickable).",
				Types = new Type[2]
				{
					typeof(Pickable),
					typeof(PickableItem)
				}
			},
			new Category
			{
				Key = "build",
				Label = "Build pieces",
				Description = "Everything that can be built by hand: walls, floors, furniture and workstations (Piece and WearNTear).",
				Types = new Type[2]
				{
					typeof(WearNTear),
					typeof(Piece)
				}
			},
			new Category
			{
				Key = "creatures",
				Label = "Creatures",
				Description = "The animals and the monsters (Character), plus the fish and the birds, with the bars the game draws over them.",
				Types = new Type[3]
				{
					typeof(Character),
					typeof(Fish),
					typeof(RandomFlyingBird)
				},
				SkipLocalPlayer = true
			},
			new Category
			{
				Key = "items",
				Label = "Ground items",
				Description = "What lies on the ground: every dropped item and resource (ItemDrop).",
				Types = new Type[1] { typeof(ItemDrop) }
			},
			new Category
			{
				Key = "plants",
				Label = "Crops",
				Description = "The plants you grow: carrots, turnips, onions, anything carrying a Plant component.",
				Types = new Type[1] { typeof(Plant) }
			},
			new Category
			{
				Key = "spawners",
				Label = "Spawners",
				Description = "The spawners, the nests and piles, the evil hearts and the holes that open on approach.",
				Types = new Type[3]
				{
					typeof(CreatureSpawner),
					typeof(SpawnArea),
					typeof(TriggerSpawner)
				}
			},
			new Category
			{
				Key = "clutter",
				Label = "Grass / vegetation",
				Description = "The clutter growing in the world: bushes, grass patches, ferns, reeds, moss, mushrooms and flowers.",
				Types = new Type[1] { typeof(InstanceRenderer) },
				RootProvider = GrassRoots
			},
			new Category
			{
				Key = "locations",
				Label = "Locations",
				Description = "The structures the game places on its own: ruins, dungeon entrances and everything a Location carries.",
				Types = new Type[2]
				{
					typeof(LocationProxy),
					typeof(Location)
				}
			}
		};
		DisplayOrder = SortByLabel(BuiltIn);
		Registry = new Dictionary<string, Category>();
		NameScans = new Dictionary<string, NameScan>();
		Category[] builtIn = BuiltIn;
		foreach (Category category in builtIn)
		{
			Registry[category.Key] = category;
		}
	}

	public static bool TryGet(string key, out Category category)
	{
		return Registry.TryGetValue(key, out category);
	}

	public static void Register(Category category)
	{
		if (category != null && !string.IsNullOrEmpty(category.Key))
		{
			Registry[category.Key] = category;
		}
	}

	public static void Invalidate(string key)
	{
		if (!string.IsNullOrEmpty(key))
		{
			NameScans.Remove(key);
		}
	}

	public static List<GameObject> CollectRoots(Category category)
	{
		List<GameObject> list = new List<GameObject>();
		HashSet<int> hashSet = new HashSet<int>();
		if (category == null)
		{
			return list;
		}
		long stamp = ReconcileTiming.Stamp();
		AddProvidedRoots(category, list, hashSet);
		AddTypedRoots(category, list, hashSet);
		ReconcileTiming.AddSince("of which collect", stamp);
		if (category.HasNameMatch)
		{
			foreach (GameObject item in NameScanOf(category))
			{
				if ((Object)(object)item != (Object)null && hashSet.Add(((Object)item).GetInstanceID()))
				{
					list.Add(item);
				}
			}
		}
		return list;
	}

	public static int Count(Category category)
	{
		return CollectRoots(category).Count;
	}

	public static List<GameObject> GrassRoots()
	{
		List<GameObject> list = new List<GameObject>();
		Transform val = GrassRoot();
		if ((Object)(object)val != (Object)null)
		{
			list.Add(((Component)val).gameObject);
		}
		return list;
	}

	public static Transform GrassRoot()
	{
		ClutterSystem val = ClutterSystem.instance;
		if ((Object)(object)val == (Object)null)
		{
			Object[] array = Object.FindObjectsByType(typeof(ClutterSystem), (FindObjectsSortMode)0);
			foreach (Object obj in array)
			{
				Object obj2 = ((obj is Component) ? obj : null);
				val = (ClutterSystem)(object)((obj2 is ClutterSystem) ? obj2 : null);
				if ((Object)(object)val != (Object)null)
				{
					break;
				}
			}
		}
		if (!((Object)(object)val != (Object)null))
		{
			return null;
		}
		return ((Component)val).transform.Find("grassroot");
	}

	public static Transform GrassItem(Transform transform)
	{
		Transform val = GrassRoot();
		if ((Object)(object)val == (Object)null || (Object)(object)transform == (Object)null || ((Object)(object)transform != (Object)(object)val && !transform.IsChildOf(val)))
		{
			return null;
		}
		Transform val2 = transform;
		while ((Object)(object)val2 != (Object)(object)val && (Object)(object)val2.parent != (Object)null && (Object)(object)val2.parent != (Object)(object)val)
		{
			val2 = val2.parent;
		}
		return val2;
	}

	private static void AddProvidedRoots(Category category, List<GameObject> roots, HashSet<int> seen)
	{
		if (category.RootProvider == null)
		{
			return;
		}
		List<GameObject> list = category.RootProvider();
		if (list == null)
		{
			return;
		}
		foreach (GameObject item in list)
		{
			if ((Object)(object)item != (Object)null && seen.Add(((Object)item).GetInstanceID()))
			{
				roots.Add(item);
			}
		}
	}

	private static void AddTypedRoots(Category category, List<GameObject> roots, HashSet<int> seen)
	{
		if (category.Types == null)
		{
			return;
		}
		Type[] types = category.Types;
		for (int i = 0; i < types.Length; i++)
		{
			Object[] array = Object.FindObjectsByType(types[i], (FindObjectsSortMode)0);
			foreach (Object obj in array)
			{
				Component val = (Component)(object)((obj is Component) ? obj : null);
				if ((Object)(object)val == (Object)null)
				{
					continue;
				}
				Player val2 = (Player)(object)((val is Player) ? val : null);
				if (!category.SkipLocalPlayer || !((Object)(object)val2 != (Object)null) || !((Object)(object)val2 == (Object)(object)Player.m_localPlayer))
				{
					GameObject gameObject = val.gameObject;
					if (!category.MatchesExclude(((Object)gameObject).name) && seen.Add(((Object)gameObject).GetInstanceID()))
					{
						roots.Add(gameObject);
					}
				}
			}
		}
	}

	private static List<GameObject> NameScanOf(Category category)
	{
		if (NameScans.TryGetValue(category.Key, out var value) && Time.realtimeSinceStartup - value.Time < NameScanDuration())
		{
			return value.Roots;
		}
		value = new NameScan
		{
			Time = Time.realtimeSinceStartup
		};
		long stamp = ReconcileTiming.Stamp();
		AddNamedRoots(category, value.Roots);
		ReconcileTiming.AddSince("of which names", stamp);
		NameScans[category.Key] = value;
		return value.Roots;
	}

	private static void AddNamedRoots(Category category, List<GameObject> roots)
	{
		HashSet<int> hashSet = new HashSet<int>();
		HashSet<Transform> roots2 = ForeignRoots(category);
		Object[] array = Object.FindObjectsByType(typeof(Transform), (FindObjectsSortMode)0);
		foreach (Object obj in array)
		{
			Transform val = (Transform)(object)((obj is Transform) ? obj : null);
			if (!((Object)(object)val == (Object)null) && category.MatchesName(((Object)val).name) && !IsUnderAny(val, roots2) && TypedOwner(((Component)val).gameObject) == null && !HasMatchingParent(val, category) && hashSet.Add(((Object)((Component)val).gameObject).GetInstanceID()))
			{
				roots.Add(((Component)val).gameObject);
			}
		}
	}

	private static HashSet<Transform> ForeignRoots(Category category)
	{
		HashSet<Transform> hashSet = null;
		foreach (Category item in All)
		{
			if (item == category || item.RootProvider == null)
			{
				continue;
			}
			List<GameObject> list = item.RootProvider();
			if (list == null)
			{
				continue;
			}
			foreach (GameObject item2 in list)
			{
				if (!((Object)(object)item2 == (Object)null))
				{
					if (hashSet == null)
					{
						hashSet = new HashSet<Transform>();
					}
					hashSet.Add(item2.transform);
				}
			}
		}
		return hashSet;
	}

	private static bool IsUnderAny(Transform transform, HashSet<Transform> roots)
	{
		if (roots == null || (Object)(object)transform == (Object)null)
		{
			return false;
		}
		Transform val = transform;
		while ((Object)(object)val != (Object)null)
		{
			if (roots.Contains(val))
			{
				return true;
			}
			val = val.parent;
		}
		return false;
	}

	private static bool HasMatchingParent(Transform transform, Category category)
	{
		Transform parent = transform.parent;
		while ((Object)(object)parent != (Object)null)
		{
			if (category.MatchesName(((Object)parent).name) && TypedOwner(((Component)parent).gameObject) == null)
			{
				return true;
			}
			parent = parent.parent;
		}
		return false;
	}

	private static Category TypedOwner(GameObject go)
	{
		Category[] builtIn = BuiltIn;
		foreach (Category category in builtIn)
		{
			if (category.Types == null || category.MatchesExclude(((Object)go).name))
			{
				continue;
			}
			Type[] types = category.Types;
			foreach (Type type in types)
			{
				if ((Object)(object)go.GetComponent(type) != (Object)null)
				{
					return category;
				}
			}
		}
		return null;
	}
}
internal static class NameRulesFile
{
	private class Rules
	{
		public string[] NameContains { get; set; }

		public string[] NameStartsWith { get; set; }

		public string[] NameExcludes { get; set; }

		public string[] NameMatches { get; set; }

		public string[] NameExcludesMatches { get; set; }
	}

	internal const string DefaultFileName = "orax.XRays.names.yaml";

	private const float CheckInterval = 0.5f;

	private static readonly Regex VersionLine = new Regex("^version[ \\t]*:[^\\r\\n]*", RegexOptions.Multiline | RegexOptions.Compiled);

	private static readonly IDeserializer Reader = ((BuilderSkeleton<DeserializerBuilder>)new DeserializerBuilder()).WithNamingConvention(CamelCaseNamingConvention.Instance).Build();

	private static readonly HashSet<string> KnownKeys = KnownCategoryKeys();

	private static string loadedName;

	private static string configPath;

	private static DateTime readDate;

	private static float checkTimer;

	public static void Load()
	{
		loadedName = CurrentName();
		string text = null;
		DateTime before = DateTime.MinValue;
		bool flag = false;
		try
		{
			if (loadedName.IndexOfAny(Path.GetInvalidPathChars()) >= 0)
			{
				throw new ArgumentException("the file name holds characters a path cannot have");
			}
			text = Path.Combine(Paths.ConfigPath, loadedName);
			before = File.GetLastWriteTimeUtc(text);
			string text2 = (File.Exists(text) ? File.ReadAllText(text) : null);
			if (text2 == null || VersionOf(text2) != "0.1.0")
			{
				text2 = Refresh(text, text2, ref before);
			}
			if (text2 != null)
			{
				Apply(Reader.Deserialize<Dictionary<string, Rules>>(VersionLine.Replace(text2, "")));
				flag = true;
				XRays instance = XRays.Instance;
				if (instance != null)
				{
					instance.Log.LogInfo((object)("Name rules read from " + loadedName + "."));
				}
			}
			else
			{
				Apply(null);
				XRays instance2 = XRays.Instance;
				if (instance2 != null)
				{
					instance2.Log.LogError((object)("XRays: " + loadedName + " is missing everywhere (BepInEx\\plugins, beside the mod and in BepInEx\\scripts): every category has no name rule."));
				}
			}
		}
		catch (Exception ex)
		{
			XRays instance3 = XRays.Instance;
			if (instance3 != null)
			{
				instance3.Log.LogError((object)("XRays: " + loadedName + " could not be read, the rules stay as they are: " + ex));
			}
		}
		finally
		{
			configPath = text ?? configPath;
			if (flag)
			{
				readDate = before;
			}
			else if (configPath != null)
			{
				readDate = File.GetLastWriteTimeUtc(configPath);
			}
		}
	}

	public static void Tick(float deltaTime)
	{
		checkTimer += deltaTime;
		if (!(checkTimer < 0.5f))
		{
			checkTimer = 0f;
			if (CurrentName() != loadedName)
			{
				Load();
			}
			else if (configPath != null && !(File.GetLastWriteTimeUtc(configPath) == readDate))
			{
				Load();
			}
		}
	}

	private static string Refresh(string path, string text, ref DateTime before)
	{
		string text2 = ShippedFile();
		if (text2 == null)
		{
			if (text != null)
			{
				XRays instance = XRays.Instance;
				if (instance != null)
				{
					instance.Log.LogWarning((object)("XRays: the shipped " + loadedName + " could not be found, the file of the config folder could not be replaced."));
				}
			}
			return text;
		}
		string text3 = Stamp(File.ReadAllText(text2), "0.1.0");
		File.WriteAllText(path, text3);
		before = File.GetLastWriteTimeUtc(path);
		XRays instance2 = XRays.Instance;
		if (instance2 != null)
		{
			instance2.Log.LogInfo((object)((text != null) ? ("Name rules file " + loadedName + " is written for another version of the mod: replaced by the shipped one.") : ("Name rules file copied to " + loadedName + ".")));
		}
		return text3;
	}

	private static string ShippedFile()
	{
		foreach (string item in ShippedDirectories())
		{
			string text = Path.Combine(item, loadedName);
			if (File.Exists(text))
			{
				return text;
			}
		}
		return null;
	}

	private static IEnumerable<string> ShippedDirectories()
	{
		yield return Paths.PluginPath;
		string location = Assembly.GetExecutingAssembly().Location;
		if (!string.IsNullOrEmpty(location))
		{
			yield return Path.GetDirectoryName(location);
		}
		yield return Path.Combine(Paths.BepInExRootPath, "scripts");
	}

	private static string VersionOf(string text)
	{
		Match match = VersionLine.Match(text);
		if (!match.Success)
		{
			return null;
		}
		string text2 = match.Value.Substring(match.Value.IndexOf(':') + 1).Trim();
		int i;
		for (i = 0; i < text2.Length && !char.IsWhiteSpace(text2[i]); i++)
		{
		}
		return text2.Substring(0, i).Trim('"', '\'');
	}

	private static string Stamp(string text, string version)
	{
		string line = "version: " + version;
		if (VersionLine.IsMatch(text))
		{
			return VersionLine.Replace(text, (Match match) => line);
		}
		return line + Environment.NewLine + text;
	}

	private static string CurrentName()
	{
		string text = (((Object)(object)XRays.Instance != (Object)null && XRays.Instance.NameRulesFileName != null) ? XRays.Instance.NameRulesFileName.Value : null);
		if (!string.IsNullOrWhiteSpace(text))
		{
			return text.Trim();
		}
		return "orax.XRays.names.yaml";
	}

	private static void Apply(Dictionary<string, Rules> read)
	{
		if (read == null)
		{
			read = new Dictionary<string, Rules>();
		}
		Category[] builtIn = Categories.BuiltIn;
		foreach (Category category in builtIn)
		{
			read.TryGetValue(category.Key, out var value);
			category.NameContains = value?.NameContains;
			category.NameStartsWith = value?.NameStartsWith;
			category.NameExcludes = value?.NameExcludes;
			category.NameMatches = value?.NameMatches;
			category.NameExcludesMatches = value?.NameExcludesMatches;
		}
		foreach (string key in read.Keys)
		{
			if (!KnownKeys.Contains(key))
			{
				XRays instance = XRays.Instance;
				if (instance != null)
				{
					instance.Log.LogWarning((object)("XRays: " + loadedName + " holds no category named \"" + key + "\"."));
				}
			}
		}
	}

	private static HashSet<string> KnownCategoryKeys()
	{
		HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal);
		Category[] builtIn = Categories.BuiltIn;
		foreach (Category category in builtIn)
		{
			hashSet.Add(category.Key);
		}
		return hashSet;
	}
}
internal class Panel
{
	private class CategoryRow
	{
		public Category Category;

		public Toggle Toggle;

		public Text Label;

		public Text Count;
	}

	private class LayerOption
	{
		public Toggle Toggle;

		public Text Label;

		public float Width;
	}

	private const float PanelWidth = 470f;

	private const float LeftMargin = -215f;

	private const float RightMargin = 215f;

	private const float ToggleSize = 26f;

	private const float RefreshInterval = 0.4f;

	private const float CountInterval = 1.5f;

	private const string SaveTip = "Writes the options ticked right now to the config. The saved setup is never applied by itself, not even when the game starts: it only comes back with the \"Restore setup\" button.";

	private const string RestoreTip = "Ticks back the options written with \"Save setup\", right away. Nothing of it runs while the game loads, so this button is the only way the saved setup is applied.";

	private const string TooltipsTip = "Shows the little box describing a row while the pointer rests on it. Turning it off takes every box away.";

	private const float MinPanelHeight = 360f;

	private const float MaxPanelHeight = 1200f;

	private const float BottomPadding = 8f;

	private const float MinViewportHeight = 160f;

	private const float ScrollbarInset = 6f;

	private const float ScrollbarWidth = 10f;

	private const float LayerRowHeight = 30f;

	private const float LayerLabelGap = 6f;

	private const float LayerItemGap = 14f;

	private const float LayerLineWidth = 430f;

	private const int LayerFontSize = 14;

	private readonly XRays plugin;

	private readonly VisibilityManager visibility;

	private readonly List<CategoryRow> categoryRows = new List<CategoryRow>();

	private GameObject root;

	private Transform container;

	private RectTransform viewportRect;

	private RectTransform contentRect;

	private RectTransform scrollbarRect;

	private float headerHeight;

	private float contentHeight;

	private float builtHeight;

	private Toggle terrainToggle;

	private Toggle waterToggle;

	private Toggle cloudsToggle;

	private Toggle mistToggle;

	private Toggle playerToggle;

	private Toggle hudToggle;

	private Toggle tooltipsToggle;

	private Button revealButton;

	private Text cursorText;

	private Text objectsText;

	private Tooltip tip;

	private float refreshTimer;

	private float countTimer;

	private int countCursor;

	internal static readonly Color BodyColor = new Color(0.93f, 0.88f, 0.79f, 1f);

	private Transform Parent
	{
		get
		{
			if (!((Object)(object)container != (Object)null))
			{
				return root.transform;
			}
			return container;
		}
	}

	public bool IsOpen
	{
		get
		{
			if ((Object)(object)root != (Object)null)
			{
				return root.activeSelf;
			}
			return false;
		}
	}

	public bool IsVisible
	{
		get
		{
			if (IsOpen && root.activeInHierarchy && (Object)(object)cursorText != (Object)null)
			{
				return ((Behaviour)cursorText).isActiveAndEnabled;
			}
			return false;
		}
	}

	public string CursorText
	{
		get
		{
			if (!((Object)(object)cursorText != (Object)null))
			{
				return null;
			}
			return cursorText.text;
		}
	}

	public Panel(XRays owner, VisibilityManager manager)
	{
		plugin = owner;
		visibility = manager;
	}

	public void Toggle()
	{
		if (IsOpen)
		{
			Close();
		}
		else
		{
			Open();
		}
	}

	public void Open()
	{
		if ((!((Object)(object)root == (Object)null) || Build()) && !((Object)(object)root == (Object)null))
		{
			root.SetActive(true);
			GUIManager.BlockInput(true);
			Refresh();
			RefreshCounts();
		}
	}

	public void Close()
	{
		if ((Object)(object)root != (Object)null && root.activeSelf)
		{
			root.SetActive(false);
		}
		GUIManager.BlockInput(false);
		if (tip != null)
		{
			tip.Reset();
		}
	}

	public void Invalidate()
	{
		root = null;
		container = null;
		tip = null;
		viewportRect = null;
		contentRect = null;
		scrollbarRect = null;
		headerHeight = 0f;
		contentHeight = 0f;
		builtHeight = 0f;
		terrainToggle = null;
		waterToggle = null;
		cloudsToggle = null;
		mistToggle = null;
		playerToggle = null;
		hudToggle = null;
		tooltipsToggle = null;
		revealButton = null;
		cursorText = null;
		objectsText = null;
		countCursor = 0;
		categoryRows.Clear();
	}

	public void Tick(float deltaTime)
	{
		if (IsOpen)
		{
			refreshTimer += deltaTime;
			if (refreshTimer >= 0.4f)
			{
				refreshTimer = 0f;
				Refresh();
			}
			countTimer += deltaTime;
			if (countTimer >= 1.5f)
			{
				countTimer = 0f;
				TickCounts();
			}
			if (plugin.ShowTooltips.Value)
			{
				tip.Tick(deltaTime);
			}
			else
			{
				tip.Reset();
			}
		}
	}

	public void Refresh()
	{
		if (!IsOpen)
		{
			return;
		}
		float num = Mathf.Clamp(plugin.PanelHeight.Value, 360f, 1200f);
		if (!Mathf.Approximately(num, builtHeight))
		{
			ApplyHeight(num);
		}
		terrainToggle.SetIsOnWithoutNotify(visibility.TerrainHidden);
		waterToggle.SetIsOnWithoutNotify(visibility.WaterHidden);
		cloudsToggle.SetIsOnWithoutNotify(visibility.CloudsHidden);
		mistToggle.SetIsOnWithoutNotify(visibility.MistHidden);
		playerToggle.SetIsOnWithoutNotify(visibility.PlayerHidden);
		hudToggle.SetIsOnWithoutNotify(visibility.HudHidden);
		tooltipsToggle.SetIsOnWithoutNotify(plugin.ShowTooltips.Value);
		foreach (CategoryRow categoryRow in categoryRows)
		{
			categoryRow.Toggle.SetIsOnWithoutNotify(visibility.IsCategoryHidden(categoryRow.Category.Key));
		}
		SetButtonLabel(revealButton, visibility.IsRevealing ? "Restore" : (visibility.AnyOptionChecked ? "Reveal" : "Hide"));
		objectsText.text = "Hidden objects: " + visibility.HiddenRootCount + "    Other: " + visibility.OffListCount;
		if (!PointerOverPanel())
		{
			cursorText.text = PickService.Describe(plugin.PickDistance.Value, visibility);
		}
	}

	private bool PointerOverPanel()
	{
		//IL_0019: Unknown result type (might be due to invalid IL or missing references)
		//IL_001e: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)root != (Object)null)
		{
			return RectTransformUtility.RectangleContainsScreenPoint(root.GetComponent<RectTransform>(), Vector2.op_Implicit(Input.mousePosition));
		}
		return false;
	}

	public void RefreshCounts()
	{
		if (!IsOpen)
		{
			return;
		}
		foreach (CategoryRow categoryRow in categoryRows)
		{
			categoryRow.Count.text = Categories.Count(categoryRow.Category).ToString();
		}
	}

	public void TickCounts()
	{
		if (IsOpen && categoryRows.Count != 0)
		{
			countCursor %= categoryRows.Count;
			CategoryRow categoryRow = categoryRows[countCursor++];
			categoryRow.Count.text = Categories.Count(categoryRow.Category).ToString();
		}
	}

	private bool Build()
	{
		try
		{
			return BuildPanel();
		}
		catch (Exception ex)
		{
			if ((Object)(object)root != (Object)null)
			{
				Object.Destroy((Object)(object)root);
			}
			Invalidate();
			plugin.Log.LogError((object)("XRays panel: build failed: " + ex));
			return false;
		}
	}

	private bool BuildPanel()
	{
		//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e3: Unknown result type (might be due to invalid IL or missing references)
		//IL_0170: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ae: Unknown result type (might be due to invalid IL or missing references)
		//IL_01b8: Expected O, but got Unknown
		//IL_01ef: Unknown result type (might be due to invalid IL or missing references)
		//IL_0379: Unknown result type (might be due to invalid IL or missing references)
		//IL_03ab: Unknown result type (might be due to invalid IL or missing references)
		//IL_048b: Unknown result type (might be due to invalid IL or missing references)
		//IL_04eb: Unknown result type (might be due to invalid IL or missing references)
		//IL_04f5: Expected O, but got Unknown
		//IL_053a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0544: Expected O, but got Unknown
		//IL_05a7: 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_062d: Expected O, but got Unknown
		//IL_067d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0687: Expected O, but got Unknown
		//IL_06cc: Unknown result type (might be due to invalid IL or missing references)
		//IL_06d6: Expected O, but got Unknown
		//IL_070d: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)GUIManager.CustomGUIFront == (Object)null)
		{
			plugin.Log.LogWarning((object)"XRays panel: Jötunn interface not available yet");
			return false;
		}
		GUIManager instance = GUIManager.Instance;
		float cursor = 14f;
		float[] array = new float[7 + Categories.DisplayOrder.Length];
		array[0] = Row(44f);
		headerHeight = cursor + 8f;
		cursor = 6f;
		array[1] = Row(80f);
		builtHeight = Mathf.Clamp(plugin.PanelHeight.Value, 360f, 1200f);
		root = instance.CreateWoodpanel(GUIManager.CustomGUIFront.transform, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), new Vector2(280f, 0f), 470f, builtHeight, true);
		if ((Object)(object)root == (Object)null)
		{
			plugin.Log.LogError((object)"XRays panel: woodpanel creation failed");
			return false;
		}
		categoryRows.Clear();
		container = root.transform;
		tip = new Tooltip(root.GetComponent<RectTransform>());
		MakeText("XRays", -215f, array[0], 300f, 30f, 22, instance.ValheimOrange, (TextAnchor)3, instance.AveriaSerifBold);
		Button val = MakeButton("Close", 215f, array[0], 96f, 30f);
		((UnityEvent)val.onClick).AddListener(new UnityAction(Close));
		tip.Attach(((Component)val).gameObject, "Closes the panel (Escape by default).");
		MakeScrollArea();
		cursorText = MakeText("Under the cursor: —", -215f, array[1], 430f, 80f, 13, BodyColor, (TextAnchor)3, instance.AveriaSerif);
		cursor += 10f;
		cursor = MakeLayerOptions(instance, cursor);
		for (int i = 0; i < Categories.DisplayOrder.Length; i++)
		{
			array[2 + i] = Row(26f);
		}
		array[2 + Categories.DisplayOrder.Length] = Row(30f);
		array[3 + Categories.DisplayOrder.Length] = Row(36f);
		array[4 + Categories.DisplayOrder.Length] = Row(26f);
		array[5 + Categories.DisplayOrder.Length] = Row(34f);
		array[6 + Categories.DisplayOrder.Length] = Row(100f);
		contentHeight = cursor + 16f;
		ApplyHeight(builtHeight);
		for (int j = 0; j < Categories.DisplayOrder.Length; j++)
		{
			Category category = Categories.DisplayOrder[j];
			float y = array[2 + j];
			CategoryRow categoryRow = new CategoryRow
			{
				Category = category,
				Toggle = MakeToggle(-215f, y, visibility.IsCategoryHidden(category.Key), delegate(bool v)
				{
					OnCategoryToggle(category, v);
				})
			};
			categoryRow.Label = MakeText(category.Label, -179f, y, 250f, 26f, 15, BodyColor, (TextAnchor)3, instance.AveriaSerif);
			categoryRow.Count = MakeText("0", 215f, y, 120f, 26f, 14, BodyColor, (TextAnchor)5, instance.AveriaSerif, rightAlign: true);
			tip.Attach(((Component)categoryRow.Toggle).gameObject, category.Description);
			tip.Attach(((Component)categoryRow.Label).gameObject, category.Description);
			tip.Attach(((Component)categoryRow.Count).gameObject, category.Description);
			categoryRows.Add(categoryRow);
		}
		int num = 2 + Categories.DisplayOrder.Length;
		int num2 = num + 1;
		int num3 = num2 + 1;
		int num4 = num3 + 1;
		int num5 = num4 + 1;
		objectsText = MakeText("Hidden objects: 0    Other: 0", -215f, array[num], 230f, 30f, 14, BodyColor, (TextAnchor)3, instance.AveriaSerif);
		tip.Attach(((Component)objectsText).gameObject, "The object roots currently invisible (ticked categories, objects picked one by one, Hide all), and what is hidden without a row of its own in this list.");
		Button val2 = MakeButton("Save setup", -5f, array[num2], 210f, 24f);
		((UnityEvent)val2.onClick).AddListener(new UnityAction(OnSaveSetup));
		tip.Attach(((Component)val2).gameObject, "Writes the options ticked right now to the config. The saved setup is never applied by itself, not even when the game starts: it only comes back with the \"Restore setup\" button.");
		Button val3 = MakeButton("Restore setup", 215f, array[num2], 210f, 24f);
		((UnityEvent)val3.onClick).AddListener(new UnityAction(OnRestoreSetup));
		tip.Attach(((Component)val3).gameObject, "Ticks back the options written with \"Save setup\", right away. Nothing of it runs while the game loads, so this button is the only way the saved setup is applied.");
		tooltipsToggle = MakeToggle(-215f, array[num3], plugin.ShowTooltips.Value, OnTooltipsToggle);
		Text val4 = MakeText("Show tooltips", -179f, array[num3], 380f, 26f, 15, BodyColor, (TextAnchor)3, instance.AveriaSerif);
		tip.Attach(((Component)tooltipsToggle).gameObject, "Shows the little box describing a row while the pointer rests on it. Turning it off takes every box away.");
		tip.Attach(((Component)val4).gameObject, "Shows the little box describing a row while the pointer rests on it. Turning it off takes every box away.");
		revealButton = MakeButton("Reveal", 15f, array[num4], 90f, 30f);
		((UnityEvent)revealButton.onClick).AddListener(new UnityAction(OnToggleReveal));
		tip.Attach(((Component)revealButton).gameObject, RevealTip);
		Button val5 = MakeButton("Hide all", 115f, array[num4], 90f, 30f);
		((UnityEvent)val5.onClick).AddListener(new UnityAction(OnHideAll));
		tip.Attach(((Component)val5).gameObject, "Checks every option of the list at once: terrain, water, clouds, mist, character, HUD and every category. It also replaces the setup the reveal would bring back.");
		Button val6 = MakeButton("Show all", 215f, array[num4], 90f, 30f);
		((UnityEvent)val6.onClick).AddListener(new UnityAction(OnRestoreAll));
		tip.Attach(((Component)val6).gameObject, "Everything back on screen: every option unchecked and the objects picked one by one shown again. It also replaces the setup the reveal would bring back.");
		string content = PluginHelpText();
		MakeText(content, -215f, array[num5], 430f, 100f, 13, BodyColor, (TextAnchor)3, instance.AveriaSerif);
		container = null;
		return true;
		float Row(float height)
		{
			float result = 0f - (cursor + height / 2f);
			cursor += height;
			return result;
		}
	}

	private float MakeLayerOptions(GUIManager gui, float top)
	{
		LayerOption[] array = new LayerOption[6]
		{
			MakeLayerOption(gui, "Clouds", "The clouds of the sky: the big planes and the horizon cylinder, the particle clouds and the distant mist clouds.", visibility.CloudsHidden, delegate(bool v)
			{
				visibility.SetClouds(v);
				Refresh();
			}),
			MakeLayerOption(gui, "HUD", "The whole in-game HUD: health, stamina and eitr bars, hot bar, mini map, crosshair, hints and panels.", visibility.HudHidden, delegate(bool v)
			{
				visibility.SetHud(v);
				Refresh();
			}),
			MakeLayerOption(gui, "Mist", "The mist and the fog: ground mist, weather fog, distant fog planes, ocean mist, the Mistlands mist and the distance fog of the game.", visibility.MistHidden, delegate(bool v)
			{
				visibility.SetMist(v);
				Refresh();
			}),
			MakeLayerOption(gui, "Player", "Your own character — body, armor and held item — out of the picture. Renderers only; the camera and the colliders stay.", visibility.PlayerHidden, delegate(bool v)
			{
				visibility.SetPlayer(v);
				Refresh();
			}),
			MakeLayerOption(gui, "Terrain", "The ground itself: the loaded zones, the sea bed and the horizon, taken away so you can see through it.", visibility.TerrainHidden, delegate(bool v)
			{
				visibility.SetTerrain(v);
				Refresh();
			}),
			MakeLayerOption(gui, "Water", "The water: the surface of the loaded zones and the sea drawn beyond them.", visibility.WaterHidden, delegate(bool v)
			{
				visibility.SetWater(v);
				Refresh();
			})
		};
		cloudsToggle = array[0].Toggle;
		hudToggle = array[1].Toggle;
		mistToggle = array[2].Toggle;
		playerToggle = array[3].Toggle;
		terrainToggle = array[4].Toggle;
		waterToggle = array[5].Toggle;
		float num = 0f;
		for (int num2 = 0; num2 < array.Length; num2++)
		{
			num += array[num2].Width + ((num2 > 0) ? 14f : 0f);
		}
		int num3 = ((num <= 430f) ? array.Length : ((array.Length + 1) / 2));
		float y = 0f - (top + 15f);
		PlaceLayers(array, 0, num3, y);
		top += 30f;
		if (num3 < array.Length)
		{
			y = 0f - (top + 15f);
			PlaceLayers(array, num3, array.Length - num3, y);
			top += 30f;
		}
		return top;
	}

	private LayerOption MakeLayerOption(GUIManager gui, string label, string tipText, bool shown, Action<bool> onChanged)
	{
		//IL_0038: Unknown result type (might be due to invalid IL or missing references)
		//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_007a: 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)
		LayerOption layerOption = new LayerOption
		{
			Toggle = MakeToggle(-215f, 0f, shown, onChanged),
			Label = MakeText(label, -215f, 0f, 300f, 26f, 14, BodyColor, (TextAnchor)3, gui.AveriaSerif)
		};
		HorizontalWrapMode horizontalOverflow = layerOption.Label.horizontalOverflow;
		layerOption.Label.horizontalOverflow = (HorizontalWrapMode)1;
		float preferredWidth = layerOption.Label.preferredWidth;
		layerOption.Label.horizontalOverflow = horizontalOverflow;
		((Graphic)layerOption.Label).rectTransform.sizeDelta = new Vector2(preferredWidth + 6f, 26f);
		layerOption.Width = 32f + preferredWidth;
		tip.Attach(((Component)layerOption.Toggle).gameObject, tipText);
		tip.Attach(((Component)layerOption.Label).gameObject, tipText);
		return layerOption;
	}

	private static void PlaceLayers(LayerOption[] options, int from, int count, float y)
	{
		//IL_0019: Unknown result type (might be due to invalid IL or missing references)
		//IL_0020: Unknown result type (might be due to invalid IL or missing references)
		//IL_0043: Unknown result type (might be due to invalid IL or missing references)
		float num = -215f;
		for (int i = from; i < from + count; i++)
		{
			LayerOption layerOption = options[i];
			((RectTransform)((Component)layerOption.Toggle).transform).anchoredPosition = new Vector2(num, y);
			((Graphic)layerOption.Label).rectTransform.anchoredPosition = new Vector2(num + 26f + 6f, y);
			num += layerOption.Width + 14f;
		}
	}

	private void MakeScrollArea()
	{
		//IL_0059: Unknown result type (might be due to invalid IL or missing references)
		//IL_005f: Expected O, but got Unknown
		//IL_0066: Unknown result type (might be due to invalid IL or missing references)
		//IL_0070: Expected O, but got Unknown
		//IL_0097: 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_00cb: 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_00fd: Unknown result type (might be due to invalid IL or missing references)
		//IL_0122: Unknown result type (might be due to invalid IL or missing references)
		float viewportHeight = GetViewportHeight(builtHeight);
		GameObject val = new GameObject("XRaysViewport", new Type[5]
		{
			typeof(RectTransform),
			typeof(CanvasRenderer),
			typeof(Image),
			typeof(RectMask2D),
			typeof(ScrollRect)
		});
		viewportRect = (RectTransform)val.transform;
		((Transform)viewportRect).SetParent(root.transform, false);
		viewportRect.anchorMin = new Vector2(0.5f, 1f);
		viewportRect.anchorMax = new Vector2(0.5f, 1f);
		viewportRect.pivot = new Vector2(0.5f, 1f);
		viewportRect.anchoredPosition = new Vector2(0f, 0f - headerHeight);
		viewportRect.sizeDelta = new Vector2(470f, viewportHeight);
		Image component = val.GetComponent<Image>();
		((Graphic)component).color = new Color(0f, 0f, 0f, 0f);
		((Graphic)component).raycastTarget = true;
		contentRect = MakeContentRect(viewportHeight);
		ScrollRect component2 = val.GetComponent<ScrollRect>();
		component2.content = contentRect;
		component2.viewport = viewportRect;
		component2.horizontal = false;
		component2.vertical = true;
		component2.movementType = (MovementType)2;
		component2.inertia = false;
		component2.scrollSensitivity = 30f;
		component2.verticalNormalizedPosition = 1f;
		Scrollbar verticalScrollbar = MakeScrollbar(viewportHeight);
		component2.verticalScrollbar = verticalScrollbar;
		component2.verticalScrollbarVisibility = (ScrollbarVisibility)1;
		component2.verticalScrollbarSpacing = 4f;
		container = (Transform)(object)contentRect;
	}

	private RectTransform MakeContentRect(float viewportHeight)
	{
		//IL_0018: Unknown result type (might be due to invalid IL or missing references)
		//IL_0022: Unknown result type (might be due to invalid IL or missing references)
		//IL_0027: 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_003f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0049: Unknown result type (might be due to invalid IL or missing references)
		//IL_0054: Unknown result type (might be due to invalid IL or missing references)
		//IL_005e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0069: 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_0074: 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_0090: Unknown result type (might be due to invalid IL or missing references)
		//IL_009b: Expected O, but got Unknown
		RectTransform val = (RectTransform)new GameObject("XRaysContent", new Type[1] { typeof(RectTransform) }).transform;
		((Transform)val).SetParent((Transform)(object)viewportRect, false);
		val.anchorMin = new Vector2(0.5f, 1f);
		val.anchorMax = new Vector2(0.5f, 1f);
		val.pivot = new Vector2(0.5f, 1f);
		val.anchoredPosition = Vector2.zero;
		val.sizeDelta = new Vector2(470f, Mathf.Max(contentHeight, viewportHeight));
		return val;
	}

	private Scrollbar MakeScrollbar(float viewportHeight)
	{
		//IL_003f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0045: Expected O, but got Unknown
		//IL_004c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0056: Expected O, but got Unknown
		//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_00b1: 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_00eb: Unknown result type (might be due to invalid IL or missing references)
		//IL_010f: Unknown result type (might be due to invalid IL or missing references)
		//IL_014b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0150: Unknown result type (might be due to invalid IL or missing references)
		//IL_0156: Unknown result type (might be due to invalid IL or missing references)
		//IL_015c: Expected O, but got Unknown
		//IL_0174: Unknown result type (might be due to invalid IL or missing references)
		//IL_0189: Unknown result type (might be due to invalid IL or missing references)
		//IL_019e: Unknown result type (might be due to invalid IL or missing references)
		//IL_01b3: Unknown result type (might be due to invalid IL or missing references)
		//IL_01c4: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ea: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ef: Unknown result type (might be due to invalid IL or missing references)
		//IL_01f2: Unknown result type (might be due to invalid IL or missing references)
		//IL_0212: Unknown result type (might be due to invalid IL or missing references)
		//IL_0232: Unknown result type (might be due to invalid IL or missing references)
		//IL_023e: Unknown result type (might be due to invalid IL or missing references)
		//IL_025e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0275: Unknown result type (might be due to invalid IL or missing references)
		GameObject val = new GameObject("XRaysScrollbar", new Type[4]
		{
			typeof(RectTransform),
			typeof(CanvasRenderer),
			typeof(Image),
			typeof(Scrollbar)
		});
		scrollbarRect = (RectTransform)val.transform;
		((Transform)scrollbarRect).SetParent(root.transform, false);
		scrollbarRect.anchorMin = new Vector2(1f, 1f);
		scrollbarRect.anchorMax = new Vector2(1f, 1f);
		scrollbarRect.pivot = new Vector2(1f, 0.5f);
		scrollbarRect.anchoredPosition = new Vector2(-6f, 0f - (headerHeight + viewportHeight / 2f));
		scrollbarRect.sizeDelta = new Vector2(10f, viewportHeight);
		((Graphic)val.GetComponent<Image>()).color = new Color(0f, 0f, 0f, 0.4f);
		GameObject val2 = new GameObject("Handle", new Type[3]
		{
			typeof(RectTransform),
			typeof(CanvasRenderer),
			typeof(Image)
		});
		RectTransform val3 = (RectTransform)val2.transform;
		((Transform)val3).SetParent((Transform)(object)scrollbarRect, false);
		val3.anchorMin = new Vector2(0f, 0f);
		val3.anchorMax = new Vector2(1f, 1f);
		val3.offsetMin = new Vector2(1f, 2f);
		val3.offsetMax = new Vector2(-1f, -2f);
		Image component = val2.GetComponent<Image>();
		((Graphic)component).color = BodyColor;
		Scrollbar component2 = val.GetComponent<Scrollbar>();
		component2.handleRect = val3;
		((Selectable)component2).targetGraphic = (Graphic)(object)component;
		component2.direction = (Direction)2;
		ColorBlock colors = ((Selectable)component2).colors;
		((ColorBlock)(ref colors)).normalColor = BodyColor;
		((ColorBlock)(ref colors)).highlightedColor = new Color(1f, 0.96f, 0.88f, 1f);
		((ColorBlock)(ref colors)).pressedColor = new Color(0.76f, 0.7f, 0.6f, 1f);
		((ColorBlock)(ref colors)).selectedColor = BodyColor;
		((ColorBlock)(ref colors)).disabledColor = new Color(0.5f, 0.5f, 0.5f, 0.8f);
		((ColorBlock)(ref colors)).fadeDuration = 0.1f;
		((Selectable)component2).colors = colors;
		return component2;
	}

	private float GetViewportHeight(float panelHeight)
	{
		return Mathf.Max(160f, panelHeight - headerHeight - 8f);
	}

	private void ApplyHeight(float height)
	{
		//IL_0020: Unknown result type (might be due to invalid IL or missing references)
		//IL_003c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0052: 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_0097: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
		builtHeight = height;
		float viewportHeight = GetViewportHeight(height);
		root.GetComponent<RectTransform>().sizeDelta = new Vector2(470f, height);
		viewportRect.anchoredPosition = new Vector2(0f, 0f - headerHeight);
		viewportRect.sizeDelta = new Vector2(470f, viewportHeight);
		contentRect.sizeDelta = new Vector2(470f, Mathf.Max(contentHeight, viewportHeight));
		scrollbarRect.anchoredPosition = new Vector2(-6f, 0f - (headerHeight + viewportHeight / 2f));
		scrollbarRect.sizeDelta = new Vector2(10f, viewportHeight);
	}

	private string RevealTip()
	{
		if (visibility.IsRevealing)
		{
			return "Brings back the setup that was on before the reveal; anything ticked while the reveal was on is kept.";
		}
		if (!visibility.AnyOptionChecked)
		{
			return "Nothing is checked: the first press checks every option of the list instead.";
		}
		return "Shows everything the checked options are hiding; the next press brings that exact setup back.";
	}

	private string PluginHelpText()
	{
		List<string> list = new List<string>();
		AddHelpLine(list, (XRays.Instance.KeyPanel, "panel"), (XRays.Instance.KeyClose, "close"), (XRays.Instance.KeyHud, "hud"));
		AddHelpLine(list, (XRays.Instance.KeyTerrain, "terrain"), (XRays.Instance.KeyWater, "water"), (XRays.Instance.KeyClouds, "clouds"));
		AddHelpLine(list, (XRays.Instance.KeyMist, "mist"), (XRays.Instance.KeyPlayer, "player"), (XRays.Instance.KeyReveal, "reveal / restore"));
		AddHelpLine(list, (XRays.Instance.KeyPick, "hide object under cursor"));
		AddHelpLine(list, (XRays.Instance.KeyPickCategory, "whole category under the cursor"));
		return string.Join("\n", list);
	}

	private static void AddHelpLine(List<string> lines, params (ConfigEntry<KeyboardShortcut> key, string label)[] hotkeys)
	{
		//IL_0022: Unknown result type (might be due to invalid IL or missing references)
		//IL_0027: 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_0044: Unknown result type (might be due to invalid IL or missing references)
		//IL_0049: Unknown result type (might be due to invalid IL or missing references)
		List<string> list = new List<string>();
		for (int i = 0; i < hotkeys.Length; i++)
		{
			var (val, text) = hotkeys[i];
			if (!((object)val.Value/*cast due to .constrained prefix*/).Equals((object?)KeyboardShortcut.Empty))
			{
				list.Add(((object)val.Value/*cast due to .constrained prefix*/).ToString() + " : " + text);
			}
		}
		if (list.Count > 0)
		{
			lines.Add(string.Join("   ·   ", list));
		}
	}

	private void OnCategoryToggle(Category category, bool hidden)
	{
		visibility.SetCategory(category, hidden);
		Refresh();
	}

	private void OnSaveSetup()
	{
		plugin.SaveSetup();
	}

	private void OnRestoreSetup()
	{
		plugin.RestoreSetup();
		Refresh();
	}

	private void OnTooltipsToggle(bool show)
	{
		plugin.ShowTooltips.Value = show;
	}

	private void OnRestoreAll()
	{
		visibility.RestoreAll();
		Refresh();
	}

	private void OnToggleReveal()
	{
		visibility.ToggleChecked();
		Refresh();
	}

	private static void SetButtonLabel(Button button, string label)
	{
		Text val = (((Object)(object)button != (Object)null) ? ((Component)button).GetComponentInChildren<Text>(true) : null);
		if ((Object)(object)val != (Object)null && val.text != label)
		{
			val.text = label;
		}
	}

	private void OnHideAll()
	{
		visibility.HideAll();
		Refresh();
	}

	private Text MakeText(string content, float x, float y, float width, float height, int fontSize, Color color, TextAnchor alignment, Font font, bool rightAlign = false)
	{
		//IL_0016: Unknown result type (might be due to invalid IL or missing references)
		//IL_0025: Unknown result type (might be due to invalid IL or missing references)
		//IL_002a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0033: Unknown result type (might be due to invalid IL or missing references)
		//IL_0036: Unknown result type (might be due to invalid IL or missing references)
		//IL_006b: Unknown result type (might be due to invalid IL or missing references)
		//IL_005a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0077: Unknown result type (might be due to invalid IL or missing references)
		//IL_0087: Unknown result type (might be due to invalid IL or missing references)
		GameObject obj = GUIManager.Instance.CreateText(content, Parent, new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), Vector2.zero, font, fontSize, color, true, Color.black, width, height, false);
		RectTransform component = obj.GetComponent<RectTransform>();
		component.pivot = (rightAlign ? new Vector2(1f, 0.5f) : new Vector2(0f, 0.5f));
		component.anchoredPosition = new Vector2(x, y);
		Text component2 = obj.GetComponent<Text>();
		component2.alignment = alignment;
		component2.verticalOverflow = (VerticalWrapMode)1;
		component2.horizontalOverflow = (HorizontalWrapMode)0;
		return component2;
	}

	private Button MakeButton(string label, float x, float y, float width, float height)
	{
		//IL_0016: Unknown result type (might be due to invalid IL or missing references)
		//IL_0025: Unknown result type (might be due to invalid IL or missing references)
		//IL_002a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0049: Unknown result type (might be due to invalid IL or missing references)
		//IL_0055: Unknown result type (might be due to invalid IL or missing references)
		GameObject obj = GUIManager.Instance.CreateButton(label, Parent, new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), Vector2.zero, width, height);
		RectTransform component = obj.GetComponent<RectTransform>();
		component.pivot = new Vector2(1f, 0.5f);
		component.anchoredPosition = new Vector2(x, y);
		Button component2 = obj.GetComponent<Button>();
		GUIManager.Instance.ApplyButtonStyle(component2, 14);
		return component2;
	}

	private Toggle MakeToggle(float x, float y, bool initial, Action<bool> onChanged)
	{
		//IL_0039: Unknown result type (might be due to invalid IL or missing references)
		//IL_004e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0063: Unknown result type (might be due to invalid IL or missing references)
		//IL_006f: Unknown result type (might be due to invalid IL or missing references)
		GameObject obj = GUIManager.Instance.CreateToggle(Parent, 26f, 26f);
		RectTransform component = obj.GetComponent<RectTransform>();
		component.anchorMin = new Vector2(0.5f, 1f);
		component.anchorMax = new Vector2(0.5f, 1f);
		component.pivot = new Vector2(0f, 0.5f);
		component.anchoredPosition = new Vector2(x, y);
		Toggle component2 = obj.GetComponent<Toggle>();
		component2.SetIsOnWithoutNotify(initial);
		if (onChanged != null)
		{
			((UnityEvent<bool>)(object)component2.onValueChanged).AddListener((UnityAction<bool>)delegate(bool value)
			{
				onChanged(value);
			});
		}
		return component2;
	}
}
[HarmonyPatch(typeof(Menu), "UpdateNavigation")]
internal static class Patch_MenuNavigation
{
	private static void Postfix()
	{
		PauseMenuEntry.WireNavigation();
	}
}
[HarmonyPatch(typeof(Menu), "Show")]
internal static class Patch_MenuShow
{
	private static void Postfix(Menu __instance)
	{
		PauseMenuEntry.Add(__instance);
	}
}
internal static class PauseMenuEntry
{
	private const string EntryLabel = "XRays";

	private const string EntryName = "XRaysEntry";

	private static Button entryButton;

	private static Menu pauseMenu;

	private static Selectable belowSettings;

	private static bool failed;

	internal static void Add(Menu menu)
	{
		try
		{
			pauseMenu = menu;
			XRays instance = XRays.Instance;
			if ((Object)(object)instance == (Object)null)
			{
				return;
			}
			instance.EndMenuCloseGrace();
			if (!instance.Enable.Value)
			{
				Forget("Enable is off");
				return;
			}
			if ((Object)(object)entryButton == (Object)null)
			{
				entryButton = Build(menu);
			}
			if ((Object)(object)entryButton != (Object)null)
			{
				WireNavigation();
			}
		}
		catch (Exception ex)
		{
			if (!failed)
			{
				failed = true;
				XRays instance2 = XRays.Instance;
				if (instance2 != null)
				{
					instance2.Log.LogError((object)("Pause menu entry: " + ex));
				}
			}
		}
	}

	internal static void OnSettingChanged()
	{
		Menu val = pauseMenu;
		if ((Object)(object)val != (Object)null)
		{
			Add(val);
		}
	}

	internal static void WireNavigation()
	{
		//IL_0020: Unknown result type (might be due to invalid IL or missing references)
		//IL_0025: Unknown result type (might be due to invalid IL or missing references)
		//IL_0038: Unknown result type (might be due to invalid IL or missing references)
		//IL_003d: Unknown result type (might be due to invalid IL or missing references)
		//IL_004c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0051: Unknown result type (might be due to invalid IL or missing references)
		//IL_006f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0076: Unknown result type (might be due to invalid IL or missing references)
		//IL_007b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0085: Unknown result type (might be due to invalid IL or missing references)
		Button val = entryButton;
		if ((Object)(object)val == (Object)null)
		{
			return;
		}
		Button val2 = SettingsButton();
		if (!((Object)(object)val2 == (Object)null))
		{
			Navigation navigation = ((Selectable)val2).navigation;
			if (!((Object)(object)((Navigation)(ref navigation)).selectOnDown == (Object)(object)val))
			{
				navigation = ((Selectable)val2).navigation;
				belowSettings = ((Navigation)(ref navigation)).selectOnDown;
				Navigation navigation2 = ((Selectable)val).navigation;
				((Navigation)(ref navigation2)).mode = (Mode)4;
				((Navigation)(ref navigation2)).selectOnUp = (Selectable)(object)val2;
				((Navigation)(ref navigation2)).selectOnDown = belowSettings;
				((Selectable)val).navigation = navigation2;
				Navigation navigation3 = ((Selectable)val2).navigation;
				((Navigation)(ref navigation3)).selectOnDown = (Selectable)(object)val;
				((Selectable)val2).navigation = navigation3;
			}
		}
	}

	private static void OnClicked()
	{
		XRays instance = XRays.Instance;
		if (!((Object)(object)instance == (Object)null) && instance.Enable.Value && instance.Panel != null)
		{
			instance.BeginMenuCloseGrace();
			if ((Object)(object)pauseMenu != (Object)null)
			{
				pauseMenu.Hide();
			}
			instance.Panel.Open();
		}
	}

	private static Button Build(Menu menu)
	{
		//IL_007d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0087: Expected O, but got Unknown
		//IL_0094: Unknown result type (might be due to invalid IL or missing references)
		//IL_009e: Expected O, but got Unknown
		Button settingsButton = menu.m_settingsButton;
		Transform val = (((Object)(object)settingsButton != (Object)null) ? ((Component)settingsButton).transform.parent : null);
		if ((Object)(object)settingsButton == (Object)null || (Object)(object)val == (Object)null)
		{
			return Fail("Menu carries no Settings button to take the shape of.");
		}
		GameObject val2 = Object.Instantiate<GameObject>(((Component)settingsButton).gameObject, val);
		((Object)val2).name = "XRaysEntry";
		val2.SetActive(true);
		Button component = val2.GetComponent<Button>();
		if ((Object)(object)component == (Object)null)
		{
			Object.Destroy((Object)(object)val2);
			return Fail("The button taken as a shape carries no Button.");
		}
		component.onClick = new ButtonClickedEvent();
		((UnityEvent)component.onClick).AddListener(new UnityAction(OnClicked));
		((Selectable)component).interactable = true;
		SetLabel(val2);
		val2.transform.SetSiblingIndex(((Component)settingsButton).transform.GetSiblingIndex() + 1);
		XRays instance = XRays.Instance;
		if (instance != null)
		{
			instance.Log.LogInfo((object)"Pause menu: entry added (XRays).");
		}
		return component;
	}

	private static void Forget(string why)
	{
		//IL_0058: 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_006f: 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_003c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0041: 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)
		Button val = entryButton;
		entryButton = null;
		if ((Object)(object)val == (Object)null)
		{
			return;
		}
		Button val2 = SettingsButton();
		if ((Object)(object)val2 != (Object)null)
		{
			Navigation navigation = ((Selectable)val2).navigation;
			if ((Object)(object)((Navigation)(ref navigation)).selectOnDown == (Object)(object)val)
			{
				Navigation navigation2 = ((Selectable)val2).navigation;
				((Navigation)(ref navigation2)).selectOnDown = belowSettings;
				((Selectable)val2).navigation = navigation2;
			}
		}
		Navigation navigation3 = ((Selectable)val).navigation;
		((Navigation)(ref navigation3)).selectOnUp = null;
		((Navigation)(ref navigation3)).selectOnDown = null;
		((Selectable)val).navigation = navigation3;
		XRays instance = XRays.Instance;
		if (instance != null)
		{
			instance.Log.LogInfo((object)("Pause menu: entry removed (" + why + ")."));
		}
		Object.Destroy((Object)(object)((Component)val).gameObject);
		belowSettings = null;
	}

	internal static void Cleanup()
	{
		try
		{
			Forget("plugin unloaded");
		}
		catch (Exception ex)
		{
			XRays instance = XRays.Instance;
			if (instance != null)
			{
				instance.Log.LogError((object)("Pause menu cleanup: " + ex));
			}
		}
	}

	private static Button SettingsButton()
	{
		if (!((Object)(object)pauseMenu != (Object)null))
		{
			return null;
		}
		return pauseMenu.m_settingsButton;
	}

	private static void SetLabel(GameObject entry)
	{
		TMP_Text componentInChildren = entry.GetComponentInChildren<TMP_Text>(true);
		if ((Object)(object)componentInChildren != (Object)null && !string.Equals(componentInChildren.text, "XRays", StringComparison.Ordinal))
		{
			componentInChildren.text = "XRays";
		}
		Text componentInChildren2 = entry.GetComponentInChildren<Text>(true);
		if ((Object)(object)componentInChildren2 != (Object)null && !string.Equals(componentInChildren2.text, "XRays", StringComparison.Ordinal))
		{
			componentInChildren2.text = "XRays";
		}
	}

	private static Button Fail(string reason)
	{
		if (!failed)
		{
			failed = true;
			XRays instance = XRays.Instance;
			if (instance != null)
			{
				instance.Log.LogWarning((object)("Pause menu: no XRays entry - " + reason));
			}
		}
		return null;
	}
}
internal enum PickKind
{
	None,
	Terrain,
	Water,
	Object
}
internal class PickResult
{
	public PickKind Kind;

	public Category Category;

	public GameObject Root;

	public string Name;

	public bool Hidden;

	public Vector3 Position;

	public float Distance;

	public Type MatchedType;

	public string MatchedName;

	public string MatchedRoot;

	public string HitPath;
}
internal static class PickService
{
	public static PickResult Pick(float maxDistance, VisibilityManager visibility)
	{
		//IL_0015: Unknown result type (might be due to invalid IL or missing references)
		//IL_001a: Unknown result type (might be due to invalid IL or missing references)
		//IL_001f: Unknown result type (might be due to invalid IL or missing references)
		//IL_006f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0074: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f9: Unknown result type (might be due to invalid IL or missing references)
		//IL_0111: Unknown result type (might be due to invalid IL or missing references)
		//IL_010c: Unknown result type (might be due to invalid IL or missing references)
		Camera camera = GetCamera();
		if ((Object)(object)camera == (Object)null || visibility == null)
		{
			return null;
		}
		RaycastHit[] array = Physics.RaycastAll(camera.ScreenPointToRay(Vector2.op_Implicit(GetPointerPosition())), maxDistance, -1, (QueryTriggerInteraction)1);
		if (array.Length == 0)
		{
			return null;
		}
		Array.Sort(array, (RaycastHit a, RaycastHit b) => ((RaycastHit)(ref a)).distance.CompareTo(((RaycastHit)(ref b)).distance));
		Player localPlayer = Player.m_localPlayer;
		PickResult pickResult = null;
		RaycastHit[] array2 = array;
		for (int num = 0; num < array2.Length; num++)
		{
			RaycastHit val = array2[num];
			Transform val2 = (((Object)(object)((RaycastHit)(ref val)).collider != (Object)null) ? ((Component)((RaycastHit)(ref val)).collider).transform : null);
			if ((Object)(object)val2 == (Object)null || ((Object)(object)localPlayer != (Object)null && val2.IsChildOf(((Component)localPlayer).transform)))
			{
				continue;
			}
			PickResult pickResult2 = Classify(val2);
			if (pickResult2 == null)
			{
				continue;
			}
			pickResult2.Distance = ((RaycastHit)(ref val)).distance;
			pickResult2.Position = ((pickResult2.Kind == PickKind.Object && (Object)(object)pickResult2.Root != (Object)null) ? pickResult2.Root.transform.position : ((RaycastHit)(ref val)).point);
			pickResult2.HitPath = BuildHitPath(val2, pickResult2.Root);
			if (pickResult2.Kind == PickKind.Terrain)
			{
				if (!visibility.TerrainHidden)
				{
					return pickResult2;
				}
				continue;
			}
			if (pickResult2.Kind == PickKind.Water)
			{
				if (!visibility.WaterHidden)
				{
					return pickResult2;
				}
				continue;
			}
			if (visibility.IsRootHidden(pickResult2.Root))
			{
				pickResult2.Hidden = true;
				if (pickResult == null)
				{
					pickResult = pickResult2;
				}
				continue;
			}
			return pickResult2;
		}
		return pickResult;
	}

	public static string Describe(float maxDistance, VisibilityManager visibility)
	{
		PickResult pickResult = Pick(maxDistance, visibility);
		if (pickResult == null)
		{
			return "Under the cursor: none";
		}
		string text = "Under the cursor: " + pickResult.Kind switch
		{
			PickKind.Terrain => "Terrain", 
			PickKind.Water => "Water", 
			_ => pickResult.Name, 
		};
		if (pickResult.Category != null)
		{
			text = text + "  ·  Category: " + pickResult.Category.Label;
		}
		if (pickResult.Hidden || ((Object)(object)pickResult.Root != (Object)null && visibility.IsRootHidden(pickResult.Root)))
		{
			text += "  ·  hidden";
		}
		return text + "\n" + TypesLine(pickResult) + "\n" + HitLine(pickResult) + "\n" + Details(pickResult);
	}

	private static string HitLine(PickResult pick)
	{
		string text = pick.HitPath;
		if (string.IsNullOrEmpty(text))
		{
			text = (((Object)(object)pick.Root != (Object)null) ? ((Object)pick.Root).name : pick.Name);
		}
		return "Hit: " + text;
	}

	private static string TypesLine(PickResult pick)
	{
		if (pick.Kind == PickKind.Terrain)
		{
			return "Game type: Heightmap";
		}
		if (pick.Kind == PickKind.Water)
		{
			return "Game type: WaterVolume";
		}
		Category category = pick.Category;
		if (category == null)
		{
			return "Game type: none";
		}
		if (category.IsNameCategory)
		{
			return "Game type: none  ·  category created by name";
		}
		string text = "none";
		if (category.Types != null && category.Types.Length != 0)
		{
			string[] array = new string[category.Types.Length];
			for (int i = 0; i < category.Types.Length; i++)
			{
				array[i] = category.Types[i].Name;
			}
			text = string.Join(", ", array);
		}
		if (category.HasNameMatch)
		{
			text += " + names";
		}
		string text2 = ((pick.MatchedType != null) ? pick.MatchedType.Name : ((pick.MatchedName != null) ? ("name (\"" + pick.MatchedName + "\")") : ((pick.MatchedRoot == null) ? "?" : pick.MatchedRoot)));
		return "Game types: " + text + "  ·  match: " + text2;
	}

	private static string Details(PickResult pick)
	{
		//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_0018: Unknown result type (might be due to invalid IL or missing references)
		//IL_002e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0044: Unknown result type (might be due to invalid IL or missing references)
		Vector3 position = pick.Position;
		return "Pos (" + Format(position.x) + ", " + Format(position.y) + ", " + Format(position.z) + ")  ·  " + Format(pick.Distance) + " m";
	}

	private static string Format(float value)
	{
		return value.ToString("0.0", CultureInfo.InvariantCulture);
	}

	private static PickResult Classify(Transform start)
	{
		Transform val = start;
		while ((Object)(object)val != (Object)null)
		{
			if ((Object)(object)((Component)val).GetComponent<ClutterSystem>() != (Object)null || (Object)(object)((Component)val).GetComponent<EnvMan>() != (Object)null)
			{
				return null;
			}
			if ((Object)(object)((Component)val).GetComponent<Heightmap>() != (Object)null)
			{
				return new PickResult
				{
					Kind = PickKind.Terrain,
					Root = ((Component)val).gameObject,
					Name = "Terrain",
					MatchedType = typeof(Heightmap)
				};
			}
			if ((Object)(object)((Component)val).GetComponent<WaterVolume>() != (Object)null || IsWaterSurface(val))
			{
				return new PickResult
				{
					Kind = PickKind.Water,
					Root = ((Component)val).gameObject,
					Name = "Water",
					MatchedType = typeof(WaterVolume)
				};
			}
			Category[] builtIn = Categories.BuiltIn;
			foreach (Category category in builtIn)
			{
				if (category.Types == null || category.MatchesExclude(((Object)val).name))
				{
					continue;
				}
				Type[] types = category.Types;
				foreach (Type type in types)
				{
					if ((Object)(object)((Component)val).GetComponent(type) != (Object)null)
					{
						return new PickResult
						{
							Kind = PickKind.Object,
							Category = category,
							Root = ((Component)val).gameObject,
							Name = ((Object)val).name,
							MatchedType = type
						};
					}
				}
			}
			Transform val2 = Categories.GrassItem(val);
			if ((Object)(object)val2 != (Object)null)
			{
				return GrassResult(val2);
			}
			if ((Object)(object)((Component)val).GetComponent<ZNetView>() != (Object)null || (Object)(object)((Component)val).GetComponent<LODGroup>() != (Object)null)
			{
				return ObjectResult(val);
			}
			if ((Object)(object)val.parent == (Object)null)
			{
				return ObjectResult(val);
			}
			val = val.parent;
		}
		return null;
	}

	private static bool IsWaterSurface(Transform current)
	{
		if ((Object)(object)((Component)current).GetComponent<Water>() != (Object)null)
		{
			return true;
		}
		return string.Equals(((Object)current).name, "WaterSurface", StringComparison.OrdinalIgnoreCase);
	}

	private static PickResult GrassResult(Transform item)
	{
		Categories.TryGet("clutter", out var category);
		PickResult pickResult = new PickResult
		{
			Kind = PickKind.Object,
			Category = category,
			Root = ((Component)item).gameObject,
			Name = ((Object)item).name
		};
		if ((Object)(object)((Component)item).GetComponent<InstanceRenderer>() != (Object)null)
		{
			pickResult.MatchedType = typeof(InstanceRenderer);
		}
		else if (category != null && category.MatchesName(((Object)item).name))
		{
			pickResult.MatchedName = ((Object)item).name;
		}
		else
		{
			pickResult.MatchedRoot = "grass root";
		}
		return pickResult;
	}

	private static PickResult ObjectResult(Transform current)
	{
		Category[] builtIn = Categories.BuiltIn;
		foreach (Category category in builtIn)
		{
			if (category.MatchesName(((Object)current).name))
			{
				return new PickResult
				{
					Kind = PickKind.Object,
					Category = category,
					Root = ((Component)current).gameObject,
					Name = ((Object)current).name,
					MatchedName = ((Object)current).name
				};
			}
		}
		return new PickResult
		{
			Kind = PickKind.Object,
			Category = Category.ForName(((Object)current).name),
			Root = ((Component)current).gameObject,
			Name = ((Object)current).name
		};
	}

	private static string BuildHitPath(Transform hit, GameObject root)
	{
		List<string> list = new List<string>(6);
		Transform val = (((Object)(object)root != (Object)null) ? root.transform : null);
		Transform val2 = hit;
		while ((Object)(object)val2 != (Object)null)
		{
			list.Add(((Object)val2).name);
			if ((Object)(object)val2 == (Object)(object)val || list.Count >= 6)
			{
				break;
			}
			val2 = val2.parent;
		}
		list.Reverse();
		return string.Join("/", list);
	}

	private static Camera GetCamera()
	{
		Camera val = Camera.main;
		if ((Object)(object)val == (Object)null && (Object)(object)GameCamera.instance != (Object)null)
		{
			val = ((Component)GameCamera.instance).GetComponentInChildren<Camera>(true);
		}
		return val;
	}

	private static Vector2 GetPointerPosition()
	{
		//IL_0000: Unknown result type (might be due to invalid IL or missing references)
		//IL_0006: Invalid comparison between Unknown and I4
		//IL_0026: Unknown result type (might be due to invalid IL or missing references)
		//IL_002b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0020: Unknown result type (might be due to invalid IL or missing references)
		if ((int)Cursor.lockState == 1)
		{
			return new Vector2((float)Screen.width * 0.5f, (float)Screen.height * 0.5f);
		}
		return Vector2.op_Implicit(Input.mousePosition);
	}
}
internal static class ReconcileTiming
{
	private class Stat
	{
		public long Count;

		public double Sum;

		public double Min;

		public double Max;

		public void Add(double value)
		{
			if (Count == 0L || value < Min)
			{
				Min = value;
			}
			if (Count == 0L || value > Max)
			{
				Max = value;
			}
			Count++;
			Sum += value;
		}

		public void Reset()
		{
			Count = 0L;
			Sum = 0.0;
			Min = 0.0;
			Max = 0.0;
		}
	}

	public static bool Enabled;

	private const float LogInterval = 1f;

	private static readonly string[] PhaseNames;

	private static readonly Dictionary<string, Stat> Phases;

	private static readonly Stat Total;

	private static readonly Stopwatch Watch;

	private static readonly StringBuilder Line;

	private static double lastMark;

	private static long passes;

	private static float windowTimer;

	static ReconcileTiming()
	{
		PhaseNames = new string[9] { "terrain", "water", "clouds", "mist", "player", "roots", "of which collect", "of which names", "apply" };
		Phases = new Dictionary<string, Stat>(StringComparer.Ordinal);
		Total = new Stat();
		Watch = new Stopwatch();
		Line = new StringBuilder(320);
		string[] phaseNames = PhaseNames;
		foreach (string key in phaseNames)
		{
			Phases[key] = new Stat();
		}
	}

	public static void Begin()
	{
		if (Enabled)
		{
			Watch.Restart();
			lastMark = 0.0;
			passes++;
		}
	}

	public static void Mark(string name)
	{
		if (Enabled && passes != 0L)
		{
			double totalMilliseconds = Watch.Elapsed.TotalMilliseconds;
			double value = totalMilliseconds - lastMark;
			lastMark = totalMilliseconds;
			Total.Add(value);
			Of(name).Add(value);
		}
	}

	public static long Stamp()
	{
		if (!Enabled)
		{
			return 0L;
		}
		return Stopwatch.GetTimestamp();
	}

	public static void AddSince(string name, long stamp)
	{
		if (Enabled && stamp != 0L && passes != 0L)
		{
			double value = (double)(Stopwatch.GetTimestamp() - stamp) * 1000.0 / (double)Stopwatch.Frequency;
			Of(name).Add(value);
		}
	}

	public static void Tick(float deltaTime)
	{
		if (!Enabled)
		{
			if (windowTimer > 0f || passes > 0)
			{
				Reset();
			}
			return;
		}
		windowTimer += deltaTime;
		if (!(windowTimer < 1f))
		{
			if (passes > 0)
			{
				Write(windowTimer);
			}
			Reset();
		}
	}

	private static Stat Of(string name)
	{
		if (!Phases.TryGetValue(name, out var value))
		{
			value = new Stat();
			Phases[name] = value;
		}
		return value;
	}

	private static void Reset()
	{
		windowTimer = 0f;
		passes = 0L;
		lastMark = 0.0;
		Total.Reset();
		string[] phaseNames = PhaseNames;
		foreach (string key in phaseNames)
		{
			Phases[key].Reset();
		}
	}

	private static void Write(float seconds)
	{
		Line.Length = 0;
		Line.Append("Reconcile timing over ");
		Line.Append(seconds.ToString("0.0", CultureInfo.InvariantCulture));
		Line.Append(" s (");
		Line.Append(passes);
		Line.Append((passes == 1) ? " pass), all in ms: " : " passes), all in ms: ");
		Append(Line, "total", Total);
		string[] phaseNames = PhaseNames;
		foreach (string text in phaseNames)
		{
			Stat stat = Phases[text];
			if (stat.Count != 0L)
			{
				Line.Append(" | ");
				Append(Line, text, stat);
			}
		}
		XRays instance = XRays.Instance;
		if (instance != null)
		{
			instance.Log.LogInfo((object)Line.ToString());
		}
	}

	private static void Append(StringBuilder builder, string name, Stat stat)
	{
		builder.Append(name);
		builder.Append(' ');
		builder.Append((stat.Sum / (double)stat.Count).ToString("0.0", CultureInfo.InvariantCulture));
		builder.Append(" (");
		builder.Append(stat.Min.ToString("0.0", CultureInfo.InvariantCulture));
		builder.Append('-');
		builder.Append(stat.Max.ToString("0.0", CultureInfo.InvariantCulture));
		builder.Append(')');
	}
}
internal class Tooltip
{
	private const float ShowDelay = 0.4f;

	private const float HideDelay = 0.15f;

	private const float Gap = 10f;

	private const float Margin = 6f;

	private const float TextWidth = 320f;

	private const float PaddingX = 9f;

	private const float PaddingY = 6f;

	private const int FontSize = 13;

	private readonly RectTransform area;

	private readonly RectTransform box;

	private readonly Text label;

	private GameObject hovered;

	private Func<string> source;

	private float wait;

	private bool shown;

	public Tooltip(RectTransform panel)
	{
		//IL_003f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0045: Expected O, but got Unknown
		//IL_0073: Unknown result type (might be due to invalid IL or missing references)
		//IL_008d: 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_00cc: Unknown result type (might be due to invalid IL or missing references)
		area = panel;
		GameObject val = new GameObject("XRaysTip", new Type[3]
		{
			typeof(RectTransform),
			typeof(CanvasRenderer),
			typeof(Image)
		});
		box = val.GetComponent<RectTransform>();
		((Transform)box).SetParent((Transform)(object)area, false);
		box.anchorMin = new Vector2(0.5f, 0.5f);
		box.anchorMax = new Vector2(0.5f, 0.5f);
		box.pivot = new Vector2(0f, 1f);
		Image component = val.GetComponent<Image>();
		((Graphic)component).color = new Color(0.09f, 0.07f, 0.05f, 0.96f);
		((Graphic)component).raycastTarget = false;
		label = MakeText(val.transform);
		((Graphic)label).raycastTarget = false;
		val.SetActive(false);
	}

	public void Attach(GameObject target, string text)
	{
		if (!string.IsNullOrEmpty(text))
		{
			Attach(target, () => text);
		}
	}

	public void Attach(GameObject target, Func<string> text)
	{
		//IL_0053: Unknown result type (might be due to invalid IL or missing references)
		//IL_0058: Unknown result type (might be due to invalid IL or missing references)
		//IL_005a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0060: Expected O, but got Unknown
		//IL_0083: Unknown result type (might be due to invalid IL or missing references)
		//IL_0088: Unknown result type (might be due to invalid IL or missing references)
		//IL_008a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0090: Expected O, but got Unknown
		if (!((Object)(object)target == (Object)null) && text != null)
		{
			EventTrigger val = target.GetComponent<EventTrigger>();
			if ((Object)(object)val == (Object)null)
			{
				val = target.AddComponent<EventTrigger>();
			}
			Entry val2 = new Entry
			{
				eventID = (EventTriggerType)0
			};
			((UnityEvent<BaseEventData>)(object)val2.callback).AddListener((UnityAction<BaseEventData>)delegate
			{
				Hover(target, text);
			});
			val.triggers.Add(val2);
			Entry val3 = new Entry
			{
				eventID = (EventTriggerType)1
			};
			((UnityEvent<BaseEventData>)(object)val3.callback).AddListener((UnityAction<BaseEventData>)delegate
			{
				Leave(target);
			});
			val.triggers.Add(val3);
		}
	}

	public void Reset()
	{
		hovered = null;
		source = null;
		wait = 0f;
		Hide();
	}

	public void Tick(float deltaTime)
	{
		string text = (((Object)(object)hovered != (Object)null && source != null) ? source() : null);
		if (string.IsNullOrEmpty(text))
		{
			if (shown)
			{
				wait += deltaTime;
				if (wait >= 0.15f)
				{
					Hide();
				}
			}
			return;
		}
		wait += deltaTime;
		if (!shown)
		{
			if (wait < 0.4f)
			{
				return;
			}
			Show();
		}
		SetText(text);
		Place();
	}

	private void Hover(GameObject target, Func<string> text)
	{
		hovered = target;
		source = text;
		wait = 0f;
	}

	private void Leave(GameObject target)
	{
		if (!((Object)(object)hovered != (Object)(object)target))
		{
			hovered = null;
			source = null;
			wait = 0f;
		}
	}

	private void Show()
	{
		shown = true;
		((Transform)box).SetAsLastSibling();
		((Component)box).gameObject.SetActive(true);
	}

	private void Hide()
	{
		shown = false;
		((Component)box).gameObject.SetActive(false);
		label.text = string.Empty;
	}

	private void SetText(string text)
	{
		//IL_006b: Unknown result type (might be due to invalid IL or missing references)
		if (!(text == label.text))
		{
			label.text = text;
			((Graphic)label).rectTransform.SetSizeWithCurrentAnchors((Axis)0, 320f);
			float num = Mathf.Ceil(label.preferredHeight);
			((Graphic)label).rectTransform.SetSizeWithCurrentAnchors((Axis)1, num);
			box.sizeDelta = new Vector2(338f, num + 12f);
		}
	}

	private void Place()
	{
		//IL_0006: Unknown result type (might be due to invalid IL or missing references)
		//IL_000b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0021: 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_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_0049: Unknown result type (might be due to invalid IL or missing references)
		//IL_007a: Unknown result type (might be due to invalid IL or missing references)
		//IL_006a: Unknown result type (might be due to invalid IL or missing references)
		//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
		//IL_010c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0117: Unknown result type (might be due to invalid IL or missing references)
		//IL_009b: Unknown result type (might be due to invalid IL or missing references)
		Vector2 val = default(Vector2);
		if (RectTransformUtility.ScreenPointToLocalPointInRectangle(area, Vector2.op_Implicit(Input.mousePosition), (Camera)null, ref val))
		{
			Rect rect = area.rect;
			float x = box.sizeDelta.x;
			float y = box.sizeDelta.y;
			float num = val.x + 10f;
			if (num + x > ((Rect)(ref rect)).xMax - 6f)
			{
				num = val.x - 10f - x;
			}
			float num2 = val.y - 10f;
			if (num2 - y < ((Rect)(ref rect)).yMin + 6f)
			{
				num2 = val.y + 10f;
			}
			num = Mathf.Clamp(num, ((Rect)(ref rect)).xMin + 6f, ((Rect)(ref rect)).xMax - 6f - x);
			num2 = Mathf.Clamp(num2, ((Rect)(ref rect)).yMin + 6f + y, ((Rect)(ref rect)).yMax - 6f);
			box.anchoredPosition = new Vector2(num - ((Rect)(ref rect)).center.x, num2 - ((Rect)(ref rect)).center.y);
		}
	}

	private Text MakeText(Transform parent)
	{
		//IL_0015: Unknown result type (might be due to invalid IL or missing references)
		//IL_0024: Unknown result type (might be due to invalid IL or missing references)
		//IL_0029: 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_0066: Unknown result type (might be due to invalid IL or missing references)
		//IL_007a: Unknown result type (might be due to invalid IL or missing references)
		GameObject obj = GUIManager.Instance.CreateText(string.Empty, parent, new Vector2(0f, 1f), new Vector2(0f, 1f), Vector2.zero, GUIManager.Instance.AveriaSerif, 13, Panel.BodyColor, true, Color.black, 320f, 19f, false);
		RectTransform component = obj.GetComponent<RectTransform>();
		component.pivot = new Vector2(0f, 1f);
		component.anchoredPosition = new Vector2(9f, -6f);
		Text component2 = obj.GetComponent<Text>();
		component2.alignment = (TextAnchor)3;
		component2.verticalOverflow = (VerticalWrapMode)1;
		component2.horizontalOverflow = (HorizontalWrapMode)0;
		return component2;
	}
}
internal class VisibilityManager
{
	private struct RendererState
	{
		public bool Enabled;

		public bool ForceOff;

		public RendererState(bool enabled, bool forceOff)
		{
			Enabled = enabled;
			ForceOff = forceOff;
		}
	}

	private class CachedRoot
	{
		public GameObject Root;

		public Renderer[] Renderers;

		public InstanceRenderer[] Instances;

		public int NextPass;

		public void Collect(GameObject go, int nextPass)
		{
			Root = go;
			Renderers = go.GetComponentsInChildren<Renderer>(true);
			Instances = go.GetComponentsInChildren<InstanceRenderer>(true);
			NextPass = nextPass;
		}
	}

	public const float DefaultUpdateInterval = 2f;

	public float UpdateInterval = 2f;

	private const int RendererBuckets = 10;

	private readonly Dictionary<int, Renderer> hiddenRenderers = new Dictionary<int, Renderer>();

	private readonly Dictionary<int, RendererState> rendererStates = new Dictionary<int, RendererState>();

	private readonly Dictionary<int, InstanceRenderer> hiddenInstances = new Dictionary<int, InstanceRenderer>();

	private readonly Dictionary<int, bool> instanceStates = new Dictionary<int, bool>();

	private readonly Dictionary<int, CachedRoot> rendererCache = new Dictionary<int, CachedRoot>();

	private readonly List<int> deadRoots = new List<int>();

	private int passCounter;

	private int staggerCounter;

	private readonly Dictionary<int, GameObject> individualObjects = new Dictionary<int, GameObject>();

	private readonly HashSet<string> hiddenCategories = new HashSet<string>();

	private readonly HashSet<int> hiddenRoots = new HashSet<int>();

	private float refreshTimer;

	private CanvasGroup enemyHudGroup;

	private float enemyHudAlpha = 1f;

	private bool enemyHudRaycasts = true;

	private bool fogTaken;

	private bool fogOriginal = true;

	public const int WorldOptionCount = 6;

	private bool[] revealedFrom;

	private const float NotVisibleThreshold = 1000f;

	public bool TerrainHidden { get; private set; }

	public bool WaterHidden { get; private set; }

	public bool CloudsHidden { get; private set; }

	public bool MistHidden { get; private set; }

	public bool PlayerHidden { get; private set; }

	public bool AnyActive
	{
		get
		{
			if (!TerrainHidden && !WaterHidden && !CloudsHidden && !MistHidden && !PlayerHidden && hiddenCategories.Count <= 0)
			{
				return individualObjects.Count > 0;
			}
			return true;
		}
	}

	public int HiddenRootCount => hiddenRoots.Count;

	public int IndividualCount => individualObjects.Count;

	public int OffListCount => individualObjects.Count + HiddenNameCategoryCount;

	public int HiddenNameCategoryCount
	{
		get
		{
			int num = 0;
			foreach (string hiddenCategory in hiddenCategories)
			{
				if (hiddenCategory.StartsWith("name:"))
				{
					num++;
				}
			}
			return num;
		}
	}

	public bool HudHidden { get; private set; }

	public bool IsRevealing => revealedFrom != null;

	public bool AnyOptionChecked
	{
		get
		{
			if (TerrainHidden || WaterHidden || CloudsHidden || MistHidden || PlayerHidden || HudHidden)
			{
				return true;
			}
			Category[] builtIn = Categories.BuiltIn;
			foreach (Category category in builtIn)
			{
				if (hiddenCategories.Contains(category.Key))
				{
					return true;
				}
			}
			return false;
		}
	}

	public void SetTerrain(bool hidden)
	{
		if (TerrainHidden != hidden)
		{
			TerrainHidden = hidden;
			Reconcile();
		}
	}

	public void ToggleTerrain()
	{
		SetTerrain(!TerrainHidden);
	}

	public void SetWater(bool hidden)
	{
		if (WaterHidden != hidden)
		{
			WaterHidden = hidden;
			Reconcile();
		}
	}

	public void ToggleWater()
	{
		SetWater(!WaterHidden);
	}

	public void SetClouds(bool hidden)
	{
		if (CloudsHidden != hidden)
		{
			CloudsHidden = hidden;
			Reconcile();
		}
	}

	public void ToggleClouds()
	{
		SetClouds(!CloudsHidden);
	}

	public void SetMist(bool hidden)
	{
		if (MistHidden != hidden)
		{
			MistHidden = hidden;
			Reconcile();
		}
	}

	public void ToggleMist()
	{
		SetMist(!MistHidden);
	}

	public void SetPlayer(bool hidden)
	{
		if (PlayerHidden != hidden)
		{
			PlayerHidden = hidden;
			Reconcile();
		}
	}

	public void TogglePlayer()
	{
		SetPlayer(!PlayerHidden);
	}

	public void SetHud(bool hidden)
	{
		if (HudHidden != hidden)
		{
			HudHidden = hidden;
			ApplyHud();
		}
	}

	public void ToggleHud()
	{
		SetHud(!HudHidden);
	}

	public bool IsCategoryHidden(string key)
	{
		if (!string.IsNullOrEmpty(key))
		{
			return hiddenCategories.Contains(key);
		}
		return false;
	}

	public int SetCategory(Category category, bool hidden)
	{
		if (category == null)
		{
			return 0;
		}
		Categories.Register(category);
		Categories.Invalidate(category.Key);
		int count = Categories.CollectRoots(category).Count;
		if (hidden)
		{
			hiddenCategories.Add(category.Key);
		}
		else
		{
			hiddenCategories.Remove(category.Key);
		}
		Reconcile();
		return count;
	}

	public bool ToggleObject(GameObject go)
	{
		if ((Object)(object)go == (Object)null)
		{
			return false;
		}
		int instanceID = ((Object)go).GetInstanceID();
		bool num = !individualObjects.ContainsKey(instanceID);
		if (num)
		{
			individualObjects[instanceID] = go;
		}
		else
		{
			individualObjects.Remove(instanceID);
		}
		Reconcile();
		return num;
	}

	public bool IsObjectHidden(GameObject go)
	{
		Transform val = (((Object)(object)go != (Object)null) ? go.transform : null);
		while ((Object)(object)val != (Object)null)
		{
			if (individualObjects.ContainsKey(((Object)((Component)val).gameObject).GetInstanceID()))
			{
				return true;
			}
			val = val.parent;
		}
		return false;
	}

	public bool IsRootHidden(GameObject go)
	{
		Transform val = (((Object)(object)go != (Object)null) ? go.transform : null);
		while ((Object)(object)val != (Object)null)
		{
			if (hiddenRoots.Contains(((Object)((Component)val).gameObject).GetInstanceID()))
			{
				return true;
			}
			val = val.parent;
		}
		return false;
	}

	public void RestoreAll()
	{
		TerrainHidden = false;
		WaterHidden = false;
		CloudsHidden = false;
		MistHidden = false;
		PlayerHidden = false;
		hiddenCategories.Clear();
		individualObjects.Clear();
		revealedFrom = null;
		SetHud(hidden: false);
		Reconcile();
	}

	public void HideAll()
	{
		TerrainHidden = true;
		WaterHidden = true;
		CloudsHidden = true;
		MistHidden = true;
		PlayerHidden = true;
		foreach (Category item in Categories.All)
		{
			hiddenCategories.Add(item.Key);
		}
		revealedFrom = null;
		SetHud(hidden: true);
		Reconcile();
	}

	public void ToggleChecked()
	{
		bool[] array = CaptureOptions();
		if (revealedFrom == null)
		{
			bool anyOptionChecked = AnyOptionChecked;
			revealedFrom = array;
			ApplyOptions(Filled(array, !anyOptionChecked));
			return;
		}
		bool[] array2 = (bool[])revealedFrom.Clone();
		for (int i = 0; i < array2.Length; i++)
		{
			array2[i] = array2[i] || array[i];
		}
		revealedFrom = null;
		ApplyOptions(array2);
	}

	public bool[] CaptureOptions()
	{
		bool[] array = new bool[6 + Categories.BuiltIn.Length];
		array[0] = TerrainHidden;
		array[1] = WaterHidden;
		array[2] = CloudsHidden;
		array[3] = MistHidden;
		array[4] = PlayerHidden;
		array[5] = HudHidden;
		for (int i = 0; i < Categories.BuiltIn.Length; i++)
		{
			array[6 + i] = IsCategoryHidden(Categories.BuiltIn[i].Key);
		}
		return array;
	}

	public void ApplyOptions(bool[] state)
	{
		TerrainHidden = state[0];
		WaterHidden = state[1];
		CloudsHidden = state[2];
		MistHidden = state[3];
		PlayerHidden = state[4];
		if (HudHidden != state[5])
		{
			HudHidden = state[5];
			ApplyHud();
		}
		for (int i = 0; i < Categories.BuiltIn.Length; i++)
		{
			string key = Categories.BuiltIn[i].Key;
			if (state[6 + i])
			{
				hiddenCategories.Add(key);
			}
			else
			{
				hiddenCategories.Remove(key);
			}
		}
		Reconcile();
	}

	private static bool[] Filled(bool[] from, bool value)
	{
		bool[] array = new bool[from.Length];
		for (int i = 0; i < array.Length; i++)
		{
			array[i] = value;
		}
		return array;
	}

	public void Tick(float deltaTime)
	{
		ReconcileTiming.Tick(deltaTime);
		ApplyEnemyHud();
		if (HudHidden)
		{
			ApplyHud();
		}
		if (AnyActive)
		{
			refreshTimer += deltaTime;
			if (!(refreshTimer < UpdateInterval))
			{
				refreshTimer = 0f;
				Reconcile();
			}
		}
	}

	public void Reconcile()
	{
		ReconcileTiming.Begin();
		passCounter++;
		HashSet<int> excluded = CollectWaterSurfaceIds();
		Dictionary<int, Renderer> dictionary = new Dictionary<int, Renderer>();
		Dictionary<int, InstanceRenderer> wantedInstances = new Dictionary<int, InstanceRenderer>();
		HashSet<int> hashSet = new HashSet<int>();
		if (TerrainHidden)
		{
			foreach (Heightmap item in CollectHeightmaps())
			{
				AddRenderers(((Object)(object)item != (Object)null) ? ((Component)item).gameObject : null, dictionary, wantedInstances, excluded);
			}
			TerrainLod[] array = Object.FindObjectsByType<TerrainLod>((FindObjectsSortMode)0);
			foreach (TerrainLod val in array)
			{
				AddRenderers(((Object)(object)val != (Object)null) ? ((Component)val).gameObject : null, dictionary, wantedInstances, excluded);
			}
			ReconcileTiming.Mark("terrain");
		}
		if (WaterHidden)
		{
			foreach (WaterVolume item2 in CollectWaterVolumes())
			{
				if ((Object)(object)item2 != (Object)null && (Object)(object)item2.m_waterSurface != (Object)null)
				{
					dictionary[((Object)item2.m_waterSurface).GetInstanceID()] = (Renderer)(object)item2.m_waterSurface;
				}
			}
			foreach (GameObject item3 in CollectOceanRoots())
			{
				AddRenderers(item3, dictionary, wantedInstances, excluded);
			}
			ReconcileTiming.Mark("water");
		}
		if (CloudsHidden)
		{
			foreach (GameObject item4 in CollectCloudRoots())
			{
				AddRenderers(item4, dictionary, wantedInstances, null);
			}
			EnvMan instance = EnvMan.instance;
			if ((Object)(object)instance != (Object)null)
			{
				AddWanted((Renderer)(object)instance.m_clouds, dictionary);
				AddWanted((Renderer)(object)instance.m_rainClouds, dictionary);
				AddWanted((Renderer)(object)instance.m_rainCloudsDownside, dictionary);
			}
			ReconcileTiming.Mark("clouds");
		}
		if (MistHidden)
		{
			foreach (GameObject item5 in CollectMistRoots())
			{
				AddRenderers(item5, dictionary, wantedInstances, null);
			}
			MistEmitter[] array2 = Object.FindObjectsByType<MistEmitter>((FindObjectsSortMode)0);
			foreach (MistEmitter val2 in array2)
			{
				AddRenderers(((Component)val2).gameObject, dictionary, wantedInstances, null);
				if ((Object)(object)val2.m_psystem != (Object)null)
				{
					AddWanted(((Component)val2.m_psystem).GetComponent<Renderer>(), dictionary);
				}
			}
			ParticleMist[] array3 = Object.FindObjectsByType<ParticleMist>((FindObjectsSortMode)0);
			foreach (ParticleMist val3 in array3)
			{
				AddRenderers(((Component)val3).gameObject, dictionary, wantedInstances, null);
			}
			ReconcileTiming.Mark("mist");
		}
		if (PlayerHidden)
		{
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer != (Object)null)
			{
				AddRenderers(((Component)localPlayer).gameObject, dictionary, wantedInstances, excluded, fresh: true);
			}
			ReconcileTiming.Mark("player");
		}
		foreach (string hiddenCategory in hiddenCategories)
		{
			if (!Categories.TryGet(hiddenCategory, out var category))
			{
				continue;
			}
			foreach (GameObject item6 in Categories.CollectRoots(category))
			{
				if (!((Object)(object)item6 == (Object)null))
				{
					hashSet.Add(((Object)item6).GetInstanceID());
					AddRenderers(item6, dictionary, wantedInstances, excluded);
				}
			}
		}
		foreach (KeyValuePair<int, GameObject> individualObject in individualObjects)
		{
			GameObject value = individualObject.Value;
			if (!((Object)(object)value == (Object)null))
			{
				hashSet.Add(individualObject.Key);
				AddRenderers(value, dictionary, wantedInstances, excluded);
			}
		}
		hiddenRoots.Clear();
		hiddenRoots.UnionWith(hashSet);
		PurgeRendererCache();
		ReconcileTiming.Mark("roots");
		Apply(dictionary, wantedInstances);
		ApplyEnemyHud();
		ApplyFog();
		ReconcileTiming.Mark("apply");
	}

	private void ApplyHud()
	{
		//IL_0064: Unknown result type (might be due to invalid IL or missing references)
		//IL_007b: Unknown result type (might be due to invalid IL or missing references)
		Hud instance = Hud.instance;
		GameObject val = (((Object)(object)instance != (Object)null) ? instance.m_rootObject : null);
		if ((Object)(object)val == (Object)null)
		{
			return;
		}
		if (HudHidden)
		{
			if (val.activeSelf)
			{
				val.SetActive(false);
			}
			KeepPauseMenuOnScreen();
			return;
		}
		if (instance.m_userHidden)
		{
			instance.m_userHidden = false;
		}
		if (!val.activeSelf)
		{
			val.SetActive(true);
		}
		if (val.transform.localPosition.x >= 1000f)
		{
			val.transform.localPosition = Vector3.zero;
		}
	}

	private void ApplyEnemyHud()
	{
		bool flag = HudHidden || IsCategoryHidden("creatures");
		CanvasGroup val = enemyHudGroup;
		if (!flag && (Object)(object)val == (Object)null)
		{
			return;
		}
		EnemyHud instance = EnemyHud.instance;
		GameObject val2 = (((Object)(object)instance != (Object)null) ? instance.m_hudRoot : null);
		if ((Object)(object)val2 == (Object)null)
		{
			return;
		}
		if ((Object)(object)val == (Object)null)
		{
			enemyHudGroup = null;
		}
		else if ((Object)(object)((Component)val).gameObject != (Object)(object)val2)
		{
			enemyHudGroup = null;
			val = null;
		}
		if (!flag)
		{
			if ((Object)(object)val != (Object)null)
			{
				val.alpha = enemyHudAlpha;
				val.blocksRaycasts = enemyHudRaycasts;
				enemyHudGroup = null;
			}
			return;
		}
		if ((Object)(object)val == (Object)null)
		{
			val = val2.GetComponent<CanvasGroup>();
			if ((Object)(object)val == (Object)null)
			{
				enemyHudAlpha = 1f;
				enemyHudRaycasts = true;
				val = val2.AddComponent<CanvasGroup>();
			}
			else
			{
				enemyHudAlpha = val.alpha;
				enemyHudRaycasts = val.blocksRaycasts;
			}
			enemyHudGroup = val;
		}
		if (val.alpha != 0f)
		{
			val.alpha = 0f;
		}
		if (val.blocksRaycasts)
		{
			val.blocksRaycasts = false;
		}
	}

	private static void KeepPauseMenuOnScreen()
	{
		//IL_0024: Unknown result type (might be due to invalid IL or missing references)
		//IL_003b: Unknown result type (might be due to invalid IL or missing references)
		Menu instance = Menu.instance;
		if (!((Object)(object)instance == (Object)null) && !((Object)(object)instance.m_root == (Object)null) && instance.m_root.localPosition.x >= 1000f)
		{
			instance.m_root.localPosition = Vector3.zero;
		}
	}

	private static IEnumerable<Heightmap> CollectHeightmaps()
	{
		List<IMonoUpdater> instances = Heightmap.Instances;
		if (instances != null && instances.Count > 0)
		{
			List<Heightmap> list = new List<Heightmap>(instances.Count);
			foreach (IMonoUpdater item in instances)
			{
				Heightmap val = (Heightmap)(object)((item is Heightmap) ? item : null);
				if ((Object)(object)val != (Object)null)
				{
					list.Add(val);
				}
			}
			if (list.Count > 0)
			{
				return list;
			}
		}
		return Object.FindObjectsByType<Heightmap>((FindObjectsSortMode)0);
	}

	private static IEnumerable<WaterVolume> CollectWaterVolumes()
	{
		List<WaterVolume> instances = WaterVolume.Instances;
		if (instances != null && instances.Count > 0)
		{
			return instances;
		}
		return Object.FindObjectsByType<WaterVolume>((FindObjectsSortMode)0);
	}

	private static HashSet<int> CollectWaterSurfaceIds()
	{
		HashSet<int> hashSet = new HashSet<int>();
		foreach (WaterVolume item in CollectWaterVolumes())
		{
			if ((Object)(object)item != (Object)null && (Object)(object)item.m_waterSurface != (Object)null)
			{
				hashSet.Add(((Object)item.m_waterSurface).GetInstanceID());
			}
		}
		return hashSet;
	}

	priva