Decompiled source of MuddyScrapPileHighlight v0.1.0

plugins/MuddyScrapPileHighlight.dll

Decompiled 2 weeks ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security.Permissions;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using Jotunn.Entities;
using Jotunn.Managers;
using UnityEngine;
using UnityEngine.Rendering;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("MuddyScrapPileHighlight")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("MuddyScrapPileHighlight")]
[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 MuddyScrapPileHighlight;

internal class ModConfig
{
	internal readonly ConfigEntry<bool> Enabled;

	internal readonly ConfigEntry<KeyboardShortcut> ToggleKey;

	internal readonly ConfigEntry<bool> OnlyChunks;

	internal readonly ConfigEntry<float> MaxDistance;

	internal readonly ConfigEntry<float> ScanInterval;

	internal readonly ConfigEntry<bool> Shell;

	internal readonly ConfigEntry<Color> ShellColor;

	internal readonly ConfigEntry<float> ShellWidth;

	internal readonly ConfigEntry<bool> HideCryptWater;

	internal readonly ConfigEntry<bool> Marker;

	internal readonly ConfigEntry<Color> MarkerColor;

	internal readonly ConfigEntry<float> MarkerSize;

	internal readonly ConfigEntry<bool> MarkerDistance;

	internal readonly ConfigEntry<bool> DebugLog;

	internal ModConfig(ConfigFile file)
	{
		//IL_001e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0028: Expected O, but got Unknown
		//IL_003f: Unknown result type (might be due to invalid IL or missing references)
		//IL_004f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0059: Expected O, but got Unknown
		//IL_0076: Unknown result type (might be due to invalid IL or missing references)
		//IL_0080: Expected O, but got Unknown
		//IL_00af: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b9: Expected O, but got Unknown
		//IL_00e8: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f2: Expected O, but got Unknown
		//IL_010f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0119: Expected O, but got Unknown
		//IL_0137: Unknown result type (might be due to invalid IL or missing references)
		//IL_013c: Unknown result type (might be due to invalid IL or missing references)
		//IL_014c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0156: Expected O, but got Unknown
		//IL_0185: Unknown result type (might be due to invalid IL or missing references)
		//IL_018f: Expected O, but got Unknown
		//IL_01ac: Unknown result type (might be due to invalid IL or missing references)
		//IL_01b6: Expected O, but got Unknown
		//IL_01d3: Unknown result type (might be due to invalid IL or missing references)
		//IL_01dd: Expected O, but got Unknown
		//IL_01fe: Unknown result type (might be due to invalid IL or missing references)
		//IL_0203: Unknown result type (might be due to invalid IL or missing references)
		//IL_0213: Unknown result type (might be due to invalid IL or missing references)
		//IL_021d: Expected O, but got Unknown
		//IL_024c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0256: Expected O, but got Unknown
		//IL_0273: Unknown result type (might be due to invalid IL or missing references)
		//IL_027d: Expected O, but got Unknown
		//IL_029a: Unknown result type (might be due to invalid IL or missing references)
		//IL_02a4: Expected O, but got Unknown
		Enabled = file.Bind<bool>("General", "Enabled", true, new ConfigDescription("Whether the highlight is on at startup. The toggle key switches it at runtime and writes this value.", (AcceptableValueBase)null, Array.Empty<object>()));
		ToggleKey = file.Bind<KeyboardShortcut>("General", "ToggleKey", new KeyboardShortcut((KeyCode)0, Array.Empty<KeyCode>()), new ConfigDescription("Key that toggles the highlight on and off. Not bound by default - set a chord to enable the toggle.", (AcceptableValueBase)null, Array.Empty<object>()));
		OnlyChunks = file.Bind<bool>("General", "OnlyChunks", true, new ConfigDescription("Highlight only the fracturable chunks (mudpile_frac / mudpile2_frac) - the fragments a pickaxe blow leaves behind - and leave intact piles dark. Off, an intact pile is highlighted instead whenever no chunk exists in range.", (AcceptableValueBase)null, Array.Empty<object>()));
		MaxDistance = file.Bind<float>("General", "MaxDistance", 5f, new ConfigDescription("How far from the player piles are tracked and highlighted, in metres. Piles beyond it lose their shell.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 10f), Array.Empty<object>()));
		ScanInterval = file.Bind<float>("General", "ScanInterval", 1f, new ConfigDescription("Seconds between two scans of the loaded zones for piles.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.2f, 10f), Array.Empty<object>()));
		Shell = file.Bind<bool>("Highlight", "Shell", true, new ConfigDescription("Draw the glowing shell over the piles.", (AcceptableValueBase)null, Array.Empty<object>()));
		ShellColor = file.Bind<Color>("Highlight", "ShellColor", Color32.op_Implicit(new Color32(byte.MaxValue, (byte)191, (byte)0, (byte)16)), new ConfigDescription("Shell colour, alpha included.", (AcceptableValueBase)null, Array.Empty<object>()));
		ShellWidth = file.Bind<float>("Highlight", "ShellWidth", 0.03f, new ConfigDescription("How far the shell floats over the pile surface, in metres.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.005f, 0.2f), Array.Empty<object>()));
		HideCryptWater = file.Bind<bool>("Highlight", "HideCryptWater", true, new ConfigDescription("Hide the green water of Sunken Crypts so the shells show through it. Off, the crypt water is drawn as usual and hides underwater shells (its shader cannot be drawn over).", (AcceptableValueBase)null, Array.Empty<object>()));
		Marker = file.Bind<bool>("Highlight", "Marker", false, new ConfigDescription("Draw an on-screen marker over every tracked fragment, on top of everything: walls, terrain and darkness cannot hide it. Fragments outside the view get an arrow at the screen edge instead.", (AcceptableValueBase)null, Array.Empty<object>()));
		MarkerColor = file.Bind<Color>("Highlight", "MarkerColor", Color32.op_Implicit(new Color32(byte.MaxValue, (byte)140, (byte)0, byte.MaxValue)), new ConfigDescription("Marker and distance colour, alpha included.", (AcceptableValueBase)null, Array.Empty<object>()));
		MarkerSize = file.Bind<float>("Highlight", "MarkerSize", 12f, new ConfigDescription("Marker size in pixels.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(4f, 40f), Array.Empty<object>()));
		MarkerDistance = file.Bind<bool>("Highlight", "MarkerDistance", true, new ConfigDescription("Show the distance to the fragment next to its marker.", (AcceptableValueBase)null, Array.Empty<object>()));
		DebugLog = file.Bind<bool>("General", "DebugLog", false, new ConfigDescription("Log every scan and every pile found, with its position.", (AcceptableValueBase)null, Array.Empty<object>()));
	}
}
[BepInPlugin("orax.MuddyScrapPileHighlight", "MuddyScrapPileHighlight", "0.1.0")]
[BepInDependency(/*Could not decode attribute arguments.*/)]
internal class MuddyScrapPileHighlight : BaseUnityPlugin
{
	public const string PluginGUID = "orax.MuddyScrapPileHighlight";

	public const string PluginName = "MuddyScrapPileHighlight";

	public const string PluginVersion = "0.1.0";

	public static CustomLocalization Localization = LocalizationManager.Instance.GetLocalization();

	private ModConfig config;

	private PileTracker tracker;

	private ShellHighlighter shell;

	private ScreenMarkers markers;

	private float nextSyncTime;

	private bool inCrypt;

	private float nextCryptCheck;

	private const float CryptCheckInterval = 3f;

	private const string CryptEnvName = "SunkenCrypt";

	private bool wasActive;

	private void Awake()
	{
		//IL_0077: Unknown result type (might be due to invalid IL or missing references)
		//IL_00be: Unknown result type (might be due to invalid IL or missing references)
		config = new ModConfig(((BaseUnityPlugin)this).Config);
		tracker = new PileTracker(config, ((BaseUnityPlugin)this).Logger);
		shell = new ShellHighlighter(config, ((BaseUnityPlugin)this).Logger);
		markers = new ScreenMarkers(config, ((BaseUnityPlugin)this).Logger);
		((BaseUnityPlugin)this).Logger.LogInfo((object)("MuddyScrapPileHighlight 0.1.0 loaded - toggle: " + DescribeShortcut(config.ToggleKey.Value) + ", " + $"chunks: mudpile_frac / mudpile2_frac, distance: {config.MaxDistance.Value:0} m, " + $"shell: {config.ShellColor.Value}"));
	}

	private void Update()
	{
		if (PressedToggle())
		{
			config.Enabled.Value = !config.Enabled.Value;
			((BaseUnityPlugin)this).Logger.LogInfo((object)(config.Enabled.Value ? "MuddyScrapPileHighlight enabled" : "MuddyScrapPileHighlight disabled"));
		}
		if (Time.time >= nextCryptCheck)
		{
			nextCryptCheck = Time.time + 3f;
			inCrypt = IsInSunkenCrypt();
		}
		if (!config.Enabled.Value || !((Object)(object)Player.m_localPlayer != (Object)null) || !inCrypt)
		{
			if (wasActive)
			{
				tracker.Reset();
				shell.Clear();
				nextSyncTime = 0f;
				wasActive = false;
			}
		}
		else
		{
			wasActive = true;
			if (!(Time.time < nextSyncTime))
			{
				nextSyncTime = Time.time + Mathf.Max(0.2f, config.ScanInterval.Value);
				bool changed = tracker.Tick(Time.time);
				shell.Sync(tracker.Chunks, changed);
			}
		}
	}

	private void OnGUI()
	{
		if (config.Enabled.Value && !((Object)(object)Player.m_localPlayer == (Object)null) && inCrypt)
		{
			markers.Draw(tracker.Chunks);
		}
	}

	private void OnDestroy()
	{
		shell?.Clear();
	}

	private bool IsInSunkenCrypt()
	{
		EnvMan instance = EnvMan.instance;
		if ((Object)(object)instance != (Object)null)
		{
			return string.Equals(instance.m_forceEnv, "SunkenCrypt", StringComparison.OrdinalIgnoreCase);
		}
		return false;
	}

	private bool PressedToggle()
	{
		//IL_000b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0010: Unknown result type (might be due to invalid IL or missing references)
		//IL_0013: 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_0043: Unknown result type (might be due to invalid IL or missing references)
		KeyboardShortcut value = config.ToggleKey.Value;
		if ((int)((KeyboardShortcut)(ref value)).MainKey == 0 || InputBlocked())
		{
			return false;
		}
		if (!Input.GetKeyDown(((KeyboardShortcut)(ref value)).MainKey))
		{
			return false;
		}
		foreach (KeyCode modifier in ((KeyboardShortcut)(ref value)).Modifiers)
		{
			if (!Input.GetKey(modifier))
			{
				return false;
			}
		}
		return true;
	}

	private static bool InputBlocked()
	{
		if (Console.IsVisible() || Menu.IsVisible())
		{
			return true;
		}
		if ((Object)(object)Chat.instance != (Object)null && Chat.instance.HasFocus())
		{
			return true;
		}
		Player localPlayer = Player.m_localPlayer;
		if ((Object)(object)localPlayer != (Object)null)
		{
			return ((Character)localPlayer).IsDead();
		}
		return false;
	}

	private static string DescribeShortcut(KeyboardShortcut shortcut)
	{
		//IL_0002: 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_0055: Unknown result type (might be due to invalid IL or missing references)
		//IL_005a: Unknown result type (might be due to invalid IL or missing references)
		if ((int)((KeyboardShortcut)(ref shortcut)).MainKey == 0)
		{
			return "not set";
		}
		List<string> list = new List<string>();
		foreach (KeyCode modifier in ((KeyboardShortcut)(ref shortcut)).Modifiers)
		{
			list.Add(((object)modifier/*cast due to .constrained prefix*/).ToString());
		}
		list.Add(((object)((KeyboardShortcut)(ref shortcut)).MainKey/*cast due to .constrained prefix*/).ToString());
		return string.Join("+", list);
	}
}
internal class PileTracker
{
	private static readonly string[] TargetNames = new string[2] { "mudpile_frac", "mudpile2_frac" };

	private const string PilePrefix = "mudpile";

	private readonly ModConfig config;

	private readonly ManualLogSource log;

	private HashSet<GameObject> chunks = new HashSet<GameObject>();

	private HashSet<GameObject> scanBuffer = new HashSet<GameObject>();

	private readonly List<GameObject> scanList = new List<GameObject>();

	private readonly HashSet<GameObject> seenCandidates = new HashSet<GameObject>();

	private readonly Collider[] nearbyColliders = (Collider[])(object)new Collider[256];

	private readonly List<Transform> subtreeScratch = new List<Transform>();

	private readonly List<GameObject> rootsScratch = new List<GameObject>();

	private readonly List<GameObject> mineRockList = new List<GameObject>();

	private readonly Dictionary<string, int> nameCounts = new Dictionary<string, int>();

	private HashSet<GameObject> previousMineRocks = new HashSet<GameObject>();

	private HashSet<GameObject> newMineRocks = new HashSet<GameObject>();

	private HashSet<GameObject> knownFragments = new HashSet<GameObject>();

	private readonly List<GameObject> staleScratch = new List<GameObject>();

	private List<Vector3> pilePositions = new List<Vector3>();

	private List<Vector3> pilePositionsBuffer = new List<Vector3>();

	private const float SpawnTolerance = 3f;

	private Dictionary<GameObject, string> pileSignatures = new Dictionary<GameObject, string>();

	private readonly StringBuilder signatureScratch = new StringBuilder();

	private float nextScanTime;

	private bool announcedFirstFind;

	private bool hierarchyDumped;

	private bool loggedMissingChunks;

	private bool censusDone;

	private bool fracHunted;

	internal HashSet<GameObject> Chunks => chunks;

	internal PileTracker(ModConfig config, ManualLogSource log)
	{
		this.config = config;
		this.log = log;
	}

	internal bool Tick(float now)
	{
		if (now < nextScanTime)
		{
			return false;
		}
		nextScanTime = now + Mathf.Max(0.2f, config.ScanInterval.Value);
		Player localPlayer = Player.m_localPlayer;
		if ((Object)(object)localPlayer == (Object)null)
		{
			return Reset();
		}
		return Rescan(localPlayer);
	}

	internal bool Reset()
	{
		bool result = chunks.Count > 0;
		chunks.Clear();
		scanBuffer.Clear();
		nextScanTime = 0f;
		announcedFirstFind = false;
		hierarchyDumped = false;
		loggedMissingChunks = false;
		censusDone = false;
		fracHunted = false;
		pileSignatures.Clear();
		previousMineRocks.Clear();
		newMineRocks.Clear();
		knownFragments.Clear();
		staleScratch.Clear();
		pilePositions.Clear();
		pilePositionsBuffer.Clear();
		return result;
	}

	private bool Rescan(Player player)
	{
		//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_0050: Unknown result type (might be due to invalid IL or missing references)
		//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d4: 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_00da: Unknown result type (might be due to invalid IL or missing references)
		//IL_0125: Unknown result type (might be due to invalid IL or missing references)
		//IL_021c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0221: Unknown result type (might be due to invalid IL or missing references)
		//IL_0222: Unknown result type (might be due to invalid IL or missing references)
		//IL_0227: Unknown result type (might be due to invalid IL or missing references)
		//IL_0293: Unknown result type (might be due to invalid IL or missing references)
		Vector3 position = ((Component)player).transform.position;
		float num = config.MaxDistance.Value * config.MaxDistance.Value;
		scanList.Clear();
		seenCandidates.Clear();
		mineRockList.Clear();
		CollectNearby(position, num);
		scanBuffer.Clear();
		rootsScratch.Clear();
		GameObject val = null;
		int num2 = 0;
		if (config.DebugLog.Value)
		{
			PurgeSignatures();
		}
		Vector3 val2;
		foreach (GameObject scan in scanList)
		{
			if ((Object)(object)scan == (Object)null || !((Object)scan).name.StartsWith("mudpile", StringComparison.Ordinal))
			{
				continue;
			}
			val2 = scan.transform.position - position;
			if (((Vector3)(ref val2)).sqrMagnitude > num || !seenCandidates.Add(scan))
			{
				continue;
			}
			num2++;
			if ((Object)(object)val == (Object)null)
			{
				val = scan;
			}
			rootsScratch.Add(scan);
			pilePositionsBuffer.Add(scan.transform.position);
			scan.GetComponentsInChildren<Transform>(true, subtreeScratch);
			if (config.DebugLog.Value)
			{
				WatchSubtree(scan, subtreeScratch);
			}
			foreach (Transform item in subtreeScratch)
			{
				if (IsTarget(((Object)item).name))
				{
					scanBuffer.Add(((Component)item).gameObject);
				}
			}
			subtreeScratch.Clear();
		}
		newMineRocks.Clear();
		PurgeStale(knownFragments);
		foreach (GameObject mineRock in mineRockList)
		{
			if ((Object)(object)mineRock == (Object)null)
			{
				continue;
			}
			val2 = mineRock.transform.position - position;
			if (!(((Vector3)(ref val2)).sqrMagnitude > num))
			{
				bool flag = !previousMineRocks.Contains(mineRock);
				newMineRocks.Add(mineRock);
				if (flag && config.DebugLog.Value)
				{
					log.LogInfo((object)($"New MineRock5 in range: {((Object)mineRock).name} at {mineRock.transform.position} " + "(" + DescribeLineage(mineRock.transform) + ") - " + DescribeComponents(mineRock.transform) + " - m_name='" + mineRock.GetComponent<MineRock5>()?.m_name + "'"));
					DumpHierarchy(mineRock);
				}
				if (knownFragments.Contains(mineRock) || IsFragment(mineRock, flag))
				{
					knownFragments.Add(mineRock);
					scanBuffer.Add(mineRock);
				}
			}
		}
		HashSet<GameObject> hashSet = previousMineRocks;
		previousMineRocks = newMineRocks;
		newMineRocks = hashSet;
		newMineRocks.Clear();
		List<Vector3> list = pilePositions;
		pilePositions = pilePositionsBuffer;
		pilePositionsBuffer = list;
		pilePositionsBuffer.Clear();
		if (scanBuffer.Count == 0 && !config.OnlyChunks.Value)
		{
			foreach (GameObject item2 in rootsScratch)
			{
				if (item2.activeInHierarchy)
				{
					scanBuffer.Add(item2);
				}
			}
		}
		rootsScratch.Clear();
		ReportDiagnostics(val, num2, scanBuffer.Count);
		bool num3 = !scanBuffer.SetEquals(chunks);
		if (num3 && config.DebugLog.Value)
		{
			LogScan(scanBuffer, num2);
		}
		HashSet<GameObject> hashSet2 = chunks;
		chunks = scanBuffer;
		scanBuffer = hashSet2;
		scanBuffer.Clear();
		if (!announcedFirstFind && chunks.Count > 0)
		{
			announcedFirstFind = true;
			log.LogInfo((object)($"Tracking {chunks.Count} Muddy Scrap Pile(s) within " + $"{config.MaxDistance.Value:0} m"));
		}
		return num3;
	}

	private static bool IsTarget(string name)
	{
		string[] targetNames = TargetNames;
		foreach (string text in targetNames)
		{
			if (name.Equals(text, StringComparison.Ordinal))
			{
				return true;
			}
			if (name.Length > text.Length && name.StartsWith(text, StringComparison.Ordinal))
			{
				return true;
			}
		}
		return false;
	}

	private void WatchSubtree(GameObject root, List<Transform> subtree)
	{
		signatureScratch.Length = 0;
		foreach (Transform item in subtree)
		{
			signatureScratch.Append(((Object)item).name).Append('|');
		}
		string text = signatureScratch.ToString();
		if (!pileSignatures.TryGetValue(root, out var value))
		{
			pileSignatures[root] = text;
		}
		else if (!(value == text))
		{
			pileSignatures[root] = text;
			log.LogInfo((object)("Hierarchy of " + ((Object)root).name + " changed:"));
			DumpHierarchy(root);
		}
	}

	private void PurgeSignatures()
	{
		bool flag = false;
		foreach (KeyValuePair<GameObject, string> pileSignature in pileSignatures)
		{
			if ((Object)(object)pileSignature.Key == (Object)null)
			{
				flag = true;
				break;
			}
		}
		if (!flag)
		{
			return;
		}
		Dictionary<GameObject, string> dictionary = new Dictionary<GameObject, string>();
		foreach (KeyValuePair<GameObject, string> pileSignature2 in pileSignatures)
		{
			if ((Object)(object)pileSignature2.Key != (Object)null)
			{
				dictionary[pileSignature2.Key] = pileSignature2.Value;
			}
		}
		pileSignatures = dictionary;
	}

	private bool IsFragment(GameObject mineRock, bool appeared)
	{
		//IL_0082: Unknown result type (might be due to invalid IL or missing references)
		if (IsTarget(((Object)mineRock).name))
		{
			return true;
		}
		int num = 0;
		Transform parent = mineRock.transform.parent;
		while ((Object)(object)parent != (Object)null && num < 8)
		{
			if (IsTarget(((Object)parent).name))
			{
				return true;
			}
			num++;
			parent = parent.parent;
		}
		if (((Object)mineRock).name.StartsWith("mudpile", StringComparison.Ordinal))
		{
			return true;
		}
		if (appeared && HasFracDescendant(mineRock.transform))
		{
			return true;
		}
		if (IsMuddyMineRock(mineRock))
		{
			return true;
		}
		if (appeared)
		{
			return NearLastPile(mineRock.transform.position);
		}
		return false;
	}

	private static bool IsMuddyMineRock(GameObject mineRock)
	{
		string text = mineRock.GetComponent<MineRock5>()?.m_name;
		if (string.IsNullOrEmpty(text))
		{
			return false;
		}
		if (text.IndexOf("mud", StringComparison.OrdinalIgnoreCase) < 0)
		{
			return text.IndexOf("boue", StringComparison.OrdinalIgnoreCase) >= 0;
		}
		return true;
	}

	private bool HasFracDescendant(Transform root)
	{
		int num = 0;
		Transform[] componentsInChildren = ((Component)root).GetComponentsInChildren<Transform>(true);
		foreach (Transform val in componentsInChildren)
		{
			if (!((Object)(object)val == (Object)(object)root))
			{
				if (IsTarget(((Object)val).name))
				{
					return true;
				}
				if (++num >= 96)
				{
					break;
				}
			}
		}
		return false;
	}

	private bool NearLastPile(Vector3 position)
	{
		//IL_0016: Unknown result type (might be due to invalid IL or missing references)
		//IL_001b: Unknown result type (might be due to invalid IL or missing references)
		//IL_001c: Unknown result type (might be due to invalid IL or missing references)
		//IL_001d: Unknown result type (might be due to invalid IL or missing references)
		//IL_001e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0023: Unknown result type (might be due to invalid IL or missing references)
		//IL_005c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0061: Unknown result type (might be due to invalid IL or missing references)
		//IL_0063: Unknown result type (might be due to invalid IL or missing references)
		//IL_0064: Unknown result type (might be due to invalid IL or missing references)
		//IL_0066: 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)
		float num = 9f;
		Vector3 val;
		foreach (Vector3 pilePosition in pilePositions)
		{
			val = position - pilePosition;
			if (((Vector3)(ref val)).sqrMagnitude <= num)
			{
				return true;
			}
		}
		foreach (Vector3 item in pilePositionsBuffer)
		{
			val = position - item;
			if (((Vector3)(ref val)).sqrMagnitude <= num)
			{
				return true;
			}
		}
		return false;
	}

	private void PurgeStale(HashSet<GameObject> set)
	{
		staleScratch.Clear();
		foreach (GameObject item in set)
		{
			if ((Object)(object)item == (Object)null)
			{
				staleScratch.Add(item);
			}
		}
		foreach (GameObject item2 in staleScratch)
		{
			set.Remove(item2);
		}
	}

	private static string DescribeLineage(Transform node)
	{
		List<string> list = new List<string>();
		Transform parent = node.parent;
		while ((Object)(object)parent != (Object)null && list.Count < 5)
		{
			list.Add(((Object)parent).name);
			parent = parent.parent;
		}
		if (list.Count != 0)
		{
			return string.Join(" > ", list);
		}
		return "no parent";
	}

	private void CollectNearby(Vector3 origin, float maxSqr)
	{
		//IL_000d: Unknown result type (might be due to invalid IL or missing references)
		float num = Mathf.Sqrt(maxSqr) + 3f;
		int num2 = Physics.OverlapSphereNonAlloc(origin, num, nearbyColliders, -1, (QueryTriggerInteraction)2);
		for (int i = 0; i < num2; i++)
		{
			Collider val = nearbyColliders[i];
			if ((Object)(object)val == (Object)null)
			{
				continue;
			}
			MineRock5 componentInParent = ((Component)val).GetComponentInParent<MineRock5>(true);
			if ((Object)(object)componentInParent != (Object)null)
			{
				GameObject gameObject = ((Component)componentInParent).gameObject;
				if (!mineRockList.Contains(gameObject))
				{
					mineRockList.Add(gameObject);
					if (!scanList.Contains(gameObject))
					{
						scanList.Add(gameObject);
					}
				}
			}
			if (!config.OnlyChunks.Value)
			{
				Destructible componentInParent2 = ((Component)val).GetComponentInParent<Destructible>(true);
				if ((Object)(object)componentInParent2 != (Object)null && !scanList.Contains(((Component)componentInParent2).gameObject))
				{
					scanList.Add(((Component)componentInParent2).gameObject);
				}
			}
		}
	}

	private void ReportDiagnostics(GameObject firstRoot, int roots, int targets)
	{
		if ((Object)(object)firstRoot == (Object)null)
		{
			return;
		}
		if (!hierarchyDumped && (config.DebugLog.Value || targets == 0))
		{
			hierarchyDumped = true;
			if (targets == 0 && roots > 0 && !loggedMissingChunks)
			{
				loggedMissingChunks = true;
				log.LogWarning((object)($"Found {roots} Muddy Scrap Pile(s) in range but no fracturable " + "chunk (MineRock5) among them - dumping the hierarchy of " + ((Object)firstRoot).name + ":"));
			}
			DumpHierarchy(firstRoot);
			Census(force: false);
			if (config.DebugLog.Value)
			{
				HuntFracs();
			}
		}
		else if (config.DebugLog.Value && chunks.Count == 0 && targets > 0)
		{
			DumpHierarchy(firstRoot);
			Census(force: true);
			HuntFracs();
		}
	}

	private void Census(bool force)
	{
		//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
		if (censusDone && !force)
		{
			return;
		}
		censusDone = true;
		Transform[] array = Object.FindObjectsByType<Transform>((FindObjectsInactive)1, (FindObjectsSortMode)0);
		nameCounts.Clear();
		List<string> list = new List<string>();
		int num = 0;
		Transform[] array2 = array;
		foreach (Transform val in array2)
		{
			if (((Object)val).name.StartsWith("mudpile", StringComparison.Ordinal))
			{
				num++;
				nameCounts.TryGetValue(((Object)val).name, out var value);
				nameCounts[((Object)val).name] = value + 1;
				if (IsTarget(((Object)val).name) && list.Count < 30)
				{
					string name = ((Object)val).name;
					Transform parent = val.parent;
					list.Add($"  {name} parent={((parent != null) ? ((Object)parent).name : null)} at {val.position} " + "(" + (((Component)val).gameObject.activeInHierarchy ? "active" : "inactive") + ")");
				}
			}
		}
		if (num == 0)
		{
			log.LogInfo((object)"Census: no object named mudpile* in the loaded scenes");
			return;
		}
		List<string> list2 = new List<string>(nameCounts.Count);
		foreach (KeyValuePair<string, int> nameCount in nameCounts)
		{
			list2.Add($"{nameCount.Key} x{nameCount.Value}");
		}
		log.LogInfo((object)($"Census: {num} object(s) named mudpile* in the loaded scenes - " + string.Join(", ", list2)));
		foreach (string item in list)
		{
			log.LogInfo((object)item);
		}
		if (list.Count == 0)
		{
			log.LogInfo((object)("Census: no '" + string.Join("' or '", TargetNames) + "' object anywhere in the loaded scenes"));
		}
	}

	private void HuntFracs()
	{
		//IL_0057: Unknown result type (might be due to invalid IL or missing references)
		if (fracHunted)
		{
			return;
		}
		fracHunted = true;
		Transform[] array = Object.FindObjectsByType<Transform>((FindObjectsInactive)1, (FindObjectsSortMode)0);
		int num = 0;
		Transform[] array2 = array;
		foreach (Transform val in array2)
		{
			if (((Object)val).name.IndexOf("frac", StringComparison.OrdinalIgnoreCase) >= 0)
			{
				num++;
				ManualLogSource obj = log;
				string[] array3 = new string[5];
				string name = ((Object)val).name;
				object arg = val.position;
				Transform parent = val.parent;
				array3[0] = $"Frac hunt: {name} at {arg} parent={((parent != null) ? ((Object)parent).name : null)} ";
				array3[1] = "(";
				array3[2] = (((Component)val).gameObject.activeInHierarchy ? "active" : "inactive");
				array3[3] = ") ";
				array3[4] = DescribeComponents(val);
				obj.LogInfo((object)string.Concat(array3));
			}
		}
		if (num == 0)
		{
			log.LogInfo((object)"Frac hunt: no object with 'frac' in its name in the loaded scenes");
		}
	}

	private void DumpHierarchy(GameObject root)
	{
		//IL_0017: Unknown result type (might be due to invalid IL or missing references)
		log.LogInfo((object)$"Hierarchy of {((Object)root).name} at {root.transform.position}:");
		Transform[] componentsInChildren = root.GetComponentsInChildren<Transform>(true);
		foreach (Transform val in componentsInChildren)
		{
			int num = 0;
			Transform val2 = val;
			while ((Object)(object)val2 != (Object)null && (Object)(object)val2 != (Object)(object)root.transform)
			{
				num++;
				val2 = val2.parent;
			}
			string text = (((Component)val).gameObject.activeInHierarchy ? "+" : "-");
			log.LogInfo((object)("  " + new string(' ', num * 2) + "[" + text + "] " + ((Object)val).name + " (" + DescribeComponents(val) + ")"));
		}
	}

	private static string DescribeComponents(Transform node)
	{
		Component[] components = ((Component)node).GetComponents<Component>();
		List<string> list = new List<string>(components.Length);
		Component[] array = components;
		foreach (Component val in array)
		{
			if (list.Count >= 12)
			{
				list.Add("...");
				break;
			}
			list.Add(((Object)(object)val == (Object)null) ? "<missing script>" : ((object)val).GetType().Name);
		}
		return string.Join(", ", list);
	}

	private void LogScan(HashSet<GameObject> current, int rootsInRange)
	{
		//IL_00f2: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f7: Unknown result type (might be due to invalid IL or missing references)
		//IL_0130: Unknown result type (might be due to invalid IL or missing references)
		//IL_0144: Unknown result type (might be due to invalid IL or missing references)
		//IL_014c: Unknown result type (might be due to invalid IL or missing references)
		nameCounts.Clear();
		foreach (GameObject item in current)
		{
			string key = (((Object)(object)item == (Object)null) ? "<destroyed>" : ((Object)item).name);
			nameCounts.TryGetValue(key, out var value);
			nameCounts[key] = value + 1;
		}
		List<string> list = new List<string>(nameCounts.Count);
		foreach (KeyValuePair<string, int> nameCount in nameCounts)
		{
			list.Add($"{nameCount.Key} x{nameCount.Value}");
		}
		Player localPlayer = Player.m_localPlayer;
		Vector3 val = (((Object)(object)localPlayer != (Object)null) ? ((Component)localPlayer).transform.position : Vector3.zero);
		foreach (GameObject item2 in current)
		{
			if ((Object)(object)item2 != (Object)null)
			{
				log.LogInfo((object)($"  {((Object)item2).name} at {item2.transform.position} " + $"({Vector3.Distance(val, item2.transform.position):0.0} m, " + (item2.activeInHierarchy ? "active" : "inactive") + ")"));
			}
		}
		log.LogInfo((object)($"Scan: {current.Count} object(s) highlighted from {rootsInRange} pile(s) - " + string.Join(", ", list)));
	}
}
internal class ScreenMarkers
{
	private sealed class Area
	{
		internal Transform Node;

		internal MeshFilter Filter;

		internal bool IsPresent()
		{
			if ((Object)(object)Node != (Object)null && ((Component)Node).gameObject.activeInHierarchy && (Object)(object)Filter != (Object)null)
			{
				return (Object)(object)Filter.sharedMesh != (Object)null;
			}
			return false;
		}

		internal Vector3 WorldCenter()
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			Transform node = Node;
			Bounds bounds = Filter.sharedMesh.bounds;
			return node.TransformPoint(((Bounds)(ref bounds)).center);
		}
	}

	private readonly ModConfig config;

	private readonly ManualLogSource log;

	private readonly Dictionary<GameObject, Area[]> areaCache = new Dictionary<GameObject, Area[]>();

	private GUIStyle distanceStyle;

	private GUIStyle arrowStyle;

	private bool loggedEntry;

	private bool loggedCameraFallback;

	private bool loggedFirstDraw;

	private readonly HashSet<GameObject> loggedVisibility = new HashSet<GameObject>();

	private Camera fallbackCamera;

	internal ScreenMarkers(ModConfig config, ManualLogSource log)
	{
		this.config = config;
		this.log = log;
	}

	internal void Draw(HashSet<GameObject> tracked)
	{
		//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
		//IL_0183: Unknown result type (might be due to invalid IL or missing references)
		//IL_0188: Unknown result type (might be due to invalid IL or missing references)
		//IL_018b: Unknown result type (might be due to invalid IL or missing references)
		//IL_018d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0192: Unknown result type (might be due to invalid IL or missing references)
		//IL_0199: Unknown result type (might be due to invalid IL or missing references)
		//IL_019a: Unknown result type (might be due to invalid IL or missing references)
		//IL_01af: Unknown result type (might be due to invalid IL or missing references)
		//IL_01bc: Unknown result type (might be due to invalid IL or missing references)
		//IL_01c9: Unknown result type (might be due to invalid IL or missing references)
		//IL_01dd: Unknown result type (might be due to invalid IL or missing references)
		//IL_025c: 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_0260: Unknown result type (might be due to invalid IL or missing references)
		//IL_0251: Unknown result type (might be due to invalid IL or missing references)
		//IL_0253: Unknown result type (might be due to invalid IL or missing references)
		//IL_0255: Unknown result type (might be due to invalid IL or missing references)
		//IL_01eb: Unknown result type (might be due to invalid IL or missing references)
		//IL_0265: Unknown result type (might be due to invalid IL or missing references)
		//IL_0267: Unknown result type (might be due to invalid IL or missing references)
		//IL_0269: Unknown result type (might be due to invalid IL or missing references)
		//IL_0272: Unknown result type (might be due to invalid IL or missing references)
		//IL_0277: Unknown result type (might be due to invalid IL or missing references)
		//IL_027c: Unknown result type (might be due to invalid IL or missing references)
		//IL_027f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0281: Unknown result type (might be due to invalid IL or missing references)
		//IL_0283: Unknown result type (might be due to invalid IL or missing references)
		//IL_028a: Unknown result type (might be due to invalid IL or missing references)
		//IL_028f: Unknown result type (might be due to invalid IL or missing references)
		//IL_01fa: Unknown result type (might be due to invalid IL or missing references)
		//IL_0208: Unknown result type (might be due to invalid IL or missing references)
		//IL_0217: Unknown result type (might be due to invalid IL or missing references)
		//IL_021a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0221: Unknown result type (might be due to invalid IL or missing references)
		//IL_0226: Unknown result type (might be due to invalid IL or missing references)
		if (!loggedEntry && config.DebugLog.Value)
		{
			loggedEntry = true;
			log.LogInfo((object)($"Markers: OnGUI reached, enabled={config.Marker.Value}, " + $"tracked={tracked.Count}"));
		}
		if (!config.Marker.Value || tracked.Count == 0)
		{
			return;
		}
		Camera val = Camera.main;
		if ((Object)(object)val == (Object)null)
		{
			val = ResolveFallbackCamera();
		}
		if ((Object)(object)val == (Object)null)
		{
			return;
		}
		EnsureStyles();
		float value = config.MarkerSize.Value;
		Color value2 = config.MarkerColor.Value;
		Vector3 position = ((Component)val).transform.position;
		int num = 0;
		Vector2 val4 = default(Vector2);
		Vector2 val5 = default(Vector2);
		foreach (GameObject item in tracked)
		{
			if ((Object)(object)item == (Object)null)
			{
				continue;
			}
			Area[] array = AreasOf(item);
			if (array.Length == 0)
			{
				if (config.DebugLog.Value && loggedVisibility.Add(item))
				{
					log.LogInfo((object)("Markers: " + ((Object)item).name + " has no direct child mesh to mark"));
				}
				continue;
			}
			bool flag = false;
			Area[] array2 = array;
			foreach (Area area in array2)
			{
				if (area.IsPresent())
				{
					flag = true;
					num++;
					Vector3 val2 = area.WorldCenter();
					Vector3 val3 = val.WorldToScreenPoint(val2);
					string text = $"{Vector3.Distance(position, val2):0} m";
					((Vector2)(ref val4))..ctor(val3.x, (float)Screen.height - val3.y);
					bool flag2 = val3.z > 0f;
					if (flag2 && val4.x >= 0f && val4.x <= (float)Screen.width && val4.y >= 0f && val4.y <= (float)Screen.height)
					{
						DrawDot(val4, value, value2);
						DrawText(val4, value, text, value2);
						continue;
					}
					((Vector2)(ref val5))..ctor((float)Screen.width * 0.5f, (float)Screen.height * 0.5f);
					Vector2 val6 = (flag2 ? (val4 - val5) : (val5 - val4));
					Vector2 point = val5 + ClampToBorder(val6, value * 3f);
					DrawArrow(point, val6, value2);
					DrawText(point, value, text, value2);
				}
			}
			if (!flag && config.DebugLog.Value && loggedVisibility.Add(item))
			{
				log.LogInfo((object)($"Markers: {((Object)item).name}: 0/{array.Length} areas present, sample: " + Describe(array[0])));
			}
		}
		if (num > 0 && config.DebugLog.Value && !loggedFirstDraw)
		{
			loggedFirstDraw = true;
			log.LogInfo((object)$"Markers: drawing {num} marker(s) with camera {((Object)val).name}");
		}
		PurgeAreaCache(tracked);
	}

	private static string Describe(Area area)
	{
		if ((Object)(object)area.Node == (Object)null)
		{
			return "node destroyed";
		}
		Mesh val = (((Object)(object)area.Filter != (Object)null) ? area.Filter.sharedMesh : null);
		return "active=" + ((Component)area.Node).gameObject.activeInHierarchy + " mesh=" + (((Object)(object)val != (Object)null) ? $"{((Object)val).name} ({val.vertexCount}v)" : "<empty>");
	}

	private Camera ResolveFallbackCamera()
	{
		//IL_0040: Unknown result type (might be due to invalid IL or missing references)
		//IL_002f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0045: 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_0078: Unknown result type (might be due to invalid IL or missing references)
		//IL_0079: Unknown result type (might be due to invalid IL or missing references)
		//IL_007e: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)fallbackCamera != (Object)null && ((Behaviour)fallbackCamera).enabled)
		{
			return fallbackCamera;
		}
		Vector3 val = (((Object)(object)Player.m_localPlayer != (Object)null) ? ((Component)Player.m_localPlayer).transform.position : Vector3.zero);
		float num = float.MinValue;
		float num2 = float.MaxValue;
		Camera[] allCameras = Camera.allCameras;
		foreach (Camera val2 in allCameras)
		{
			if (((Behaviour)val2).enabled)
			{
				Vector3 val3 = ((Component)val2).transform.position - val;
				float sqrMagnitude = ((Vector3)(ref val3)).sqrMagnitude;
				if (val2.depth > num || (Mathf.Approximately(val2.depth, num) && sqrMagnitude < num2))
				{
					num = val2.depth;
					num2 = sqrMagnitude;
					fallbackCamera = val2;
				}
			}
		}
		if ((Object)(object)fallbackCamera != (Object)null && config.DebugLog.Value && !loggedCameraFallback)
		{
			loggedCameraFallback = true;
			log.LogInfo((object)("Markers: Camera.main is null, using " + ((Object)fallbackCamera).name + " " + $"(depth {fallbackCamera.depth}) instead"));
		}
		if ((Object)(object)fallbackCamera == (Object)null && !loggedCameraFallback)
		{
			loggedCameraFallback = true;
			log.LogWarning((object)"Markers: no camera at all in the scene");
		}
		return fallbackCamera;
	}

	private Area[] AreasOf(GameObject fragment)
	{
		//IL_002e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0035: Expected O, but got Unknown
		if (areaCache.TryGetValue(fragment, out var value))
		{
			return value;
		}
		List<Area> list = new List<Area>(32);
		foreach (Transform item in fragment.transform)
		{
			Transform val = item;
			if (!((Object)val).name.StartsWith("MuddyScrapPileHighlight", StringComparison.Ordinal))
			{
				MeshFilter component = ((Component)val).GetComponent<MeshFilter>();
				if (!((Object)(object)component == (Object)null))
				{
					list.Add(new Area
					{
						Node = val,
						Filter = component
					});
				}
			}
		}
		Area[] array = list.ToArray();
		areaCache[fragment] = array;
		return array;
	}

	private void PurgeAreaCache(HashSet<GameObject> tracked)
	{
		if (areaCache.Count < 128)
		{
			return;
		}
		List<GameObject> list = null;
		foreach (KeyValuePair<GameObject, Area[]> item in areaCache)
		{
			if ((Object)(object)item.Key == (Object)null || !tracked.Contains(item.Key))
			{
				list = list ?? new List<GameObject>();
				list.Add(item.Key);
			}
		}
		if (list == null)
		{
			return;
		}
		foreach (GameObject item2 in list)
		{
			areaCache.Remove(item2);
			loggedVisibility.Remove(item2);
		}
	}

	private static Vector2 ClampToBorder(Vector2 offset, float margin)
	{
		//IL_0017: Unknown result type (might be due to invalid IL or missing references)
		//IL_001c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0041: Unknown result type (might be due to invalid IL or missing references)
		//IL_000e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0013: Unknown result type (might be due to invalid IL or missing references)
		//IL_0062: Unknown result type (might be due to invalid IL or missing references)
		//IL_0050: Unknown result type (might be due to invalid IL or missing references)
		//IL_0083: 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)
		//IL_0071: Unknown result type (might be due to invalid IL or missing references)
		if (((Vector2)(ref offset)).sqrMagnitude < 0.01f)
		{
			offset = Vector2.up;
		}
		Vector2 normalized = ((Vector2)(ref offset)).normalized;
		float num = (float)Screen.width * 0.5f - margin;
		float num2 = (float)Screen.height * 0.5f - margin;
		float num3 = float.MaxValue;
		if (normalized.x != 0f)
		{
			num3 = Mathf.Min(num3, num / Mathf.Abs(normalized.x));
		}
		if (normalized.y != 0f)
		{
			num3 = Mathf.Min(num3, num2 / Mathf.Abs(normalized.y));
		}
		return normalized * num3;
	}

	private static void DrawDot(Vector2 point, float size, Color color)
	{
		//IL_0000: Unknown result type (might be due to invalid IL or missing references)
		//IL_0005: Unknown result type (might be due to invalid IL or missing references)
		//IL_000b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0019: Unknown result type (might be due to invalid IL or missing references)
		//IL_0029: Unknown result type (might be due to invalid IL or missing references)
		Color color2 = GUI.color;
		GUI.color = color;
		GUI.DrawTexture(new Rect(point.x - size * 0.5f, point.y - size * 0.5f, size, size), (Texture)(object)Texture2D.whiteTexture);
		GUI.color = color2;
	}

	private void DrawText(Vector2 point, float size, string text, Color color)
	{
		//IL_0015: Unknown result type (might be due to invalid IL or missing references)
		//IL_001d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0036: Unknown result type (might be due to invalid IL or missing references)
		//IL_004f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0081: Unknown result type (might be due to invalid IL or missing references)
		//IL_0092: Unknown result type (might be due to invalid IL or missing references)
		//IL_0099: Unknown result type (might be due to invalid IL or missing references)
		if (config.MarkerDistance.Value)
		{
			Rect val = default(Rect);
			((Rect)(ref val))..ctor(point.x + size, point.y - size, 100f, size * 2f);
			Color color2 = GUI.color;
			GUI.color = new Color(0f, 0f, 0f, 0.85f);
			GUI.Label(new Rect(((Rect)(ref val)).x + 1f, ((Rect)(ref val)).y + 1f, ((Rect)(ref val)).width, ((Rect)(ref val)).height), text, distanceStyle);
			GUI.color = color;
			GUI.Label(val, text, distanceStyle);
			GUI.color = color2;
		}
	}

	private void DrawArrow(Vector2 point, Vector2 direction, Color color)
	{
		//IL_0005: 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_001d: 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_0023: Unknown result type (might be due to invalid IL or missing references)
		//IL_0028: Unknown result type (might be due to invalid IL or missing references)
		//IL_0029: Unknown result type (might be due to invalid IL or missing references)
		//IL_002f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0035: 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_0057: Unknown result type (might be due to invalid IL or missing references)
		//IL_006c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0072: Unknown result type (might be due to invalid IL or missing references)
		float num = 90f - Mathf.Atan2(direction.y, direction.x) * 57.29578f;
		Color color2 = GUI.color;
		Matrix4x4 matrix = GUI.matrix;
		GUI.color = color;
		GUIUtility.RotateAroundPivot(num, point);
		GUI.Label(new Rect(point.x - 12f, point.y - 12f, 24f, 24f), "▲", arrowStyle);
		GUI.matrix = matrix;
		GUI.color = color2;
	}

	private void EnsureStyles()
	{
		//IL_000a: Unknown result type (might be due to invalid IL or missing references)
		//IL_000f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0017: Unknown result type (might be due to invalid IL or missing references)
		//IL_0023: Expected O, but got Unknown
		//IL_002e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0039: Unknown result type (might be due to invalid IL or missing references)
		//IL_003e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0046: Unknown result type (might be due to invalid IL or missing references)
		//IL_0052: Expected O, but got Unknown
		//IL_005d: Unknown result type (might be due to invalid IL or missing references)
		if (distanceStyle == null)
		{
			distanceStyle = new GUIStyle
			{
				fontSize = 12,
				alignment = (TextAnchor)3
			};
			distanceStyle.normal.textColor = Color.white;
			arrowStyle = new GUIStyle
			{
				fontSize = 18,
				alignment = (TextAnchor)4
			};
			arrowStyle.normal.textColor = Color.white;
		}
	}
}
internal class ShellHighlighter
{
	private sealed class ShellBinding
	{
		internal Renderer Source;

		internal MeshFilter SourceFilter;

		internal MeshFilter ShellFilter;

		internal MeshRenderer Shell;

		internal Mesh Mesh;

		internal int VertexCount;

		internal bool Visible = true;
	}

	private static readonly string[] ShaderCandidates = new string[5] { "Sprites/Default", "UI/Unlit/Transparent", "Custom/Particle (Unlit)", "Legacy Shaders/Particles/Alpha Blended", "Unlit/Color" };

	private const int TransparentQueue = 3000;

	private const float RefreshInterval = 0.2f;

	private readonly ModConfig config;

	private readonly ManualLogSource log;

	private readonly Dictionary<GameObject, List<ShellBinding>> shells = new Dictionary<GameObject, List<ShellBinding>>();

	private readonly Dictionary<Mesh, Mesh> expandedMeshes = new Dictionary<Mesh, Mesh>();

	private readonly List<GameObject> removeScratch = new List<GameObject>();

	private Material shellMaterial;

	private string shellTintProperty;

	private float appliedWidth = -1f;

	private Color appliedColor = new Color(0f, 0f, 0f, 0f);

	private float nextShaderRetry;

	private float nextStateLog;

	private bool loggedShaderError;

	private bool occluderProbed;

	private float nextWaterNet;

	private bool appliedHideCrypt;

	private float nextRefresh;

	private readonly HashSet<Renderer> hiddenWater = new HashSet<Renderer>();

	internal ShellHighlighter(ModConfig config, ManualLogSource log)
	{
		//IL_0041: Unknown result type (might be due to invalid IL or missing references)
		//IL_0046: Unknown result type (might be due to invalid IL or missing references)
		this.config = config;
		this.log = log;
	}

	internal void Sync(HashSet<GameObject> tracked, bool changed)
	{
		//IL_007a: Unknown result type (might be due to invalid IL or missing references)
		//IL_007f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0080: Unknown result type (might be due to invalid IL or missing references)
		//IL_0082: Unknown result type (might be due to invalid IL or missing references)
		//IL_009a: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
		if (!config.Shell.Value || !config.Enabled.Value)
		{
			Clear();
		}
		else
		{
			if (!EnsureMaterial())
			{
				return;
			}
			SyncWater();
			float value = config.ShellWidth.Value;
			if (!Mathf.Approximately(value, appliedWidth))
			{
				DestroyShells();
				ClearMeshCache();
				appliedWidth = value;
				changed = true;
			}
			Color value2 = config.ShellColor.Value;
			if (value2 != appliedColor)
			{
				shellMaterial.SetColor(shellTintProperty, value2);
				appliedColor = value2;
			}
			if (config.DebugLog.Value)
			{
				ProbeOccluder();
			}
			if (changed)
			{
				Prune(tracked);
				foreach (GameObject item in tracked)
				{
					if (!((Object)(object)item == (Object)null) && !shells.ContainsKey(item) && !HasTrackedAncestor(item, tracked))
					{
						CreateShells(item);
					}
				}
			}
			if (Time.time >= nextRefresh)
			{
				nextRefresh = Time.time + 0.2f;
				RefreshSources();
			}
			if (config.DebugLog.Value && Time.time >= nextStateLog)
			{
				nextStateLog = Time.time + 5f;
				LogBindingStates();
			}
		}
	}

	private void LogBindingStates()
	{
		foreach (KeyValuePair<GameObject, List<ShellBinding>> shell in shells)
		{
			if ((Object)(object)shell.Key == (Object)null)
			{
				continue;
			}
			int num = 0;
			ShellBinding shellBinding = null;
			foreach (ShellBinding item in shell.Value)
			{
				if ((Object)(object)item.Shell != (Object)null && ((Renderer)item.Shell).enabled)
				{
					num++;
				}
				shellBinding = shellBinding ?? item;
			}
			string text = "<no binding>";
			if (shellBinding != null)
			{
				Mesh val = (((Object)(object)shellBinding.SourceFilter != (Object)null) ? shellBinding.SourceFilter.sharedMesh : null);
				text = (((Object)(object)shellBinding.Source == (Object)null) ? "source destroyed" : ("active=" + ((Component)shellBinding.Source).gameObject.activeInHierarchy + " enabled=" + shellBinding.Source.enabled + " forcedOff=" + shellBinding.Source.forceRenderingOff + " layer=" + ((Component)shellBinding.Source).gameObject.layer + " shellActive=" + ((Object)(object)shellBinding.Shell != (Object)null && ((Component)shellBinding.Shell).gameObject.activeInHierarchy) + " mesh=" + (((Object)(object)val != (Object)null) ? $"{((Object)val).name} ({val.vertexCount}v)" : "<empty>")));
			}
			log.LogInfo((object)($"Shell state {((Object)shell.Key).name}: {num}/{shell.Value.Count} shells " + "visible; sample: " + text));
		}
	}

	internal void Clear()
	{
		RestoreCryptWater();
		appliedHideCrypt = false;
		if (shells.Count != 0 || expandedMeshes.Count != 0 || !(appliedWidth < 0f))
		{
			DestroyShells();
			ClearMeshCache();
			appliedWidth = -1f;
		}
	}

	private void Prune(HashSet<GameObject> tracked)
	{
		if (shells.Count == 0)
		{
			return;
		}
		removeScratch.Clear();
		foreach (GameObject key in shells.Keys)
		{
			if ((Object)(object)key == (Object)null)
			{
				removeScratch.Add(key);
			}
			else if (!tracked.Contains(key) || HasTrackedAncestor(key, tracked))
			{
				DestroyShellsOf(key);
				removeScratch.Add(key);
			}
		}
		foreach (GameObject item in removeScratch)
		{
			shells.Remove(item);
		}
	}

	private static bool HasTrackedAncestor(GameObject pile, HashSet<GameObject> tracked)
	{
		Transform parent = pile.transform.parent;
		while ((Object)(object)parent != (Object)null)
		{
			if (tracked.Contains(((Component)parent).gameObject))
			{
				return true;
			}
			parent = parent.parent;
		}
		return false;
	}

	private void DestroyShellsOf(GameObject pile)
	{
		if (!shells.TryGetValue(pile, out var value))
		{
			return;
		}
		foreach (ShellBinding item in value)
		{
			if ((Object)(object)item.Shell != (Object)null)
			{
				Object.Destroy((Object)(object)((Component)item.Shell).gameObject);
			}
		}
	}

	private void RefreshSources()
	{
		foreach (List<ShellBinding> value in shells.Values)
		{
			foreach (ShellBinding item in value)
			{
				if ((Object)(object)item.Shell == (Object)null)
				{
					continue;
				}
				Mesh val = (((Object)(object)item.SourceFilter != (Object)null) ? item.SourceFilter.sharedMesh : null);
				if ((Object)(object)val != (Object)(object)item.Mesh)
				{
					item.Mesh = val;
					item.VertexCount = (((Object)(object)val != (Object)null) ? val.vertexCount : 0);
					item.ShellFilter.sharedMesh = (((Object)(object)val == (Object)null) ? null : ResolveShellMesh(val));
				}
				else if ((Object)(object)val != (Object)null && val.vertexCount != item.VertexCount)
				{
					int vertexCount = item.VertexCount;
					item.VertexCount = val.vertexCount;
					DropCachedMesh(val);
					item.ShellFilter.sharedMesh = ResolveShellMesh(val);
					if (config.DebugLog.Value)
					{
						log.LogInfo((object)("Shell source " + DescribeSource(item) + ": mesh rewritten in " + $" place ({vertexCount} -> {item.VertexCount} vertices)"));
					}
				}
				if ((Object)(object)item.Source != (Object)null)
				{
					((Component)item.Shell).gameObject.layer = ((Component)item.Source).gameObject.layer;
				}
				bool flag = (Object)(object)item.Source != (Object)null && item.Source.enabled && !item.Source.forceRenderingOff && (Object)(object)val != (Object)null;
				if (flag != item.Visible)
				{
					item.Visible = flag;
					((Renderer)item.Shell).enabled = flag;
					if (config.DebugLog.Value)
					{
						log.LogInfo((object)("Shell source " + DescribeSource(item) + ": " + (flag ? "shown again" : ("hidden (" + HideReason(item) + ")"))));
					}
				}
			}
		}
	}

	private void DropCachedMesh(Mesh source)
	{
		if (expandedMeshes.TryGetValue(source, out var value) && (Object)(object)value != (Object)null && (Object)(object)value != (Object)(object)source)
		{
			Object.Destroy((Object)(object)value);
		}
		expandedMeshes.Remove(source);
	}

	private static string DescribeSource(ShellBinding binding)
	{
		if ((Object)(object)binding.Source != (Object)null)
		{
			return ((Object)binding.Source).name;
		}
		if (!((Object)(object)binding.SourceFilter != (Object)null))
		{
			return "<destroyed>";
		}
		return ((Object)binding.SourceFilter).name;
	}

	private static string HideReason(ShellBinding binding)
	{
		if ((Object)(object)binding.Source == (Object)null)
		{
			return "source destroyed";
		}
		if (!binding.Source.enabled)
		{
			return "renderer disabled";
		}
		if (!binding.Source.forceRenderingOff)
		{
			return "mesh emptied";
		}
		return "renderer forced off";
	}

	private void DestroyShells()
	{
		foreach (List<ShellBinding> value in shells.Values)
		{
			foreach (ShellBinding item in value)
			{
				if ((Object)(object)item.Shell != (Object)null)
				{
					Object.Destroy((Object)(object)((Component)item.Shell).gameObject);
				}
			}
		}
		shells.Clear();
	}

	private void ClearMeshCache()
	{
		foreach (Mesh value in expandedMeshes.Values)
		{
			if ((Object)(object)value != (Object)null)
			{
				Object.Destroy((Object)(object)value);
			}
		}
		expandedMeshes.Clear();
	}

	private void CreateShells(GameObject pile)
	{
		//IL_0048: Unknown result type (might be due to invalid IL or missing references)
		//IL_004f: Expected O, but got Unknown
		//IL_00d9: Unknown result type (might be due to invalid IL or missing references)
		//IL_00de: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f6: Unknown result type (might be due to invalid IL or missing references)
		//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
		//IL_0109: Unknown result type (might be due to invalid IL or missing references)
		//IL_010e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0112: Unknown result type (might be due to invalid IL or missing references)
		//IL_0130: Unknown result type (might be due to invalid IL or missing references)
		//IL_014a: Unknown result type (might be due to invalid IL or missing references)
		List<ShellBinding> list = new List<ShellBinding>();
		MeshFilter[] componentsInChildren = pile.GetComponentsInChildren<MeshFilter>(true);
		foreach (MeshFilter val in componentsInChildren)
		{
			Mesh sharedMesh = val.sharedMesh;
			MeshRenderer component = ((Component)val).GetComponent<MeshRenderer>();
			if (!((Object)(object)sharedMesh == (Object)null) && !((Object)(object)component == (Object)null))
			{
				GameObject val2 = new GameObject("MuddyScrapPileHighlight_Shell");
				val2.transform.SetParent(((Component)val).transform, false);
				MeshFilter val3 = val2.AddComponent<MeshFilter>();
				val3.sharedMesh = ResolveShellMesh(sharedMesh);
				MeshRenderer val4 = val2.AddComponent<MeshRenderer>();
				((Renderer)val4).sharedMaterial = shellMaterial;
				((Renderer)val4).shadowCastingMode = (ShadowCastingMode)0;
				((Renderer)val4).receiveShadows = false;
				((Renderer)val4).lightProbeUsage = (LightProbeUsage)0;
				((Renderer)val4).reflectionProbeUsage = (ReflectionProbeUsage)0;
				((Renderer)val4).enabled = ((Renderer)component).enabled;
				if (!sharedMesh.isReadable)
				{
					float[] array = new float[3];
					Bounds bounds = sharedMesh.bounds;
					array[0] = ((Bounds)(ref bounds)).size.x;
					bounds = sharedMesh.bounds;
					array[1] = ((Bounds)(ref bounds)).size.y;
					bounds = sharedMesh.bounds;
					array[2] = ((Bounds)(ref bounds)).size.z;
					float num = Mathf.Max(0.05f, Mathf.Min(array));
					val2.transform.localScale = Vector3.one * (1f + 2f * appliedWidth / num);
				}
				list.Add(new ShellBinding
				{
					Source = (Renderer)(object)component,
					SourceFilter = val,
					ShellFilter = val3,
					Shell = val4,
					Mesh = sharedMesh,
					VertexCount = sharedMesh.vertexCount,
					Visible = ((Renderer)component).enabled
				});
			}
		}
		shells[pile] = list;
	}

	private Mesh ResolveShellMesh(Mesh source)
	{
		//IL_005b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0060: Unknown result type (might be due to invalid IL or missing references)
		//IL_0064: 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_0091: 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_007c: 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_0083: 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)
		//IL_0096: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
		if (expandedMeshes.TryGetValue(source, out var value))
		{
			return value;
		}
		Mesh val = source;
		if (source.isReadable)
		{
			val = Object.Instantiate<Mesh>(source);
			((Object)val).name = ((Object)source).name + "_MuddyScrapPileHighlight";
			Vector3[] vertices = val.vertices;
			Vector3[] normals = val.normals;
			bool flag = normals != null && normals.Length == vertices.Length;
			Bounds bounds = val.bounds;
			Vector3 center = ((Bounds)(ref bounds)).center;
			for (int i = 0; i < vertices.Length; i++)
			{
				? val3;
				if (!flag)
				{
					Vector3 val2 = vertices[i] - center;
					val3 = ((Vector3)(ref val2)).normalized;
				}
				else
				{
					val3 = normals[i];
				}
				Vector3 val4 = (Vector3)val3;
				ref Vector3 reference = ref vertices[i];
				reference += val4 * appliedWidth;
			}
			val.vertices = vertices;
			val.RecalculateBounds();
		}
		expandedMeshes[source] = val;
		return val;
	}

	private bool EnsureMaterial()
	{
		//IL_0071: Unknown result type (might be due to invalid IL or missing references)
		//IL_007b: Expected O, but got Unknown
		//IL_008f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0094: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)shellMaterial != (Object)null)
		{
			return true;
		}
		if (Time.time < nextShaderRetry)
		{
			return false;
		}
		string[] shaderCandidates = ShaderCandidates;
		foreach (string text in shaderCandidates)
		{
			Shader val = Shader.Find(text);
			if (!((Object)(object)val == (Object)null))
			{
				string text2 = FindTintProperty(val);
				if (text2 != null)
				{
					shellMaterial = new Material(val);
					shellTintProperty = text2;
					appliedColor = config.ShellColor.Value;
					shellMaterial.SetColor(text2, appliedColor);
					shellMaterial.renderQueue = 3000;
					log.LogInfo((object)("Shell shader: " + text + ", tint property " + text2 + ", " + $"render queue {shellMaterial.renderQueue}"));
					return true;
				}
				log.LogInfo((object)("Shader " + text + " has no colour property, skipping it"));
			}
		}
		if (!loggedShaderError)
		{
			loggedShaderError = true;
			log.LogError((object)("No usable highlight shader found (tried " + string.Join(", ", ShaderCandidates) + "); retrying in 30 s."));
		}
		nextShaderRetry = Time.time + 30f;
		return false;
	}

	private void SyncWater()
	{
		bool value = config.HideCryptWater.Value;
		if (value != appliedHideCrypt)
		{
			appliedHideCrypt = value;
			nextWaterNet = 0f;
		}
		if (!value && hiddenWater.Count > 0)
		{
			RestoreCryptWater();
		}
		if (!value || Time.time < nextWaterNet)
		{
			return;
		}
		nextWaterNet = Time.time + 15f;
		Renderer[] array = Object.FindObjectsByType<Renderer>((FindObjectsInactive)1, (FindObjectsSortMode)0);
		foreach (Renderer val in array)
		{
			if ((Object)(object)val != (Object)null && ((Object)val).name.Contains("WaterSurface"))
			{
				ApplyWaterOptions(val, value);
			}
		}
	}

	private void ApplyWaterOptions(Renderer renderer, bool hideCrypt)
	{
		if (hideCrypt && IsCryptWater(renderer))
		{
			HideWater(renderer);
		}
	}

	private static bool IsCryptWater(Renderer renderer)
	{
		Transform val = ((Component)renderer).transform;
		while ((Object)(object)val != (Object)null)
		{
			if (((Object)val).name.IndexOf("SunkenCrypt", StringComparison.OrdinalIgnoreCase) >= 0)
			{
				return true;
			}
			val = val.parent;
		}
		return false;
	}

	private void HideWater(Renderer renderer)
	{
		if (renderer.enabled)
		{
			renderer.enabled = false;
			hiddenWater.Add(renderer);
			if (config.DebugLog.Value)
			{
				log.LogInfo((object)("Hid crypt water '" + ((Object)renderer).name + "' so the shell shows"));
			}
		}
	}

	private void RestoreCryptWater()
	{
		foreach (Renderer item in hiddenWater)
		{
			if ((Object)(object)item != (Object)null)
			{
				item.enabled = true;
			}
		}
		hiddenWater.Clear();
	}

	private void ProbeOccluder()
	{
		if (occluderProbed || !config.DebugLog.Value)
		{
			return;
		}
		Renderer[] array = Object.FindObjectsByType<Renderer>((FindObjectsInactive)1, (FindObjectsSortMode)0);
		foreach (Renderer val in array)
		{
			if (!((Object)(object)val == (Object)null) && ((Object)val).name.IndexOf("stone", StringComparison.OrdinalIgnoreCase) >= 0)
			{
				Material sharedMaterial = val.sharedMaterial;
				if (!((Object)(object)sharedMaterial == (Object)null))
				{
					occluderProbed = true;
					ManualLogSource obj = log;
					string[] obj2 = new string[8]
					{
						"Occluder probe: '",
						((Object)val).name,
						"' shader=",
						null,
						null,
						null,
						null,
						null
					};
					Shader shader = sharedMaterial.shader;
					obj2[3] = ((shader != null) ? ((Object)shader).name : null);
					obj2[4] = " ";
					obj2[5] = $"queue={sharedMaterial.renderQueue} ";
					obj2[6] = string.Format("has_ZWrite={0} ", sharedMaterial.HasProperty("_ZWrite"));
					obj2[7] = $"(shell: queue {3000}, ZTest LEqual)";
					obj.LogInfo((object)string.Concat(obj2));
					break;
				}
			}
		}
	}

	private static string FindTintProperty(Shader shader)
	{
		//IL_000f: Unknown result type (might be due to invalid IL or missing references)
		int propertyCount = shader.GetPropertyCount();
		string text = null;
		for (int i = 0; i < propertyCount; i++)
		{
			if ((int)shader.GetPropertyType(i) == 0)
			{
				string propertyName = shader.GetPropertyName(i);
				if (propertyName == "_Color")
				{
					return propertyName;
				}
				text = text ?? propertyName;
			}
		}
		return text;
	}
}