Decompiled source of BenchLink v0.1.0

BenchLink.dll

Decompiled 6 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Jotunn.Entities;
using Jotunn.Managers;
using Jotunn.Utils;
using Microsoft.CodeAnalysis;
using UnityEngine;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: IgnoresAccessChecksTo("")]
[assembly: AssemblyCompany("BenchLink")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Link nearby crafting stations into clusters so any bench's range counts for every bench type in the cluster.")]
[assembly: AssemblyFileVersion("0.1.0.0")]
[assembly: AssemblyInformationalVersion("0.1.0+be295baf63ed35d85924693d03e1b16ab5cae297")]
[assembly: AssemblyProduct("BenchLink")]
[assembly: AssemblyTitle("BenchLink")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.1.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace BenchLink
{
	internal sealed class BenchLinkCommand : ConsoleCommand
	{
		private const float NearbyDistance = 100f;

		private const int MaxAreaCells = 100000;

		public override string Name => "benchlink";

		public override string Help => "Lists the linked crafting station clusters near you: members, ranges, link distances, and the area covered with and without linking.";

		public override void Run(string[] args)
		{
			((ConsoleCommand)this).Run(args, (Terminal)(object)Console.instance);
		}

		public override void Run(string[] args, Terminal context)
		{
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null)
			{
				context.AddString("benchlink: no player in the world.");
				return;
			}
			StationGraph graph = StationClusters.Graph;
			IReadOnlyList<CraftingStation> stations = StationClusters.Stations;
			Vector3 position = ((Component)localPlayer).transform.position;
			context.AddString("BenchLink: LinkSlack " + BenchLinkConfig.LinkSlack.Value.ToString("0.#", CultureInfo.InvariantCulture) + ", " + graph.Count + " stations loaded");
			int num = 0;
			for (int i = 0; i < graph.ClusterCount; i++)
			{
				if (IsNear(graph, i, position))
				{
					num++;
					Describe(context, graph, stations, i, position);
				}
			}
			if (num == 0)
			{
				context.AddString("No stations within " + 100f + " m of you.");
			}
		}

		private static bool IsNear(StationGraph graph, int cluster, Vector3 origin)
		{
			//IL_0018: 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)
			for (int i = 0; i < graph.Count; i++)
			{
				if (graph.ClusterOf(i) == cluster && StationGraph.Distance(graph[i], new StationNode(origin.x, origin.z, 0f, -1, 0L)) < 100f)
				{
					return true;
				}
			}
			return false;
		}

		private static void Describe(Terminal context, StationGraph graph, IReadOnlyList<CraftingStation> stations, int cluster, Vector3 origin)
		{
			//IL_0048: 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_0086: 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)
			context.AddString("Cluster " + cluster + " (" + graph.ClusterSize(cluster) + " stations)");
			StationNode b = new StationNode(origin.x, origin.z, 0f, -1, 0L);
			for (int i = 0; i < graph.Count; i++)
			{
				if (graph.ClusterOf(i) == cluster)
				{
					StationNode a = graph[i];
					string text = (graph.Covers(i, origin.x, origin.z) ? ", you are in range" : string.Empty);
					string text2 = (graph.IsDuplicate(i) ? ", duplicate type: not linked" : string.Empty);
					context.AddString("  [" + i + "] " + Localization.instance.Localize(stations[i].m_name) + " level " + stations[i].GetLevel(true) + ", range " + a.Range.ToString("0.#", CultureInfo.InvariantCulture) + " m, " + StationGraph.Distance(in a, in b).ToString("0.#", CultureInfo.InvariantCulture) + " m from you" + text + text2);
				}
			}
			StringBuilder stringBuilder = new StringBuilder();
			for (int j = 0; j < graph.Count; j++)
			{
				for (int k = j + 1; k < graph.Count; k++)
				{
					if (graph.ClusterOf(j) == cluster && graph.ClusterOf(k) == cluster && StationGraph.AreLinked(graph[j], graph[k], BenchLinkConfig.LinkSlack.Value))
					{
						stringBuilder.Append("  [").Append(j).Append("]-[")
							.Append(k)
							.Append("] ")
							.Append(StationGraph.Distance(graph[j], graph[k]).ToString("0.#", CultureInfo.InvariantCulture))
							.Append(" m\n");
					}
				}
			}
			if (stringBuilder.Length > 0)
			{
				context.AddString("  links:\n" + stringBuilder.ToString().TrimEnd(new char[1] { '\n' }));
			}
			var (num, num2) = graph.EstimateAreas(cluster, 100000);
			context.AddString("  area with every type in range: vanilla " + num.ToString("N0", CultureInfo.InvariantCulture) + " m2, linked " + num2.ToString("N0", CultureInfo.InvariantCulture) + " m2 (any bench in range)");
		}
	}
	internal static class PlacementNotice
	{
		private const float PlacementWindow = 1f;

		private const float HoldSeconds = 3f;

		private const float FadeSeconds = 1.5f;

		private static float _placedAt = float.NegativeInfinity;

		private static CraftingStation _justPlaced;

		private static float _fadeAt = float.PositiveInfinity;

		private static string _shownText;

		public static void Tick()
		{
			FadeWhenDue();
			StationClusters.Poll();
			if (StationClusters.TryTakeChanged(out var names))
			{
				Show("Cluster " + Localization.instance.Localize(names) + " changed!");
			}
			if (!((Object)(object)_justPlaced == (Object)null))
			{
				CraftingStation justPlaced = _justPlaced;
				_justPlaced = null;
				int num = StationClusters.IndexOf(justPlaced);
				Player localPlayer = Player.m_localPlayer;
				if (num >= 0 && !((Object)(object)localPlayer == (Object)null) && StationClusters.Graph.IsDuplicate(num))
				{
					Show("Duplicate " + Localization.instance.Localize(justPlaced.m_name) + ": it won't extend the cluster");
				}
			}
		}

		private static void Show(string text)
		{
			MessageHud instance = MessageHud.instance;
			if (!((Object)(object)instance == (Object)null))
			{
				int count = instance._crossFadeTextBuffer.Count;
				instance.ShowMessage((MessageType)2, text, 0, (Sprite)null, false, true);
				if (instance._crossFadeTextBuffer.Count == count + 2)
				{
					instance._crossFadeTextBuffer.RemoveAt(count + 1);
					_shownText = instance.m_messageCenterText.text;
					_fadeAt = Time.unscaledTime + 3f;
				}
			}
		}

		private static void FadeWhenDue()
		{
			if (!(Time.unscaledTime < _fadeAt))
			{
				_fadeAt = float.PositiveInfinity;
				MessageHud instance = MessageHud.instance;
				if ((Object)(object)instance != (Object)null && instance.m_messageCenterText.text == _shownText)
				{
					((Graphic)instance.m_messageCenterText).CrossFadeAlpha(0f, 1.5f, true);
				}
			}
		}

		internal static void Placed(Piece piece)
		{
			if ((Object)(object)((Component)piece).GetComponentInChildren<CraftingStation>() != (Object)null)
			{
				_placedAt = Time.time;
			}
		}

		internal static void Started(CraftingStation station)
		{
			if (Time.time - _placedAt < 1f)
			{
				_placedAt = float.NegativeInfinity;
				_justPlaced = station;
			}
		}
	}
	[HarmonyPatch(typeof(Player), "PlacePiece")]
	internal static class PlacePiecePatch
	{
		private static void Postfix(Piece piece)
		{
			PlacementNotice.Placed(piece);
		}
	}
	[BepInPlugin("javidahmed64592.benchlink", "Bench Link", "0.1.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[NetworkCompatibility(/*Could not decode attribute arguments.*/)]
	public class BenchLinkPlugin : BaseUnityPlugin
	{
		public const string PluginGUID = "javidahmed64592.benchlink";

		public const string PluginName = "Bench Link";

		public const string PluginVersion = "0.1.0";

		private readonly RangeIndicator _indicator = new RangeIndicator();

		private Harmony _harmony;

		internal static ManualLogSource Log { get; private set; }

		private void Awake()
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			BenchLinkConfig.Bind(((BaseUnityPlugin)this).Config);
			_harmony = new Harmony("javidahmed64592.benchlink");
			_harmony.PatchAll();
			CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new BenchLinkCommand());
		}

		private void Update()
		{
			_indicator.Tick(Time.deltaTime);
			PlacementNotice.Tick();
		}

		private void OnDestroy()
		{
			_indicator.Hide();
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}
	}
	internal enum IndicatorMode
	{
		Off,
		WhileBuilding
	}
	internal static class BenchLinkConfig
	{
		public static ConfigEntry<float> LinkSlack;

		public static ConfigEntry<IndicatorMode> Indicator;

		public static void Bind(ConfigFile config)
		{
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Expected O, but got Unknown
			LinkSlack = config.Bind<float>("General", "LinkSlack", 0f, new ConfigDescription("Extra metres added when deciding whether two stations are linked. Stations are linked when they are closer than the larger of their two build ranges plus this.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 20f), Array.Empty<object>()));
			Indicator = config.Bind<IndicatorMode>("Look", "Indicator", IndicatorMode.WhileBuilding, "Show the build range of every linked station while the hammer is out. Off removes it.");
		}
	}
	internal sealed class RangeIndicator
	{
		private readonly struct MarkerRenderers
		{
			public Renderer[] Items { get; }

			public int ChildCount { get; }

			public MarkerRenderers(Renderer[] items, int childCount)
			{
				Items = items;
				ChildCount = childCount;
			}
		}

		private const float TickInterval = 0.25f;

		private static readonly Color LinkedTint = new Color(0.941f, 0.69f, 0.29f);

		private static readonly Color HoverTint = new Color(0.6f, 0.8f, 1f);

		private static readonly string[] ColorProperties = new string[3] { "_Color", "_TintColor", "_BaseColor" };

		private readonly List<CraftingStation> _shown = new List<CraftingStation>();

		private readonly List<CraftingStation> _next = new List<CraftingStation>();

		private readonly HashSet<CraftingStation> _tinted = new HashSet<CraftingStation>();

		private readonly Dictionary<CraftingStation, MarkerRenderers> _cache = new Dictionary<CraftingStation, MarkerRenderers>();

		private readonly MaterialPropertyBlock _block = new MaterialPropertyBlock();

		private int[] _colorIds;

		private bool[] _clusterActive = Array.Empty<bool>();

		private float _timer;

		private int _cacheVersion;

		public void Tick(float deltaTime)
		{
			_timer += deltaTime;
			if (_timer < 0.25f)
			{
				return;
			}
			_timer = 0f;
			Player localPlayer = Player.m_localPlayer;
			if (BenchLinkConfig.Indicator.Value == IndicatorMode.Off || (Object)(object)localPlayer == (Object)null || !((Character)localPlayer).InPlaceMode())
			{
				Hide();
				return;
			}
			if (_cacheVersion != StationClusters.Version)
			{
				_cache.Clear();
				_cacheVersion = StationClusters.Version;
			}
			Update(localPlayer);
		}

		public void Hide()
		{
			foreach (CraftingStation item in _shown)
			{
				Hide(item);
			}
			_shown.Clear();
			_tinted.Clear();
		}

		private void Update(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_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			Vector3 position = ((Component)player).transform.position;
			CraftingStation val = HoveredStation(player);
			StationGraph graph = StationClusters.Graph;
			IReadOnlyList<CraftingStation> stations = StationClusters.Stations;
			if (_clusterActive.Length < graph.ClusterCount)
			{
				_clusterActive = new bool[graph.ClusterCount];
			}
			for (int i = 0; i < graph.ClusterCount; i++)
			{
				_clusterActive[i] = false;
			}
			for (int j = 0; j < graph.Count; j++)
			{
				if (graph.Covers(j, position.x, position.z))
				{
					_clusterActive[graph.ClusterOf(j)] = true;
				}
			}
			_next.Clear();
			for (int k = 0; k < graph.Count; k++)
			{
				int num = graph.ClusterOf(k);
				if (_clusterActive[num] && (Object)(object)stations[k].m_areaMarker != (Object)null)
				{
					_next.Add(stations[k]);
					Show(stations[k], graph.ClusterSize(num) > 1, (Object)(object)stations[k] == (Object)(object)val);
				}
			}
			if ((Object)(object)val != (Object)null && (Object)(object)val.m_areaMarker != (Object)null && !_next.Contains(val))
			{
				val.GetStationBuildRange();
				_next.Add(val);
				Show(val, linked: false, hovered: true);
			}
			foreach (CraftingStation item in _shown)
			{
				if (!_next.Contains(item))
				{
					Hide(item);
				}
			}
			_shown.Clear();
			_shown.AddRange(_next);
		}

		private static CraftingStation HoveredStation(Player player)
		{
			Piece hoveringPiece = player.GetHoveringPiece();
			if (!((Object)(object)hoveringPiece == (Object)null))
			{
				return ((Component)hoveringPiece).GetComponentInChildren<CraftingStation>();
			}
			return null;
		}

		private void Show(CraftingStation station, bool linked, bool hovered)
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			station.m_areaMarker.SetActive(true);
			((MonoBehaviour)station).CancelInvoke("HideMarker");
			if (hovered)
			{
				Tint(station, HoverTint);
			}
			else if (linked)
			{
				Tint(station, LinkedTint);
			}
			else
			{
				Untint(station);
			}
		}

		private void Hide(CraftingStation station)
		{
			if (!((Object)(object)station == (Object)null))
			{
				Untint(station);
				if ((Object)(object)station.m_areaMarker != (Object)null)
				{
					station.m_areaMarker.SetActive(false);
				}
			}
		}

		private void Tint(CraftingStation station, Color tint)
		{
			//IL_0023: 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_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			Renderer[] array = RenderersOf(station);
			foreach (Renderer val in array)
			{
				int num = ColorId(val);
				if (num >= 0)
				{
					float a = val.sharedMaterial.GetColor(num).a;
					_block.Clear();
					_block.SetColor(num, new Color(tint.r, tint.g, tint.b, a));
					val.SetPropertyBlock(_block);
				}
			}
			_tinted.Add(station);
		}

		private void Untint(CraftingStation station)
		{
			if (_tinted.Remove(station))
			{
				Renderer[] array = RenderersOf(station);
				for (int i = 0; i < array.Length; i++)
				{
					array[i].SetPropertyBlock((MaterialPropertyBlock)null);
				}
			}
		}

		private int ColorId(Renderer renderer)
		{
			Material sharedMaterial = renderer.sharedMaterial;
			if ((Object)(object)sharedMaterial == (Object)null)
			{
				return -1;
			}
			if (_colorIds == null)
			{
				_colorIds = new int[ColorProperties.Length];
				for (int i = 0; i < ColorProperties.Length; i++)
				{
					_colorIds[i] = Shader.PropertyToID(ColorProperties[i]);
				}
			}
			int[] colorIds = _colorIds;
			foreach (int num in colorIds)
			{
				if (sharedMaterial.HasProperty(num))
				{
					return num;
				}
			}
			return -1;
		}

		private Renderer[] RenderersOf(CraftingStation station)
		{
			Transform transform = station.m_areaMarker.transform;
			if (!_cache.TryGetValue(station, out var value) || value.ChildCount != transform.childCount)
			{
				value = new MarkerRenderers(((Component)transform).GetComponentsInChildren<Renderer>(true), transform.childCount);
				_cache[station] = value;
			}
			return value.Items;
		}
	}
	internal static class StationClusters
	{
		private const float RefreshInterval = 1f;

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

		private static readonly List<CraftingStation> StationList = new List<CraftingStation>();

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

		private static readonly List<string> ChangedNames = new List<string>();

		private static StationGraph _graph;

		private static float _builtAt;

		private static float _builtSlack;

		private static int _builtVersion = -1;

		public static int Version { get; private set; }

		public static IReadOnlyList<CraftingStation> Stations
		{
			get
			{
				Refresh();
				return StationList;
			}
		}

		public static StationGraph Graph
		{
			get
			{
				Refresh();
				return _graph;
			}
		}

		public static void Poll()
		{
			Refresh();
		}

		public static void Invalidate()
		{
			Version++;
		}

		public static CraftingStation FindLinked(string name, Vector3 point)
		{
			//IL_001b: 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)
			Refresh();
			if (!TypeIds.TryGetValue(name, out var value))
			{
				return null;
			}
			int num = _graph.FindLinked(point.x, point.z, value);
			if (num >= 0)
			{
				return StationList[num];
			}
			return null;
		}

		public static CraftingStation PreferLinked(CraftingStation found, Vector3 point)
		{
			//IL_0037: 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)
			Refresh();
			if (!_graph.HasDuplicates)
			{
				return found;
			}
			int num = StationList.IndexOf(found);
			if (num < 0 || !_graph.IsDuplicate(num))
			{
				return found;
			}
			int num2 = _graph.FindPrimaryCovering(point.x, point.z, _graph[num].Type);
			if (num2 >= 0)
			{
				return StationList[num2];
			}
			return found;
		}

		public static int IndexOf(CraftingStation station)
		{
			Refresh();
			return StationList.IndexOf(station);
		}

		public static bool TryTakeChanged(out string names)
		{
			names = string.Join(", ", ChangedNames);
			ChangedNames.Clear();
			return names.Length > 0;
		}

		public static string NameOf(int type)
		{
			foreach (KeyValuePair<string, int> typeId in TypeIds)
			{
				if (typeId.Value == type)
				{
					return typeId.Key;
				}
			}
			return string.Empty;
		}

		private static void Refresh()
		{
			float num = Time.time - _builtAt;
			if (_graph == null || _builtVersion != Version || _builtSlack != BenchLinkConfig.LinkSlack.Value || !(num >= 0f) || !(num < 1f))
			{
				Rebuild();
			}
		}

		private static void Rebuild()
		{
			//IL_0074: 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_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			StationList.Clear();
			foreach (CraftingStation allStation in CraftingStation.m_allStations)
			{
				if ((Object)(object)allStation != (Object)null)
				{
					StationList.Add(allStation);
				}
			}
			StationNode[] array = new StationNode[StationList.Count];
			for (int i = 0; i < array.Length; i++)
			{
				CraftingStation val = StationList[i];
				Vector3 position = ((Component)val).transform.position;
				array[i] = new StationNode(position.x, position.z, val.GetStationBuildRange(), TypeOf(val.m_name), BuiltOrder(val));
			}
			_builtSlack = BenchLinkConfig.LinkSlack.Value;
			_graph = new StationGraph(array, _builtSlack);
			RecordChanges();
			_builtAt = Time.time;
			_builtVersion = Version;
		}

		private static void RecordChanges()
		{
			//IL_004a: 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_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			Player localPlayer = Player.m_localPlayer;
			for (int i = 0; i < StationList.Count; i++)
			{
				bool flag = _graph.IsDuplicate(i);
				if ((Object)(object)localPlayer != (Object)null && WasDuplicate.TryGetValue(((Object)StationList[i]).GetInstanceID(), out var value) && value != flag)
				{
					Vector3 position = ((Component)localPlayer).transform.position;
					string name = StationList[i].m_name;
					if (_graph.Covers(i, position.x, position.z) && !ChangedNames.Contains(name))
					{
						ChangedNames.Add(name);
					}
				}
			}
			WasDuplicate.Clear();
			for (int j = 0; j < StationList.Count; j++)
			{
				WasDuplicate[((Object)StationList[j]).GetInstanceID()] = _graph.IsDuplicate(j);
			}
		}

		private static long BuiltOrder(CraftingStation station)
		{
			ZNetView component = ((Component)station).GetComponent<ZNetView>();
			if (!((Object)(object)component != (Object)null) || !component.IsValid())
			{
				return long.MaxValue;
			}
			return ((ZDOID)(ref component.GetZDO().m_uid)).ID;
		}

		private static int TypeOf(string name)
		{
			if (!TypeIds.TryGetValue(name, out var value))
			{
				value = TypeIds.Count;
				TypeIds[name] = value;
			}
			return value;
		}
	}
	internal readonly struct StationNode
	{
		public float X { get; }

		public float Z { get; }

		public float Range { get; }

		public int Type { get; }

		public long Order { get; }

		public StationNode(float x, float z, float range, int type, long order = 0L)
		{
			X = x;
			Z = z;
			Range = range;
			Type = type;
			Order = order;
		}
	}
	internal sealed class StationGraph
	{
		private readonly StationNode[] _nodes;

		private readonly int[] _cluster;

		private readonly int[] _stamp;

		private readonly bool[] _duplicate;

		private int _stampId;

		public int Count => _nodes.Length;

		public int ClusterCount { get; }

		public StationNode this[int index] => _nodes[index];

		public bool HasDuplicates
		{
			get
			{
				bool[] duplicate = _duplicate;
				for (int i = 0; i < duplicate.Length; i++)
				{
					if (duplicate[i])
					{
						return true;
					}
				}
				return false;
			}
		}

		public StationGraph(StationNode[] nodes, float linkSlack)
		{
			_nodes = nodes;
			_cluster = new int[nodes.Length];
			_stamp = new int[nodes.Length];
			_duplicate = new bool[nodes.Length];
			ClusterCount = BuildClusters(linkSlack);
		}

		public int ClusterOf(int index)
		{
			return _cluster[index];
		}

		public bool IsDuplicate(int index)
		{
			return _duplicate[index];
		}

		public int FindPrimaryCovering(float x, float z, int type)
		{
			int result = -1;
			float num = float.MaxValue;
			for (int i = 0; i < _nodes.Length; i++)
			{
				if (_nodes[i].Type == type && !_duplicate[i] && Covers(i, x, z))
				{
					float num2 = Distance(_nodes[i].X, _nodes[i].Z, x, z);
					if (num2 < num)
					{
						result = i;
						num = num2;
					}
				}
			}
			return result;
		}

		public int ClusterSize(int cluster)
		{
			int num = 0;
			for (int i = 0; i < _cluster.Length; i++)
			{
				if (_cluster[i] == cluster)
				{
					num++;
				}
			}
			return num;
		}

		public static float Distance(in StationNode a, in StationNode b)
		{
			return Distance(a.X, a.Z, b.X, b.Z);
		}

		public static bool AreLinked(in StationNode a, in StationNode b, float linkSlack)
		{
			return Distance(in a, in b) < Math.Max(a.Range, b.Range) + linkSlack;
		}

		public bool Covers(int index, float x, float z)
		{
			return Distance(_nodes[index].X, _nodes[index].Z, x, z) < _nodes[index].Range;
		}

		public int FindLinked(float x, float z, int type)
		{
			_stampId++;
			bool flag = false;
			for (int i = 0; i < _nodes.Length; i++)
			{
				if (Covers(i, x, z))
				{
					_stamp[_cluster[i]] = _stampId;
					flag = true;
				}
			}
			if (!flag)
			{
				return -1;
			}
			int result = -1;
			float num = float.MaxValue;
			for (int j = 0; j < _nodes.Length; j++)
			{
				if (_nodes[j].Type == type && _stamp[_cluster[j]] == _stampId)
				{
					float num2 = Distance(_nodes[j].X, _nodes[j].Z, x, z);
					if (num2 < num)
					{
						result = j;
						num = num2;
					}
				}
			}
			return result;
		}

		public bool TypeCoversVanilla(int cluster, int type, float x, float z)
		{
			for (int i = 0; i < _nodes.Length; i++)
			{
				if (_cluster[i] == cluster && _nodes[i].Type == type && Covers(i, x, z))
				{
					return true;
				}
			}
			return false;
		}

		public bool ClusterCovers(int cluster, float x, float z)
		{
			for (int i = 0; i < _nodes.Length; i++)
			{
				if (_cluster[i] == cluster && Covers(i, x, z))
				{
					return true;
				}
			}
			return false;
		}

		public (double vanilla, double linked) EstimateAreas(int cluster, int maxCells)
		{
			float num = float.MaxValue;
			float num2 = float.MinValue;
			float num3 = float.MaxValue;
			float num4 = float.MinValue;
			for (int i = 0; i < _nodes.Length; i++)
			{
				if (_cluster[i] == cluster)
				{
					num = Math.Min(num, _nodes[i].X - _nodes[i].Range);
					num2 = Math.Max(num2, _nodes[i].X + _nodes[i].Range);
					num3 = Math.Min(num3, _nodes[i].Z - _nodes[i].Range);
					num4 = Math.Max(num4, _nodes[i].Z + _nodes[i].Range);
				}
			}
			float num5 = 1f;
			while ((num2 - num) / num5 * ((num4 - num3) / num5) > (float)maxCells)
			{
				num5 *= 2f;
			}
			int[] types = TypesIn(cluster);
			long num6 = 0L;
			long num7 = 0L;
			for (float num8 = num + num5 / 2f; num8 < num2; num8 += num5)
			{
				for (float num9 = num3 + num5 / 2f; num9 < num4; num9 += num5)
				{
					if (ClusterCovers(cluster, num8, num9))
					{
						num7++;
						if (CoversEveryType(cluster, types, num8, num9))
						{
							num6++;
						}
					}
				}
			}
			double num10 = (double)num5 * (double)num5;
			return (vanilla: (double)num6 * num10, linked: (double)num7 * num10);
		}

		public static double LensArea(double r1, double r2, double d)
		{
			if (d >= r1 + r2)
			{
				return 0.0;
			}
			if (d <= Math.Abs(r1 - r2))
			{
				double num = Math.Min(r1, r2);
				return Math.PI * num * num;
			}
			double num2 = r1 * r1 * Math.Acos((d * d + r1 * r1 - r2 * r2) / (2.0 * d * r1));
			double num3 = r2 * r2 * Math.Acos((d * d + r2 * r2 - r1 * r1) / (2.0 * d * r2));
			double num4 = 0.5 * Math.Sqrt((0.0 - d + r1 + r2) * (d + r1 - r2) * (d - r1 + r2) * (d + r1 + r2));
			return num2 + num3 - num4;
		}

		public static double UnionArea(double r1, double r2, double d)
		{
			return Math.PI * r1 * r1 + Math.PI * r2 * r2 - LensArea(r1, r2, d);
		}

		private static float Distance(float ax, float az, float bx, float bz)
		{
			float num = ax - bx;
			float num2 = az - bz;
			return (float)Math.Sqrt(num * num + num2 * num2);
		}

		private bool CoversEveryType(int cluster, int[] types, float x, float z)
		{
			foreach (int type in types)
			{
				if (!TypeCoversVanilla(cluster, type, x, z))
				{
					return false;
				}
			}
			return true;
		}

		private int[] TypesIn(int cluster)
		{
			int[] array = new int[_nodes.Length];
			int num = 0;
			for (int i = 0; i < _nodes.Length; i++)
			{
				if (_cluster[i] == cluster && Array.IndexOf(array, _nodes[i].Type, 0, num) < 0)
				{
					array[num++] = _nodes[i].Type;
				}
			}
			Array.Resize(ref array, num);
			return array;
		}

		private int BuildClusters(float linkSlack)
		{
			int[] array = new int[_nodes.Length];
			for (int i = 0; i < array.Length; i++)
			{
				array[i] = i;
			}
			int[] array2 = new int[_nodes.Length];
			for (int j = 0; j < array2.Length; j++)
			{
				array2[j] = j;
			}
			Array.Sort(array2, ComparePriority);
			bool[] array3 = new bool[_nodes.Length];
			int[] array4 = new int[_nodes.Length];
			float[] rootDistance = new float[_nodes.Length];
			int[] array5 = array2;
			foreach (int num in array5)
			{
				int num2 = CollectLinkedRoots(num, linkSlack, array, array3, array4, rootDistance);
				int num3 = AcceptRoots(num, array, array3, array4, num2);
				if (num2 > 0 && num3 == 0)
				{
					_duplicate[num] = true;
					continue;
				}
				array3[num] = true;
				for (int l = 0; l < num3; l++)
				{
					array[array4[l]] = num;
				}
			}
			int[] array6 = new int[_nodes.Length];
			for (int m = 0; m < array6.Length; m++)
			{
				array6[m] = -1;
			}
			int result = 0;
			for (int n = 0; n < _nodes.Length; n++)
			{
				int num4 = Find(array, n);
				if (array6[num4] < 0)
				{
					array6[num4] = result++;
				}
				_cluster[n] = array6[num4];
			}
			return result;
		}

		private int CollectLinkedRoots(int s, float linkSlack, int[] parent, bool[] admitted, int[] roots, float[] rootDistance)
		{
			int num = 0;
			for (int i = 0; i < _nodes.Length; i++)
			{
				if (admitted[i] && AreLinked(in _nodes[s], in _nodes[i], linkSlack))
				{
					int num2 = Find(parent, i);
					float num3 = Distance(in _nodes[s], in _nodes[i]);
					int num4 = Array.IndexOf(roots, num2, 0, num);
					if (num4 >= 0)
					{
						rootDistance[num4] = Math.Min(rootDistance[num4], num3);
						continue;
					}
					roots[num] = num2;
					rootDistance[num] = num3;
					num++;
				}
			}
			Array.Sort(rootDistance, roots, 0, num);
			return num;
		}

		private int AcceptRoots(int s, int[] parent, bool[] admitted, int[] roots, int rootCount)
		{
			int[] array = new int[_nodes.Length + 1];
			array[0] = _nodes[s].Type;
			int seenCount = 1;
			int result = 0;
			for (int i = 0; i < rootCount; i++)
			{
				if (TypesConflict(roots[i], parent, admitted, array, seenCount))
				{
					continue;
				}
				for (int j = 0; j < _nodes.Length; j++)
				{
					if (admitted[j] && Find(parent, j) == roots[i])
					{
						array[seenCount++] = _nodes[j].Type;
					}
				}
				roots[result++] = roots[i];
			}
			return result;
		}

		private bool TypesConflict(int root, int[] parent, bool[] admitted, int[] seen, int seenCount)
		{
			for (int i = 0; i < _nodes.Length; i++)
			{
				if (admitted[i] && Find(parent, i) == root && Array.IndexOf(seen, _nodes[i].Type, 0, seenCount) >= 0)
				{
					return true;
				}
			}
			return false;
		}

		private int ComparePriority(int a, int b)
		{
			int num = _nodes[b].Range.CompareTo(_nodes[a].Range);
			if (num != 0)
			{
				return num;
			}
			int num2 = _nodes[a].Order.CompareTo(_nodes[b].Order);
			if (num2 != 0)
			{
				return num2;
			}
			int num3 = _nodes[a].X.CompareTo(_nodes[b].X);
			if (num3 != 0)
			{
				return num3;
			}
			int num4 = _nodes[a].Z.CompareTo(_nodes[b].Z);
			if (num4 == 0)
			{
				return a.CompareTo(b);
			}
			return num4;
		}

		private static int Find(int[] parent, int i)
		{
			while (parent[i] != i)
			{
				parent[i] = parent[parent[i]];
				i = parent[i];
			}
			return i;
		}
	}
	[HarmonyPatch(typeof(CraftingStation), "HaveBuildStationInRange")]
	internal static class HaveBuildStationInRangePatch
	{
		private static void Postfix(string name, Vector3 point, ref CraftingStation __result)
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)__result != (Object)null)
			{
				__result = StationClusters.PreferLinked(__result, point);
			}
			else
			{
				__result = StationClusters.FindLinked(name, point);
			}
		}
	}
	[HarmonyPatch(typeof(CraftingStation), "Start")]
	internal static class StationStartPatch
	{
		private static void Postfix(CraftingStation __instance)
		{
			StationClusters.Invalidate();
			PlacementNotice.Started(__instance);
		}
	}
	[HarmonyPatch(typeof(CraftingStation), "OnDestroy")]
	internal static class StationDestroyPatch
	{
		private static void Postfix()
		{
			StationClusters.Invalidate();
		}
	}
}
namespace System.Runtime.CompilerServices
{
	[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
	internal sealed class IgnoresAccessChecksToAttribute : Attribute
	{
		internal IgnoresAccessChecksToAttribute(string assemblyName)
		{
		}
	}
}