Decompiled source of SmartBuild v1.10.1

SmartBuild.dll

Decompiled 14 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Jotunn.Configs;
using Jotunn.Entities;
using Jotunn.Managers;
using Jotunn.Utils;
using Microsoft.CodeAnalysis;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Controls;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: IgnoresAccessChecksTo("assembly_valheim")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.0.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 SmartBuild
{
	internal readonly struct WorldPiece
	{
		public readonly string Prefab;

		public readonly Vector3 Position;

		public readonly Quaternion Rotation;

		public readonly string SignText;

		public readonly int? DoorState;

		public WorldPiece(string prefab, Vector3 position, Quaternion rotation, string signText = null, int? doorState = null)
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: 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_0010: Unknown result type (might be due to invalid IL or missing references)
			Prefab = prefab;
			Position = position;
			Rotation = rotation;
			SignText = signText;
			DoorState = doorState;
		}
	}
	internal readonly struct PieceBounds
	{
		public readonly Vector3 Min;

		public readonly Vector3 Max;

		public readonly Vector3[] SnapPoints;

		public PieceBounds(Vector3 min, Vector3 max, Vector3[] snapPoints = null)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			Min = min;
			Max = max;
			SnapPoints = snapPoints;
		}
	}
	internal sealed class BlueprintItem
	{
		public readonly string Prefab;

		public readonly Vector3 Position;

		public readonly Quaternion Rotation;

		public readonly string SignText;

		public readonly int? DoorState;

		public BlueprintItem(string prefab, Vector3 position, Quaternion rotation, string signText = null, int? doorState = null)
		{
			//IL_000e: 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_0015: 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)
			Prefab = prefab;
			Position = position;
			Rotation = rotation;
			SignText = signText;
			DoorState = doorState;
		}
	}
	internal sealed class Blueprint
	{
		private readonly List<BlueprintItem> _items;

		private const float SharedSnapDistance = 0.01f;

		private const float DefaultLabelTolerance = 0.05f;

		public string Name { get; }

		public IReadOnlyList<BlueprintItem> Items => _items;

		public Blueprint(string name, IEnumerable<BlueprintItem> items)
		{
			if (string.IsNullOrWhiteSpace(name))
			{
				throw new ArgumentException("A blueprint needs a name.", "name");
			}
			if (name.IndexOf('\n') >= 0 || name.IndexOf('\r') >= 0)
			{
				throw new ArgumentException("A blueprint name is one line.", "name");
			}
			if (items == null)
			{
				throw new ArgumentException("A blueprint needs at least one item.", "items");
			}
			_items = new List<BlueprintItem>(items);
			if (_items.Count == 0)
			{
				throw new ArgumentException("A blueprint needs at least one item.", "items");
			}
			foreach (BlueprintItem item in _items)
			{
				if (item == null || !IsSingleWord(item.Prefab))
				{
					throw new ArgumentException("A piece name is one word with no spaces.", "items");
				}
			}
			Name = name;
		}

		internal static bool IsSingleWord(string text)
		{
			if (string.IsNullOrEmpty(text))
			{
				return false;
			}
			for (int i = 0; i < text.Length; i++)
			{
				if (char.IsWhiteSpace(text[i]))
				{
					return false;
				}
			}
			return true;
		}

		public IEnumerable<string> PrefabsByUsage()
		{
			Dictionary<string, int> counts = new Dictionary<string, int>(StringComparer.Ordinal);
			List<string> list = new List<string>();
			foreach (BlueprintItem item in _items)
			{
				if (!counts.ContainsKey(item.Prefab))
				{
					list.Add(item.Prefab);
				}
				counts.TryGetValue(item.Prefab, out var value);
				counts[item.Prefab] = value + 1;
			}
			return list.OrderByDescending((string name) => counts[name]);
		}

		private static Quaternion Inverse(Quaternion q)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: 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_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)
			//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_0038: 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_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			float num = q.x * q.x + q.y * q.y + q.z * q.z + q.w * q.w;
			return new Quaternion((0f - q.x) / num, (0f - q.y) / num, (0f - q.z) / num, q.w / num);
		}

		public static Blueprint FromWorld(string name, IList<WorldPiece> pieces)
		{
			Vector3 anchorPosition;
			Quaternion anchorRotation;
			return FromWorld(name, pieces, out anchorPosition, out anchorRotation);
		}

		public static Blueprint FromWorld(string name, IList<WorldPiece> pieces, out Vector3 anchorPosition, out Quaternion anchorRotation)
		{
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: 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_0030: 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_0060: 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_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_0110: Unknown result type (might be due to invalid IL or missing references)
			//IL_0115: Unknown result type (might be due to invalid IL or missing references)
			//IL_0117: Unknown result type (might be due to invalid IL or missing references)
			//IL_011c: Unknown result type (might be due to invalid IL or missing references)
			//IL_011f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0124: Unknown result type (might be due to invalid IL or missing references)
			//IL_0153: Unknown result type (might be due to invalid IL or missing references)
			//IL_0158: Unknown result type (might be due to invalid IL or missing references)
			//IL_0159: Unknown result type (might be due to invalid IL or missing references)
			//IL_015b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0160: Unknown result type (might be due to invalid IL or missing references)
			//IL_0165: Unknown result type (might be due to invalid IL or missing references)
			//IL_016b: Unknown result type (might be due to invalid IL or missing references)
			//IL_016c: Unknown result type (might be due to invalid IL or missing references)
			if (pieces == null || pieces.Count == 0)
			{
				throw new ArgumentException("A blueprint needs at least one piece.", "pieces");
			}
			WorldPiece worldPiece = pieces[0];
			Quaternion val = FacingOf(worldPiece.Rotation);
			Quaternion val2 = Inverse(val);
			Vector3[] array = (Vector3[])(object)new Vector3[pieces.Count];
			Vector3 val3 = default(Vector3);
			((Vector3)(ref val3))..ctor(float.MaxValue, float.MaxValue, float.MaxValue);
			for (int i = 0; i < pieces.Count; i++)
			{
				array[i] = val2 * (pieces[i].Position - worldPiece.Position);
				((Vector3)(ref val3))..ctor(Mathf.Min(val3.x, array[i].x), Mathf.Min(val3.y, array[i].y), Mathf.Min(val3.z, array[i].z));
			}
			List<BlueprintItem> list = new List<BlueprintItem>(pieces.Count);
			for (int j = 0; j < pieces.Count; j++)
			{
				WorldPiece worldPiece2 = pieces[j];
				list.Add(new BlueprintItem(worldPiece2.Prefab, array[j] - val3, val2 * worldPiece2.Rotation, worldPiece2.SignText, worldPiece2.DoorState));
			}
			anchorPosition = worldPiece.Position + val * val3;
			anchorRotation = val;
			return new Blueprint(name, list);
		}

		private static Quaternion FacingOf(Quaternion rotation)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: 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_001f: 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_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: 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_0033: 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_0073: Unknown result type (might be due to invalid IL or missing references)
			Vector3 val = rotation * Vector3.forward;
			float degrees;
			if (Mathf.Sqrt(val.x * val.x + val.z * val.z) >= 0.001f)
			{
				degrees = Mathf.Atan2(val.x, val.z) * 57.29578f;
			}
			else
			{
				Vector3 val2 = rotation * Vector3.right;
				degrees = Mathf.Atan2(0f - val2.z, val2.x) * 57.29578f;
			}
			return GridMath.Yaw(degrees);
		}

		public Blueprint Rebased()
		{
			//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)
			List<WorldPiece> list = new List<WorldPiece>(_items.Count);
			foreach (BlueprintItem item in _items)
			{
				list.Add(new WorldPiece(item.Prefab, item.Position, item.Rotation, item.SignText, item.DoorState));
			}
			return FromWorld(Name, list);
		}

		public int[] PlacementOrder()
		{
			int[] array = new int[_items.Count];
			for (int i = 0; i < array.Length; i++)
			{
				array[i] = i;
			}
			Array.Sort(array, delegate(int a, int b)
			{
				//IL_000c: Unknown result type (might be due to invalid IL or missing references)
				//IL_0011: 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_002b: 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)
				Vector3 position = _items[a].Position;
				Vector3 position2 = _items[b].Position;
				int num = position.y.CompareTo(position2.y);
				if (num == 0)
				{
					num = position.x.CompareTo(position2.x);
				}
				if (num == 0)
				{
					num = position.z.CompareTo(position2.z);
				}
				return (num == 0) ? a.CompareTo(b) : num;
			});
			return array;
		}

		public Vector3[] OuterSnapPoints(Func<string, PieceBounds> localBounds)
		{
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			if (localBounds == null)
			{
				throw new ArgumentNullException("localBounds");
			}
			Vector3[] array = PooledSnapPoints(localBounds);
			Dictionary<(long, long, long), List<int>> dictionary = new Dictionary<(long, long, long), List<int>>();
			for (int i = 0; i < array.Length; i++)
			{
				(long, long, long) key = BucketOf(array[i]);
				if (!dictionary.TryGetValue(key, out var value))
				{
					value = (dictionary[key] = new List<int>());
				}
				value.Add(i);
			}
			List<Vector3> list2 = new List<Vector3>();
			for (int j = 0; j < array.Length; j++)
			{
				if (!HasNeighbour(array, dictionary, j))
				{
					list2.Add(array[j]);
				}
			}
			return list2.ToArray();
		}

		private static (long, long, long) BucketOf(Vector3 point)
		{
			//IL_0000: 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_0026: Unknown result type (might be due to invalid IL or missing references)
			return ((long)Math.Floor(point.x / 0.01f), (long)Math.Floor(point.y / 0.01f), (long)Math.Floor(point.z / 0.01f));
		}

		private static bool HasNeighbour(Vector3[] points, Dictionary<(long, long, long), List<int>> buckets, int index)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_007e: Unknown result type (might be due to invalid IL or missing references)
			(long, long, long) tuple = BucketOf(points[index]);
			long item = tuple.Item1;
			long item2 = tuple.Item2;
			long item3 = tuple.Item3;
			for (long num = -1L; num <= 1; num++)
			{
				for (long num2 = -1L; num2 <= 1; num2++)
				{
					for (long num3 = -1L; num3 <= 1; num3++)
					{
						if (!buckets.TryGetValue((item + num, item2 + num2, item3 + num3), out var value))
						{
							continue;
						}
						foreach (int item4 in value)
						{
							if (item4 != index)
							{
								Vector3 val = points[item4] - points[index];
								if (((Vector3)(ref val)).magnitude <= 0.01f)
								{
									return true;
								}
							}
						}
					}
				}
			}
			return false;
		}

		public void PoseOf(int index, Vector3 anchorPosition, Quaternion anchorRotation, out Vector3 position, out Quaternion rotation)
		{
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_0045: 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_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			if (index < 0 || index >= _items.Count)
			{
				throw new ArgumentOutOfRangeException("index");
			}
			BlueprintItem blueprintItem = _items[index];
			position = anchorPosition + anchorRotation * blueprintItem.Position;
			rotation = anchorRotation * blueprintItem.Rotation;
		}

		public Dictionary<string, int> PieceCounts(int copies)
		{
			if (copies < 1)
			{
				throw new ArgumentOutOfRangeException("copies", "A count needs at least one repeat.");
			}
			Dictionary<string, int> dictionary = new Dictionary<string, int>();
			foreach (BlueprintItem item in _items)
			{
				dictionary.TryGetValue(item.Prefab, out var value);
				dictionary[item.Prefab] = value + copies;
			}
			return dictionary;
		}

		public List<string> MissingPieces(Func<string, bool> isAvailable)
		{
			if (isAvailable == null)
			{
				throw new ArgumentNullException("isAvailable");
			}
			List<string> list = new List<string>();
			foreach (BlueprintItem item in _items)
			{
				if (!list.Contains(item.Prefab) && !isAvailable(item.Prefab))
				{
					list.Add(item.Prefab);
				}
			}
			return list;
		}

		public (Vector3 Min, Vector3 Max) LocalBounds(Func<string, PieceBounds> localBounds)
		{
			//IL_018c: 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_0081: 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_00a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dc: 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_00e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0102: 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_0114: Unknown result type (might be due to invalid IL or missing references)
			//IL_0120: Unknown result type (might be due to invalid IL or missing references)
			//IL_0125: Unknown result type (might be due to invalid IL or missing references)
			//IL_0126: Unknown result type (might be due to invalid IL or missing references)
			//IL_012c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0138: Unknown result type (might be due to invalid IL or missing references)
			//IL_013e: Unknown result type (might be due to invalid IL or missing references)
			//IL_014a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0150: Unknown result type (might be due to invalid IL or missing references)
			//IL_015c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0161: Unknown result type (might be due to invalid IL or missing references)
			if (localBounds == null)
			{
				throw new ArgumentNullException("localBounds");
			}
			Vector3 val = default(Vector3);
			((Vector3)(ref val))..ctor(float.MaxValue, float.MaxValue, float.MaxValue);
			Vector3 val2 = default(Vector3);
			((Vector3)(ref val2))..ctor(float.MinValue, float.MinValue, float.MinValue);
			Vector3 val3 = default(Vector3);
			foreach (BlueprintItem item in _items)
			{
				PieceBounds pieceBounds = localBounds(item.Prefab);
				for (int i = 0; i < 8; i++)
				{
					((Vector3)(ref val3))..ctor(((i & 1) == 0) ? pieceBounds.Min.x : pieceBounds.Max.x, ((i & 2) == 0) ? pieceBounds.Min.y : pieceBounds.Max.y, ((i & 4) == 0) ? pieceBounds.Min.z : pieceBounds.Max.z);
					Vector3 val4 = item.Position + item.Rotation * val3;
					val = new Vector3(Mathf.Min(val.x, val4.x), Mathf.Min(val.y, val4.y), Mathf.Min(val.z, val4.z));
					val2 = new Vector3(Mathf.Max(val2.x, val4.x), Mathf.Max(val2.y, val4.y), Mathf.Max(val2.z, val4.z));
				}
			}
			return (Min: val, Max: val2);
		}

		public Vector3 Extent(Func<string, PieceBounds> localBounds)
		{
			//IL_0008: 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)
			//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_0014: Unknown result type (might be due to invalid IL or missing references)
			(Vector3 Min, Vector3 Max) tuple = LocalBounds(localBounds);
			var (val, _) = tuple;
			return tuple.Max - val;
		}

		public static string SnapPointLabel(Vector3 point, Vector3 min, Vector3 max, float tolerance = 0.05f)
		{
			//IL_0007: 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)
			//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_0049: 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_006b: 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)
			//IL_008b: 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_00ad: 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)
			List<string> list = new List<string>(3);
			if (Mathf.Abs(point.y - min.y) <= tolerance)
			{
				list.Add("bottom");
			}
			else if (Mathf.Abs(point.y - max.y) <= tolerance)
			{
				list.Add("top");
			}
			if (Mathf.Abs(point.z - min.z) <= tolerance)
			{
				list.Add("front");
			}
			else if (Mathf.Abs(point.z - max.z) <= tolerance)
			{
				list.Add("back");
			}
			if (Mathf.Abs(point.x - min.x) <= tolerance)
			{
				list.Add("left");
			}
			else if (Mathf.Abs(point.x - max.x) <= tolerance)
			{
				list.Add("right");
			}
			if (list.Count <= 0)
			{
				return "snappoint";
			}
			return string.Join("-", list);
		}

		public Steps FootprintSteps(Func<string, PieceBounds> localBounds)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: 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_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: 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_0072: 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_008b: 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)
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
			Vector3 val = Extent(localBounds);
			Vector3[] originPoints = OriginSnapPoints(localBounds);
			Vector3[] pooledPoints = PooledSnapPoints(localBounds);
			return new Steps
			{
				X = (Vector3)(((??)ProjectedSpan(originPoints, pooledPoints, 0)) ?? new Vector3(val.x, 0f, 0f)),
				Y = (Vector3)(((??)ProjectedSpan(originPoints, pooledPoints, 1)) ?? new Vector3(0f, val.y, 0f)),
				Z = (Vector3)(((??)ProjectedSpan(originPoints, pooledPoints, 2)) ?? new Vector3(0f, 0f, val.z))
			};
		}

		private static Vector3? ProjectedSpan(Vector3[] originPoints, Vector3[] pooledPoints, int axis)
		{
			//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)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: 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_006d: 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)
			Vector3? result = Footprint.SnapSpan(originPoints, axis);
			if (!result.HasValue || pooledPoints.Length == 0)
			{
				return result;
			}
			Vector3 value = result.Value;
			Vector3 normalized = ((Vector3)(ref value)).normalized;
			float num = float.MaxValue;
			float num2 = float.MinValue;
			for (int i = 0; i < pooledPoints.Length; i++)
			{
				float num3 = Vector3.Dot(pooledPoints[i], normalized);
				num = Mathf.Min(num, num3);
				num2 = Mathf.Max(num2, num3);
			}
			return normalized * (num2 - num);
		}

		private Vector3[] OriginSnapPoints(Func<string, PieceBounds> localBounds)
		{
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: 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)
			if (_items.Count == 0)
			{
				return Array.Empty<Vector3>();
			}
			BlueprintItem blueprintItem = _items[0];
			Vector3[] snapPoints = localBounds(blueprintItem.Prefab).SnapPoints;
			if (snapPoints == null)
			{
				return Array.Empty<Vector3>();
			}
			Vector3[] array = (Vector3[])(object)new Vector3[snapPoints.Length];
			for (int i = 0; i < snapPoints.Length; i++)
			{
				array[i] = blueprintItem.Position + blueprintItem.Rotation * snapPoints[i];
			}
			return array;
		}

		private Vector3[] PooledSnapPoints(Func<string, PieceBounds> localBounds)
		{
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			List<Vector3> list = new List<Vector3>();
			foreach (BlueprintItem item in _items)
			{
				Vector3[] snapPoints = localBounds(item.Prefab).SnapPoints;
				if (snapPoints != null)
				{
					Vector3[] array = snapPoints;
					foreach (Vector3 val in array)
					{
						list.Add(item.Position + item.Rotation * val);
					}
				}
			}
			return list.ToArray();
		}
	}
	internal static class BlueprintFile
	{
		public const string Extension = ".blueprint";

		private const string Header = "smartbuild-blueprint";

		private const int Version = 2;

		private const int MaxFileNameStem = 100;

		private static readonly char[] UnsafeFileNameCharacters = new char[9] { '\\', '/', ':', '*', '?', '"', '<', '>', '|' };

		private static readonly string[] ReservedNames = new string[22]
		{
			"CON", "PRN", "AUX", "NUL", "COM1", "COM2", "COM3", "COM4", "COM5", "COM6",
			"COM7", "COM8", "COM9", "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7",
			"LPT8", "LPT9"
		};

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

		public static string Serialize(Blueprint blueprint)
		{
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.Append("smartbuild-blueprint").Append(' ').Append(2)
				.Append('\n');
			stringBuilder.Append("name ").Append(blueprint.Name).Append('\n');
			foreach (BlueprintItem item in blueprint.Items)
			{
				stringBuilder.Append("item ").Append(item.Prefab).Append(' ')
					.Append(Number(item.Position.x))
					.Append(' ')
					.Append(Number(item.Position.y))
					.Append(' ')
					.Append(Number(item.Position.z))
					.Append(' ')
					.Append(Number(item.Rotation.x))
					.Append(' ')
					.Append(Number(item.Rotation.y))
					.Append(' ')
					.Append(Number(item.Rotation.z))
					.Append(' ')
					.Append(Number(item.Rotation.w))
					.Append('\n');
				if (item.SignText != null)
				{
					stringBuilder.Append("sign ").Append(Escape(item.SignText)).Append('\n');
				}
				if (item.DoorState.HasValue)
				{
					stringBuilder.Append("door ").Append(item.DoorState.Value.ToString(CultureInfo.InvariantCulture)).Append('\n');
				}
			}
			return stringBuilder.ToString();
		}

		private static string Escape(string text)
		{
			return text.Replace("\\", "\\\\").Replace("\n", "\\n");
		}

		private static string Unescape(string text)
		{
			StringBuilder stringBuilder = new StringBuilder(text.Length);
			for (int i = 0; i < text.Length; i++)
			{
				if (text[i] == '\\' && i + 1 < text.Length && (text[i + 1] == '\\' || text[i + 1] == 'n'))
				{
					stringBuilder.Append((text[i + 1] == 'n') ? '\n' : '\\');
					i++;
				}
				else
				{
					stringBuilder.Append(text[i]);
				}
			}
			return stringBuilder.ToString();
		}

		public static bool TryParse(string text, string fileName, out Blueprint blueprint, out string error)
		{
			blueprint = null;
			try
			{
				if (FirstLine(text).StartsWith("smartbuild-blueprint", StringComparison.Ordinal))
				{
					return Parse(text, out blueprint, out error);
				}
				string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(fileName ?? "");
				if (string.Equals(Path.GetExtension(fileName ?? ""), ".vbuild", StringComparison.OrdinalIgnoreCase))
				{
					return PlanBuildFile.ParseVBuild(text, fileNameWithoutExtension, out blueprint, out error);
				}
				return PlanBuildFile.ParseBlueprint(text, fileNameWithoutExtension, out blueprint, out error);
			}
			catch (Exception ex)
			{
				blueprint = null;
				error = "Unexpected problem reading the file: " + ex.Message;
				return false;
			}
		}

		private static string FirstLine(string text)
		{
			if (text == null)
			{
				return "";
			}
			string[] array = text.Split(new char[1] { '\n' });
			foreach (string text2 in array)
			{
				if (text2.Trim().Length > 0)
				{
					return text2.Trim();
				}
			}
			return "";
		}

		public static bool TryParse(string text, out Blueprint blueprint, out string error)
		{
			blueprint = null;
			try
			{
				return Parse(text, out blueprint, out error);
			}
			catch (Exception ex)
			{
				blueprint = null;
				error = "Unexpected problem reading the file: " + ex.Message;
				return false;
			}
		}

		private static bool Parse(string text, out Blueprint blueprint, out string error)
		{
			blueprint = null;
			if (string.IsNullOrWhiteSpace(text))
			{
				error = "The file is empty.";
				return false;
			}
			string[] array = text.Split(new char[1] { '\n' });
			int result = 0;
			string text2 = null;
			List<BlueprintItem> list = new List<BlueprintItem>();
			bool flag = false;
			bool flag2 = false;
			for (int i = 0; i < array.Length; i++)
			{
				int num = i + 1;
				string text3 = array[i].TrimEnd(new char[1] { '\r' });
				if (text3.Trim().Length == 0 || text3.TrimStart(Array.Empty<char>()).StartsWith("#", StringComparison.Ordinal))
				{
					continue;
				}
				if (result == 0)
				{
					string[] array2 = text3.Trim().Split(new char[1] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
					if (array2.Length == 0 || array2[0] != "smartbuild-blueprint")
					{
						error = "Line " + num + ": this is not a SmartBuild blueprint file.";
						return false;
					}
					if (array2.Length != 2 || !int.TryParse(array2[1], NumberStyles.Integer, CultureInfo.InvariantCulture, out result))
					{
						error = "Line " + num + ": the file version is missing.";
						return false;
					}
					if (result != 1 && result != 2)
					{
						error = "Line " + num + ": file version " + result + " is not supported (this SmartBuild reads versions 1 and " + 2 + ").";
						return false;
					}
				}
				else if (text3.StartsWith("name ", StringComparison.Ordinal))
				{
					if (text2 != null)
					{
						error = "Line " + num + ": the name is given twice.";
						return false;
					}
					text2 = text3.Substring(5);
				}
				else if (text3.StartsWith("item ", StringComparison.Ordinal))
				{
					if (!TryParseItem(text3, out var item))
					{
						error = "Line " + num + ": a piece line needs a name and 7 finite numbers.";
						return false;
					}
					list.Add(item);
					flag = (flag2 = false);
				}
				else if (result >= 2 && (text3 == "sign" || text3.StartsWith("sign ", StringComparison.Ordinal)))
				{
					if (list.Count == 0)
					{
						error = "Line " + num + ": sign text needs a piece line above it.";
						return false;
					}
					if (flag)
					{
						error = "Line " + num + ": this piece already has sign text.";
						return false;
					}
					list[list.Count - 1] = WithExtras(list[list.Count - 1], Unescape((text3.Length > 5) ? text3.Substring(5) : ""), list[list.Count - 1].DoorState);
					flag = true;
				}
				else
				{
					if (result < 2 || !text3.StartsWith("door ", StringComparison.Ordinal))
					{
						error = "Line " + num + ": not understood.";
						return false;
					}
					if (list.Count == 0)
					{
						error = "Line " + num + ": a door state needs a piece line above it.";
						return false;
					}
					if (flag2)
					{
						error = "Line " + num + ": this piece already has a door state.";
						return false;
					}
					if (!int.TryParse(text3.Substring(5).Trim(), NumberStyles.AllowLeadingSign, CultureInfo.InvariantCulture, out var result2))
					{
						error = "Line " + num + ": a door state is a whole number.";
						return false;
					}
					list[list.Count - 1] = WithExtras(list[list.Count - 1], list[list.Count - 1].SignText, result2);
					flag2 = true;
				}
			}
			if (result == 0)
			{
				error = "The file has no header.";
				return false;
			}
			if (string.IsNullOrWhiteSpace(text2))
			{
				error = "The file has no name.";
				return false;
			}
			if (list.Count == 0)
			{
				error = "The file has no pieces.";
				return false;
			}
			try
			{
				blueprint = new Blueprint(text2, list);
			}
			catch (ArgumentException ex)
			{
				error = ex.Message;
				return false;
			}
			if (result == 1)
			{
				blueprint = blueprint.Rebased();
			}
			error = null;
			return true;
		}

		private static BlueprintItem WithExtras(BlueprintItem item, string signText, int? doorState)
		{
			//IL_0007: 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)
			return new BlueprintItem(item.Prefab, item.Position, item.Rotation, signText, doorState);
		}

		private static bool TryParseItem(string line, out BlueprintItem item)
		{
			//IL_0074: 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)
			item = null;
			string[] array = line.Split(new char[1] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
			if (array.Length != 9)
			{
				return false;
			}
			float[] array2 = new float[7];
			for (int i = 0; i < 7; i++)
			{
				if (!float.TryParse(array[i + 2], NumberStyles.Float, CultureInfo.InvariantCulture, out array2[i]))
				{
					return false;
				}
				if (float.IsNaN(array2[i]) || float.IsInfinity(array2[i]))
				{
					return false;
				}
			}
			item = new BlueprintItem(array[1], new Vector3(array2[0], array2[1], array2[2]), new Quaternion(array2[3], array2[4], array2[5], array2[6]));
			return true;
		}

		public static string FileNameFor(string name)
		{
			string text = name ?? "";
			StringBuilder stringBuilder = new StringBuilder(text.Length);
			string text2 = text;
			foreach (char c in text2)
			{
				stringBuilder.Append((c < ' ' || Array.IndexOf(UnsafeFileNameCharacters, c) >= 0) ? '_' : c);
			}
			text = stringBuilder.ToString();
			if (text.Length > 100)
			{
				text = text.Substring(0, 100);
			}
			text = text.Trim().TrimEnd('.', ' ');
			if (text.Length == 0)
			{
				text = "blueprint";
			}
			if (Array.IndexOf(ReservedNames, text.ToUpperInvariant()) >= 0)
			{
				text += "_";
			}
			return text + ".blueprint";
		}
	}
	internal static class BlueprintLibrary
	{
		private static BlueprintStore _store;

		public static readonly List<Blueprint> All = new List<Blueprint>();

		public static string Folder
		{
			get
			{
				if (_store == null)
				{
					return "";
				}
				return _store.Directory;
			}
		}

		public static void Load()
		{
			_store = new BlueprintStore(Path.Combine(Paths.ConfigPath, "SmartBuild", "Blueprints"));
			All.Clear();
			BlueprintLoadResult blueprintLoadResult = _store.LoadAll();
			All.AddRange(blueprintLoadResult.Blueprints);
			Plugin.Log.LogInfo((object)("Loaded " + All.Count + " blueprint(s) from " + _store.Directory));
			foreach (string problem in blueprintLoadResult.Problems)
			{
				Plugin.Log.LogWarning((object)("Skipped a blueprint file: " + problem));
			}
		}

		public static void Save(Blueprint blueprint, Player player)
		{
			_store.Save(blueprint);
			All.RemoveAll((Blueprint existing) => string.Equals(existing.Name, blueprint.Name, StringComparison.OrdinalIgnoreCase));
			All.Add(blueprint);
			All.Sort((Blueprint a, Blueprint b) => string.Compare(a.Name, b.Name, StringComparison.OrdinalIgnoreCase));
			BlueprintPieces.Register(blueprint, player);
		}

		public static bool Delete(string name)
		{
			bool result = _store.Delete(name);
			All.RemoveAll((Blueprint existing) => string.Equals(existing.Name, name, StringComparison.OrdinalIgnoreCase));
			BlueprintPieces.Unregister(name);
			return result;
		}
	}
	internal static class BlueprintPieces
	{
		private const string PiecePrefix = "SmartBuildBP_";

		private const string Category = "Blueprints";

		private static readonly Dictionary<string, GameObject> StubsByName = new Dictionary<string, GameObject>(StringComparer.OrdinalIgnoreCase);

		private static readonly Dictionary<string, Blueprint> BlueprintsByPrefabName = new Dictionary<string, Blueprint>();

		private static Sprite _blankIcon;

		public static int Version { get; private set; }

		public static IEnumerable<GameObject> Stubs
		{
			get
			{
				foreach (GameObject value in StubsByName.Values)
				{
					if ((Object)(object)value != (Object)null)
					{
						yield return value;
					}
				}
			}
		}

		public static bool TryGetBlueprint(string prefabName, out Blueprint blueprint)
		{
			return BlueprintsByPrefabName.TryGetValue(prefabName ?? "", out blueprint);
		}

		public static void RegisterAll(IEnumerable<Blueprint> blueprints, Player player)
		{
			foreach (Blueprint blueprint in blueprints)
			{
				Register(blueprint, player);
			}
		}

		public static void Register(Blueprint blueprint, Player player)
		{
			try
			{
				if ((Object)(object)ZNetScene.instance == (Object)null || (Object)(object)SmartHammer.Table == (Object)null)
				{
					return;
				}
				string text = PrefabNameFor(blueprint.Name);
				GameObject val = PrefabManager.Instance.GetPrefab(text);
				if ((Object)(object)val == (Object)null)
				{
					val = PrefabManager.Instance.CreateEmptyPrefab(text, false);
					if ((Object)(object)val == (Object)null)
					{
						Plugin.Log.LogError((object)("Could not add '" + blueprint.Name + "' to the build menu: '" + text + "' could not be created"));
						return;
					}
				}
				Decorate(val, blueprint.Name, blueprint.Items.Count + ((blueprint.Items.Count == 1) ? " piece" : " pieces"), blueprint, player);
				PieceManager.Instance.RegisterPieceInPieceTable(val, "SmartHammerPieceTable", "Blueprints");
				StubsByName[blueprint.Name] = val;
				BlueprintsByPrefabName[text] = blueprint;
				Version++;
				Plugin.Log.LogInfo((object)("Registered blueprint piece '" + text + "' for '" + blueprint.Name + "' (version " + Version + ", " + StubsByName.Count + " stub(s) known)"));
				SmartHammer.Sync();
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("Could not add '" + blueprint.Name + "' to the build menu: " + ex));
			}
		}

		public static void Unregister(string name)
		{
			try
			{
				if (!StubsByName.TryGetValue(name, out var value))
				{
					return;
				}
				StubsByName.Remove(name);
				if (!((Object)(object)value == (Object)null))
				{
					string name2 = ((Object)value).name;
					BlueprintsByPrefabName.Remove(name2);
					if ((Object)(object)SmartHammer.Table != (Object)null)
					{
						SmartHammer.Table.m_pieces.Remove(value);
					}
					Version++;
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("Could not remove '" + name + "' from the build menu: " + ex));
			}
		}

		private static void Decorate(GameObject stub, string name, string description, Blueprint blueprint, Player player)
		{
			for (int num = stub.transform.childCount - 1; num >= 0; num--)
			{
				Object.DestroyImmediate((Object)(object)((Component)stub.transform.GetChild(num)).gameObject);
			}
			Piece val = stub.GetComponent<Piece>();
			if ((Object)(object)val == (Object)null)
			{
				val = stub.AddComponent<Piece>();
			}
			val.m_name = name;
			val.m_description = description;
			val.m_icon = IconFor(blueprint);
			val.m_resources = Array.Empty<Requirement>();
			BuildChildren(stub, blueprint);
			Dictionary<string, PieceBounds> bounds = null;
			if ((Object)(object)player != (Object)null)
			{
				BlueprintPlacer.TryMeasure(player, blueprint, out var _, out bounds, out var _);
			}
			BuildAnchorCollider(stub, blueprint, bounds);
			if (bounds != null)
			{
				BuildSnapPoints(stub, blueprint, bounds);
			}
		}

		private static void BuildAnchorCollider(GameObject stub, Blueprint blueprint, Dictionary<string, PieceBounds> bounds)
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Expected O, but got Unknown
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: 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_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: 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_00ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject("SmartBuildAnchor")
			{
				layer = LayerMask.NameToLayer("piece_nonsolid")
			};
			val.transform.SetParent(stub.transform, false);
			if (bounds != null)
			{
				PieceBounds value;
				(Vector3 Min, Vector3 Max) tuple = blueprint.LocalBounds((string prefab) => (!bounds.TryGetValue(prefab, out value)) ? default(PieceBounds) : value);
				Vector3 item = tuple.Min;
				Vector3 item2 = tuple.Max;
				Vector3 val2 = item2 - item;
				((Vector3)(ref val2))..ctor(Mathf.Max(val2.x, 0.1f), Mathf.Max(val2.y, 0.1f), Mathf.Max(val2.z, 0.1f));
				BoxCollider obj = val.AddComponent<BoxCollider>();
				obj.center = (item + item2) * 0.5f;
				obj.size = val2;
			}
			else
			{
				val.AddComponent<SphereCollider>().radius = 0.05f;
			}
		}

		private static void BuildChildren(GameObject stub, Blueprint blueprint)
		{
			bool forceDisableInit = ZNetView.m_forceDisableInit;
			bool forceDisableTerrainOps = TerrainOp.m_forceDisableTerrainOps;
			ZNetView.m_forceDisableInit = true;
			TerrainOp.m_forceDisableTerrainOps = true;
			try
			{
				BuildChildrenCore(stub, blueprint);
			}
			finally
			{
				ZNetView.m_forceDisableInit = forceDisableInit;
				TerrainOp.m_forceDisableTerrainOps = forceDisableTerrainOps;
			}
		}

		private static void BuildChildrenCore(GameObject stub, Blueprint blueprint)
		{
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			foreach (BlueprintItem item in blueprint.Items)
			{
				GameObject prefab = ZNetScene.instance.GetPrefab(item.Prefab);
				if (!((Object)(object)prefab == (Object)null))
				{
					GameObject obj = Object.Instantiate<GameObject>(prefab, stub.transform);
					obj.transform.localPosition = item.Position;
					obj.transform.localRotation = item.Rotation;
					StripToVisual(obj);
				}
			}
		}

		private static void BuildSnapPoints(GameObject stub, Blueprint blueprint, Dictionary<string, PieceBounds> bounds)
		{
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: 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_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: 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_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			PieceBounds value;
			Func<string, PieceBounds> localBounds = (string prefab) => (!bounds.TryGetValue(prefab, out value)) ? default(PieceBounds) : value;
			(Vector3 Min, Vector3 Max) tuple = blueprint.LocalBounds(localBounds);
			Vector3 item = tuple.Min;
			Vector3 item2 = tuple.Max;
			Vector3[] array = blueprint.OuterSnapPoints(localBounds);
			foreach (Vector3 val in array)
			{
				GameObject val2 = new GameObject(Blueprint.SnapPointLabel(val, item, item2))
				{
					tag = "snappoint",
					layer = LayerMask.NameToLayer("piece")
				};
				val2.transform.SetParent(stub.transform, false);
				val2.transform.localPosition = val;
			}
		}

		private static void StripToVisual(GameObject clone)
		{
			MonoBehaviour[] componentsInChildren = clone.GetComponentsInChildren<MonoBehaviour>(true);
			foreach (MonoBehaviour val in componentsInChildren)
			{
				if ((Object)(object)val != (Object)null)
				{
					Object.DestroyImmediate((Object)(object)val);
				}
			}
			Collider[] componentsInChildren2 = clone.GetComponentsInChildren<Collider>(true);
			for (int i = 0; i < componentsInChildren2.Length; i++)
			{
				Object.DestroyImmediate((Object)(object)componentsInChildren2[i]);
			}
		}

		private static Sprite IconFor(Blueprint blueprint)
		{
			foreach (string item in blueprint.PrefabsByUsage())
			{
				GameObject val = (((Object)(object)ZNetScene.instance != (Object)null) ? ZNetScene.instance.GetPrefab(item) : null);
				Piece val2 = (((Object)(object)val != (Object)null) ? val.GetComponent<Piece>() : null);
				if ((Object)(object)val2 != (Object)null && (Object)(object)val2.m_icon != (Object)null)
				{
					return val2.m_icon;
				}
			}
			return BlankIcon();
		}

		private static Sprite BlankIcon()
		{
			//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_001f: 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_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Expected O, but got Unknown
			if ((Object)(object)_blankIcon != (Object)null)
			{
				return _blankIcon;
			}
			Texture2D val = new Texture2D(1, 1, (TextureFormat)4, false);
			val.SetPixel(0, 0, Color.clear);
			val.Apply();
			_blankIcon = Sprite.Create(val, new Rect(0f, 0f, 1f, 1f), new Vector2(0.5f, 0.5f));
			return _blankIcon;
		}

		private static string PrefabNameFor(string blueprintName)
		{
			StringBuilder stringBuilder = new StringBuilder("SmartBuildBP_".Length + blueprintName.Length);
			stringBuilder.Append("SmartBuildBP_");
			foreach (char c in blueprintName)
			{
				stringBuilder.Append(char.IsLetterOrDigit(c) ? c : '_');
			}
			return stringBuilder.ToString();
		}
	}
	internal static class BlueprintPlacer
	{
		private const int ValidatePerFrame = 50;

		public static Blueprint Active { get; private set; }

		public static void Arm(Blueprint blueprint)
		{
			Active = blueprint;
		}

		public static void Disarm()
		{
			Active = null;
		}

		public static bool TryMeasure(Player player, Blueprint blueprint, out Dictionary<string, Piece> pieces, out Dictionary<string, PieceBounds> bounds, out string missing)
		{
			//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
			pieces = new Dictionary<string, Piece>();
			bounds = new Dictionary<string, PieceBounds>();
			missing = null;
			foreach (BlueprintItem item in blueprint.Items)
			{
				if (!pieces.ContainsKey(item.Prefab))
				{
					GameObject val = (((Object)(object)ZNetScene.instance != (Object)null) ? ZNetScene.instance.GetPrefab(item.Prefab) : null);
					Piece val2 = (((Object)(object)val != (Object)null) ? val.GetComponent<Piece>() : null);
					if ((Object)(object)val2 == (Object)null)
					{
						missing = item.Prefab;
						return false;
					}
					pieces[item.Prefab] = val2;
					GameObject val3 = GameApi.CloneGhost(val, player);
					try
					{
						Footprint footprint = Footprint.Measure(val3);
						bounds[item.Prefab] = new PieceBounds(new Vector3(footprint.MinX, footprint.MinY, footprint.MinZ), new Vector3(footprint.MaxX, footprint.MaxY, footprint.MaxZ), footprint.Snaps);
					}
					finally
					{
						Object.Destroy((Object)(object)val3);
					}
				}
			}
			return true;
		}

		public static GridLayout RepeatsFor(Blueprint blueprint, Dictionary<string, PieceBounds> bounds)
		{
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			PieceBounds value;
			Func<string, PieceBounds> localBounds = (string prefab) => (!bounds.TryGetValue(prefab, out value)) ? default(PieceBounds) : value;
			Steps steps = blueprint.FootprintSteps(localBounds);
			var (val, val2) = blueprint.LocalBounds(localBounds);
			return GridState.LayoutFor(new Footprint
			{
				SpanX = steps.X,
				SpanY = steps.Y,
				SpanZ = steps.Z,
				MinX = val.x,
				MaxX = val2.x,
				MinZ = val.z,
				MaxZ = val2.z
			});
		}

		public static Quaternion AnchorRotation(GameObject ghost)
		{
			//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_0022: Unknown result type (might be due to invalid IL or missing references)
			Quaternion rotation = ghost.transform.rotation;
			return Quaternion.Euler(0f, ((Quaternion)(ref rotation)).eulerAngles.y, 0f);
		}

		public static bool Begin(Player player, GameObject ghost)
		{
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: 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)
			//IL_0034: 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)
			Blueprint active = Active;
			if (active == null)
			{
				return false;
			}
			if (!PlacementHistory.TryStart(player, "Still placing the previous blueprint"))
			{
				return true;
			}
			Vector3 position = ghost.transform.position;
			Quaternion rot = AnchorRotation(ghost);
			((MonoBehaviour)Plugin.Instance).StartCoroutine(Guard(Run(player, active, position, rot, GridState.FollowTerrain), player));
			return true;
		}

		private static IEnumerator Guard(IEnumerator inner, Player player)
		{
			try
			{
				while (true)
				{
					bool flag;
					try
					{
						flag = inner.MoveNext();
					}
					catch (Exception ex)
					{
						Plugin.LogOnce("blueprint-place", "Blueprint placement failed: " + ex);
						GameApi.Message(player, "Blueprint placement failed, see the BepInEx log");
						flag = false;
					}
					if (flag)
					{
						yield return inner.Current;
						continue;
					}
					break;
				}
			}
			finally
			{
				PlacementHistory.Finish();
			}
		}

		private static IEnumerator Run(Player player, Blueprint blueprint, Vector3 anchor, Quaternion rot, bool followTerrain)
		{
			//IL_0015: 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_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)
			if (!TryMeasure(player, blueprint, out var pieces, out var bounds, out var missing))
			{
				GameApi.Message(player, "Missing piece: " + missing + " (this blueprint needs a mod that isn't loaded)");
				yield break;
			}
			Dictionary<string, GameObject> probes = new Dictionary<string, GameObject>();
			try
			{
				foreach (string key in pieces.Keys)
				{
					GameObject val = GameApi.CloneGhost(ZNetScene.instance.GetPrefab(key), player);
					((Object)val).name = "SmartBuildBlueprintProbe";
					val.SetActive(true);
					probes[key] = val;
				}
				GridLayout repeats = RepeatsFor(blueprint, bounds);
				int copies = repeats.Count;
				int itemsPerCopy = blueprint.Items.Count;
				int total = copies * itemsPerCopy;
				Dictionary<string, int> perCopyCounts = blueprint.PieceCounts(1);
				int[] order = blueprint.PlacementOrder();
				float? anchorGround = (followTerrain ? GameApi.GroundHeight(anchor) : ((float?)null));
				int blocked = 0;
				int checkedCount = 0;
				for (int r = 0; r < copies; r++)
				{
					repeats.Pose(anchor, rot, r, out var copyPos, out var copyRot);
					float shift = (anchorGround.HasValue ? GameApi.TerrainShift(anchorGround.Value, copyPos) : 0f);
					for (int oi = 0; oi < itemsPerCopy; oi++)
					{
						int index = order[oi];
						string prefab = blueprint.Items[index].Prefab;
						blueprint.PoseOf(index, copyPos, copyRot, out var position, out var rotation);
						position.y += shift;
						GameObject val2 = probes[prefab];
						val2.transform.SetPositionAndRotation(position, rotation);
						Physics.SyncTransforms();
						if (!CellValidator.IsValid(player, pieces[prefab], val2, position))
						{
							blocked++;
						}
						checkedCount++;
						if (checkedCount % 50 == 49)
						{
							yield return null;
						}
						if ((Object)(object)player == (Object)null)
						{
							yield break;
						}
					}
					copyPos = default(Vector3);
					copyRot = default(Quaternion);
				}
				if (blocked > 0)
				{
					GameApi.Message(player, "Blocked: " + blocked + " of " + total + " pieces can't be placed here");
					yield break;
				}
				foreach (KeyValuePair<string, int> item in blueprint.PieceCounts(copies))
				{
					if (!GridCost.CanAfford(player, pieces[item.Key], item.Value))
					{
						GameApi.Message(player, GridCost.Shortage(player, pieces[item.Key], item.Value));
						yield break;
					}
				}
				GridState.Reset();
				PlacementHistory.BeginCapture(DateTime.UtcNow.Ticks);
				bool cheated = player.NoCostCheat();
				bool anyCharged = false;
				string stopped = null;
				int placedSinceYield = 0;
				int perFrame = ModConfig.PlacePerFrame.Value;
				for (int r = 0; r < copies; r++)
				{
					if (stopped != null)
					{
						break;
					}
					if ((Object)(object)player == (Object)null || GameApi.IsDead(player))
					{
						stopped = "Stopped after " + PlacementHistory.Captured.Count + " pieces";
						break;
					}
					repeats.Pose(anchor, rot, r, out var position2, out var cellRotation);
					foreach (KeyValuePair<string, int> item2 in perCopyCounts)
					{
						if (!GridCost.CanAfford(player, pieces[item2.Key], item2.Value))
						{
							stopped = "Stopped after " + PlacementHistory.Captured.Count + " pieces: " + GridCost.Shortage(player, pieces[item2.Key], item2.Value);
							break;
						}
					}
					if (stopped != null)
					{
						break;
					}
					foreach (KeyValuePair<string, int> item3 in perCopyCounts)
					{
						if (!GameApi.IsFree(player, pieces[item3.Key]))
						{
							GameApi.ConsumeResources(player, pieces[item3.Key], item3.Value);
							anyCharged = true;
						}
					}
					float num = (anchorGround.HasValue ? GameApi.TerrainShift(anchorGround.Value, position2) : 0f);
					PlacementHistory.EnterCaptureWindow();
					try
					{
						for (int i = 0; i < itemsPerCopy; i++)
						{
							int index2 = order[i];
							string prefab2 = blueprint.Items[index2].Prefab;
							blueprint.PoseOf(index2, position2, cellRotation, out var position3, out var rotation2);
							position3.y += num;
							GameApi.PlacePiece(player, pieces[prefab2], position3, rotation2, r == 0 && i == 0, cheated);
							placedSinceYield++;
						}
					}
					finally
					{
						PlacementHistory.ExitCaptureWindow();
					}
					if (placedSinceYield >= perFrame)
					{
						placedSinceYield = 0;
						yield return null;
					}
				}
				int count = PlacementHistory.Captured.Count;
				PlacementHistory.EndCapture(anyCharged);
				GameApi.Message(player, stopped ?? ("Placed " + copies + "x '" + blueprint.Name + "' (" + count + " pieces) - still armed, click to place another"));
			}
			finally
			{
				foreach (GameObject value in probes.Values)
				{
					if ((Object)(object)value != (Object)null)
					{
						Object.Destroy((Object)(object)value);
					}
				}
			}
		}
	}
	internal class BlueprintSavePrompt : TextReceiver
	{
		private static readonly BlueprintSavePrompt Instance = new BlueprintSavePrompt();

		private static string _lastText = "";

		public static void Show(Player player)
		{
			if ((Object)(object)TextInput.instance == (Object)null)
			{
				GameApi.Message(player, "Could not open the naming prompt, see the BepInEx log");
				Plugin.Log.LogError((object)"Could not save a blueprint: TextInput.instance is null");
				return;
			}
			Safe.Run(delegate
			{
				GUI.FocusControl((string)null);
			}, "GUI.FocusControl");
			TextInput.instance.RequestText((TextReceiver)(object)Instance, "Name this blueprint", 40);
		}

		public string GetText()
		{
			return _lastText;
		}

		public void SetText(string text)
		{
			_lastText = text;
			string text2 = BlueprintSession.Save(Player.m_localPlayer, text);
			GameApi.Message(Player.m_localPlayer, text2);
			if (text2.StartsWith("Saved", StringComparison.Ordinal))
			{
				_lastText = "";
			}
		}
	}
	internal sealed class BlueprintSelection<TKey>
	{
		private readonly List<TKey> _keys = new List<TKey>();

		private readonly List<WorldPiece> _pieces = new List<WorldPiece>();

		public int Count => _keys.Count;

		public IReadOnlyList<WorldPiece> Pieces => _pieces;

		public IReadOnlyList<TKey> Keys => _keys;

		public bool Contains(TKey key)
		{
			return IndexOf(key) >= 0;
		}

		private int IndexOf(TKey key)
		{
			return _keys.FindIndex((TKey existing) => EqualityComparer<TKey>.Default.Equals(existing, key));
		}

		public bool Toggle(TKey key, WorldPiece piece)
		{
			int num = IndexOf(key);
			if (num >= 0)
			{
				_keys.RemoveAt(num);
				_pieces.RemoveAt(num);
				return false;
			}
			_keys.Add(key);
			_pieces.Add(piece);
			return true;
		}

		public bool Add(TKey key, WorldPiece piece)
		{
			if (Contains(key))
			{
				return false;
			}
			_keys.Add(key);
			_pieces.Add(piece);
			return true;
		}

		public bool Remove(TKey key)
		{
			int num = IndexOf(key);
			if (num < 0)
			{
				return false;
			}
			_keys.RemoveAt(num);
			_pieces.RemoveAt(num);
			return true;
		}

		public void ToggleGroup(IList<KeyValuePair<TKey, WorldPiece>> group)
		{
			if (group == null || group.Count == 0)
			{
				return;
			}
			bool flag = true;
			foreach (KeyValuePair<TKey, WorldPiece> item in group)
			{
				if (!Contains(item.Key))
				{
					flag = false;
					break;
				}
			}
			foreach (KeyValuePair<TKey, WorldPiece> item2 in group)
			{
				bool flag2 = Contains(item2.Key);
				if (flag)
				{
					Toggle(item2.Key, item2.Value);
				}
				else if (!flag2)
				{
					Toggle(item2.Key, item2.Value);
				}
			}
		}

		public void Clear()
		{
			_keys.Clear();
			_pieces.Clear();
		}

		public Blueprint ToBlueprint(string name)
		{
			if (_pieces.Count == 0)
			{
				throw new InvalidOperationException("Select at least one piece first.");
			}
			return Blueprint.FromWorld(name, _pieces);
		}
	}
	internal static class BlueprintSession
	{
		private static readonly Color OriginColor = new Color(1f, 0.85f, 0.2f);

		private static readonly Color SelectedColor = new Color(0.25f, 0.9f, 1f);

		private static readonly BlueprintSelection<int> Selection = new BlueprintSelection<int>();

		private static readonly Dictionary<int, Piece> Live = new Dictionary<int, Piece>();

		private static bool _lit;

		private static bool _growing;

		private static bool _sweeping;

		private static bool _sweepAdding;

		private static bool _sweepPressedPiece;

		private static int _sweepLastId;

		private static int _sweepChanged;

		private const int GrowPerFrame = 40;

		private const float TouchMargin = 0.01f;

		public static int Count => Selection.Count;

		public static bool Sweeping => _sweeping;

		public static bool IsSelected(Piece piece)
		{
			if ((Object)(object)piece != (Object)null)
			{
				return Selection.Contains(((Object)piece).GetInstanceID());
			}
			return false;
		}

		public static List<Piece> SelectedPieces()
		{
			List<Piece> list = new List<Piece>(Live.Count);
			foreach (Piece value in Live.Values)
			{
				if ((Object)(object)value != (Object)null)
				{
					list.Add(value);
				}
			}
			return list;
		}

		public static void OnSelectKey(Player player, bool wholeGroup)
		{
			try
			{
				Select(player, wholeGroup);
			}
			catch (Exception ex)
			{
				Plugin.LogOnce("blueprint-select", "Selecting a piece failed: " + ex);
			}
		}

		private static void Select(Player player, bool wholeGroup)
		{
			//IL_014e: Unknown result type (might be due to invalid IL or missing references)
			if (BlueprintPlacer.Active != null)
			{
				BlueprintPlacer.Disarm();
			}
			Piece aimed = GameApi.AimedPiece(player, ModConfig.SelectReach.Value);
			if ((Object)(object)aimed == (Object)null)
			{
				StartSweep(adding: true, 0, pressed: false);
				return;
			}
			if (!TryCapture(player, aimed, out var captured, out var reason))
			{
				GameApi.Message(player, reason);
				StartSweep(adding: true, ((Object)aimed).GetInstanceID(), pressed: true);
				return;
			}
			int instanceID = ((Object)aimed).GetInstanceID();
			if (!wholeGroup)
			{
				bool flag = Selection.Toggle(instanceID, captured);
				Track(instanceID, aimed, flag);
				GameApi.Message(player, (flag ? "Added " : "Removed ") + GameApi.Localize(aimed.m_name) + " (" + Selection.Count + " selected)");
				StartSweep(flag, instanceID, pressed: true);
				return;
			}
			List<KeyValuePair<int, WorldPiece>> list = new List<KeyValuePair<int, WorldPiece>>
			{
				new KeyValuePair<int, WorldPiece>(instanceID, captured)
			};
			Dictionary<int, Piece> dictionary = new Dictionary<int, Piece> { { instanceID, aimed } };
			long num = GroupTag.Read(aimed);
			if (num != 0L)
			{
				List<Piece> list2 = new List<Piece>();
				Piece.GetAllPiecesInRadius(((Component)aimed).transform.position, 100000f, list2);
				list2.Sort(delegate(Piece a, Piece b)
				{
					//IL_0006: 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_001b: Unknown result type (might be due to invalid IL or missing references)
					//IL_0020: Unknown result type (might be due to invalid IL or missing references)
					//IL_0031: 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_0046: Unknown result type (might be due to invalid IL or missing references)
					//IL_004b: Unknown result type (might be due to invalid IL or missing references)
					Vector3 val = ((Component)a).transform.position - ((Component)aimed).transform.position;
					float sqrMagnitude = ((Vector3)(ref val)).sqrMagnitude;
					val = ((Component)b).transform.position - ((Component)aimed).transform.position;
					return sqrMagnitude.CompareTo(((Vector3)(ref val)).sqrMagnitude);
				});
				foreach (Piece item in list2)
				{
					int instanceID2 = ((Object)item).GetInstanceID();
					if (instanceID2 != instanceID && GroupTag.Read(item) == num && TryCapture(player, item, out var captured2, out var _))
					{
						list.Add(new KeyValuePair<int, WorldPiece>(instanceID2, captured2));
						dictionary[instanceID2] = item;
					}
				}
			}
			Selection.ToggleGroup(list);
			foreach (KeyValuePair<int, WorldPiece> item2 in list)
			{
				Track(item2.Key, dictionary[item2.Key], Selection.Contains(item2.Key));
			}
			GameApi.Message(player, list.Count + ((list.Count == 1) ? " piece" : " pieces") + " in the group, " + Selection.Count + " selected");
			StartSweep(Selection.Contains(instanceID), instanceID, pressed: true);
		}

		private static void StartSweep(bool adding, int startId, bool pressed)
		{
			_sweeping = true;
			_sweepAdding = adding;
			_sweepPressedPiece = pressed;
			_sweepLastId = startId;
			_sweepChanged = 0;
		}

		public static void SweepStep(Player player)
		{
			if (!_sweeping || (Object)(object)player == (Object)null)
			{
				return;
			}
			try
			{
				Piece val = GameApi.AimedPiece(player, ModConfig.SelectReach.Value);
				if ((Object)(object)val == (Object)null)
				{
					return;
				}
				int instanceID = ((Object)val).GetInstanceID();
				if (instanceID == _sweepLastId)
				{
					return;
				}
				_sweepLastId = instanceID;
				if (_sweepAdding)
				{
					if (!Selection.Contains(instanceID) && TryCapture(player, val, out var captured, out var _) && Selection.Add(instanceID, captured))
					{
						Track(instanceID, val, selected: true);
						_sweepChanged++;
					}
				}
				else if (Selection.Remove(instanceID))
				{
					Track(instanceID, val, selected: false);
					_sweepChanged++;
				}
			}
			catch (Exception ex)
			{
				Plugin.LogOnce("blueprint-sweep", "Sweep-selecting failed: " + ex);
			}
		}

		public static void EndSweep(Player player)
		{
			if (_sweeping)
			{
				_sweeping = false;
				if (_sweepChanged > 0)
				{
					GameApi.Message(player, (_sweepAdding ? "Swept in " : "Swept out ") + _sweepChanged + ((_sweepChanged == 1) ? " piece" : " pieces") + " (" + Selection.Count + " selected)");
				}
				else if (!_sweepPressedPiece)
				{
					GameApi.Message(player, "Aim at a built piece first");
				}
			}
		}

		public static void OnGrowKey(Player player)
		{
			try
			{
				StartGrow(player);
			}
			catch (Exception ex)
			{
				Plugin.LogOnce("blueprint-grow", "Growing the selection failed: " + ex);
			}
		}

		private static void StartGrow(Player player)
		{
			if (_growing)
			{
				GameApi.Message(player, "Still growing the selection");
				return;
			}
			Piece val = GameApi.AimedPiece(player, ModConfig.SelectReach.Value);
			if ((Object)(object)val == (Object)null)
			{
				GameApi.Message(player, "Aim at a built piece first");
				return;
			}
			if (!TryCapture(player, val, out var _, out var reason))
			{
				GameApi.Message(player, reason);
				return;
			}
			bool adding = !Selection.Contains(((Object)val).GetInstanceID());
			((MonoBehaviour)Plugin.Instance).StartCoroutine(Grow(player, val, adding));
		}

		private static IEnumerator Grow(Player player, Piece start, bool adding)
		{
			_growing = true;
			int changed = 0;
			try
			{
				HashSet<int> visited = new HashSet<int> { ((Object)start).GetInstanceID() };
				Queue<Piece> frontier = new Queue<Piece>();
				frontier.Enqueue(start);
				int sinceYield = 0;
				while (frontier.Count > 0)
				{
					Piece val = frontier.Dequeue();
					if ((Object)(object)val == (Object)null)
					{
						continue;
					}
					if (TryCapture(player, val, out var captured, out var _))
					{
						int instanceID = ((Object)val).GetInstanceID();
						bool flag = Selection.Contains(instanceID);
						if (adding && !flag)
						{
							Selection.Toggle(instanceID, captured);
							Track(instanceID, val, selected: true);
							changed++;
						}
						else if (!adding && flag)
						{
							Selection.Toggle(instanceID, captured);
							Track(instanceID, val, selected: false);
							changed++;
						}
						Bounds sourceBounds = ColliderBounds(val);
						if (((Bounds)(ref sourceBounds)).size != Vector3.zero)
						{
							List<Piece> list = new List<Piece>();
							Vector3 position = ((Component)val).transform.position;
							Vector3 extents = ((Bounds)(ref sourceBounds)).extents;
							Piece.GetAllPiecesInRadius(position, ((Vector3)(ref extents)).magnitude + 8f, list);
							foreach (Piece item in list)
							{
								if (!((Object)(object)item == (Object)null))
								{
									int instanceID2 = ((Object)item).GetInstanceID();
									if (!visited.Contains(instanceID2) && Touches(sourceBounds, item))
									{
										visited.Add(instanceID2);
										frontier.Enqueue(item);
									}
								}
							}
						}
					}
					if ((Object)(object)player == (Object)null)
					{
						break;
					}
					int num = sinceYield + 1;
					sinceYield = num;
					if (num >= 40)
					{
						sinceYield = 0;
						yield return null;
					}
				}
			}
			finally
			{
				_growing = false;
			}
			if ((Object)(object)player != (Object)null)
			{
				GameApi.Message(player, changed + ((changed == 1) ? " piece " : " pieces ") + (adding ? "added" : "removed") + " (" + Selection.Count + " selected)");
			}
		}

		private static Bounds ColliderBounds(Piece piece)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: 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_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			Bounds result = default(Bounds);
			bool flag = false;
			Collider[] componentsInChildren = ((Component)piece).GetComponentsInChildren<Collider>();
			foreach (Collider val in componentsInChildren)
			{
				if (!val.isTrigger)
				{
					if (!flag)
					{
						result = val.bounds;
						flag = true;
					}
					else
					{
						((Bounds)(ref result)).Encapsulate(val.bounds);
					}
				}
			}
			return result;
		}

		private static bool Touches(Bounds sourceBounds, Piece candidate)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0001: 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)
			Bounds val = sourceBounds;
			((Bounds)(ref val)).Expand(0.02f);
			Collider[] componentsInChildren = ((Component)candidate).GetComponentsInChildren<Collider>();
			foreach (Collider val2 in componentsInChildren)
			{
				if (!val2.isTrigger && ((Bounds)(ref val)).Intersects(val2.bounds))
				{
					return true;
				}
			}
			return false;
		}

		private static bool TryCapture(Player player, Piece piece, out WorldPiece captured, out string reason)
		{
			//IL_0107: 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)
			captured = default(WorldPiece);
			if ((Object)(object)((Component)piece).GetComponent<PrivateArea>() != (Object)null)
			{
				reason = "Wards can't go in a blueprint";
				return false;
			}
			if ((Object)(object)((Component)piece).GetComponent<TeleportWorld>() != (Object)null)
			{
				reason = "Portals can't go in a blueprint";
				return false;
			}
			string prefabName = Utils.GetPrefabName(((Component)piece).gameObject);
			GameObject val = (((Object)(object)ZNetScene.instance != (Object)null) ? ZNetScene.instance.GetPrefab(prefabName) : null);
			Piece val2 = (((Object)(object)val != (Object)null) ? val.GetComponent<Piece>() : null);
			if ((Object)(object)val2 == (Object)null)
			{
				Plugin.Log.LogWarning((object)("Can't capture '" + ((Object)((Component)piece).gameObject).name + "' for a blueprint: prefab name '" + prefabName + "', ZNetScene.GetPrefab found " + ((Object)(object)val != (Object)null) + ", it has a Piece " + ((Object)(object)val2 != (Object)null)));
				reason = "That piece can't be copied";
				return false;
			}
			if (!player.IsPieceAvailable(val2))
			{
				reason = "You haven't unlocked that piece yet";
				return false;
			}
			captured = new WorldPiece(prefabName, ((Component)piece).transform.position, ((Component)piece).transform.rotation);
			reason = null;
			return true;
		}

		private static void Track(int id, Piece piece, bool selected)
		{
			if (selected)
			{
				Live[id] = piece;
				return;
			}
			Live.Remove(id);
			ResetLook(piece);
		}

		public static void Highlight()
		{
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b7: 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)
			//IL_00db: Unknown result type (might be due to invalid IL or missing references)
			if (Live.Count == 0)
			{
				return;
			}
			try
			{
				MaterialMan instance = MaterialMan.instance;
				if ((Object)(object)instance == (Object)null)
				{
					return;
				}
				_lit = true;
				List<int> list = null;
				int num = ((Selection.Count > 0) ? Selection.Keys[0] : 0);
				foreach (KeyValuePair<int, Piece> item in Live)
				{
					if ((Object)(object)item.Value == (Object)null)
					{
						(list ?? (list = new List<int>())).Add(item.Key);
						continue;
					}
					Color val = ((item.Key == num) ? OriginColor : SelectedColor);
					instance.SetValue<Color>(((Component)item.Value).gameObject, ShaderProps._EmissionColor, val * 0.5f, false);
					instance.SetValue<Color>(((Component)item.Value).gameObject, ShaderProps._Color, val, false);
				}
				if (list == null)
				{
					return;
				}
				foreach (int item2 in list)
				{
					Live.Remove(item2);
					Selection.Toggle(item2, default(WorldPiece));
				}
			}
			catch (Exception ex)
			{
				Plugin.LogOnce("blueprint-highlight", "Highlighting the selection failed: " + ex);
			}
		}

		public static void ClearHighlight()
		{
			if (!_lit)
			{
				return;
			}
			_lit = false;
			foreach (Piece value in Live.Values)
			{
				ResetLook(value);
			}
		}

		private static void ResetLook(Piece piece)
		{
			if ((Object)(object)piece == (Object)null)
			{
				return;
			}
			Safe.Run(delegate
			{
				MaterialMan instance = MaterialMan.instance;
				if (!((Object)(object)instance == (Object)null))
				{
					instance.ResetValue(((Component)piece).gameObject, ShaderProps._Color);
					instance.ResetValue(((Component)piece).gameObject, ShaderProps._EmissionColor);
				}
			}, "MaterialMan.ResetValue");
		}

		public static void Clear()
		{
			_sweeping = false;
			ClearHighlight();
			Live.Clear();
			Selection.Clear();
		}

		public static string Save(Player player, string name)
		{
			if (Selection.Count == 0)
			{
				return "Select at least one piece first (aim at it and press F)";
			}
			if (string.IsNullOrWhiteSpace(name))
			{
				return "Give the blueprint a name first";
			}
			try
			{
				Blueprint blueprint = Selection.ToBlueprint(name.Trim());
				BlueprintLibrary.Save(blueprint, player);
				Clear();
				return "Saved blueprint '" + blueprint.Name + "' (" + blueprint.Items.Count + ((blueprint.Items.Count == 1) ? " piece)" : " pieces)");
			}
			catch (ArgumentException ex)
			{
				return ex.Message;
			}
			catch (Exception ex2)
			{
				Plugin.LogOnce("blueprint-save", "Saving the blueprint failed: " + ex2);
				return "Could not save the blueprint, see the BepInEx log";
			}
		}

		public static void OnDeleteKey(Player player)
		{
			try
			{
				GameObject placementGhost = player.m_placementGhost;
				if ((Object)(object)placementGhost == (Object)null || !BlueprintPieces.TryGetBlueprint(((Object)placementGhost).name, out var blueprint))
				{
					GameApi.Message(player, "Select a saved blueprint in the build menu first");
					return;
				}
				BlueprintLibrary.Delete(blueprint.Name);
				BlueprintPlacer.Disarm();
				GameApi.Message(player, "Deleted blueprint '" + blueprint.Name + "'");
			}
			catch (Exception ex)
			{
				Plugin.LogOnce("blueprint-delete", "Deleting the blueprint failed: " + ex);
			}
		}
	}
	internal sealed class BlueprintLoadResult
	{
		public readonly List<Blueprint> Blueprints = new List<Blueprint>();

		public readonly List<string> Problems = new List<string>();
	}
	internal sealed class BlueprintStore
	{
		private static readonly Encoding Utf8WithoutBom = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false);

		private readonly Dictionary<string, string> _files = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);

		public string Directory { get; }

		public BlueprintStore(string directory)
		{
			Directory = directory;
		}

		private string PathFor(string name)
		{
			if (!_files.TryGetValue(name, out var value))
			{
				return Path.Combine(Directory, BlueprintFile.FileNameFor(name));
			}
			return value;
		}

		public string Save(Blueprint blueprint)
		{
			System.IO.Directory.CreateDirectory(Directory);
			string text = PathFor(blueprint.Name);
			File.WriteAllText(text, BlueprintFile.Serialize(blueprint), Utf8WithoutBom);
			_files[blueprint.Name] = text;
			return text;
		}

		public BlueprintLoadResult LoadAll()
		{
			BlueprintLoadResult blueprintLoadResult = new BlueprintLoadResult();
			_files.Clear();
			if (!System.IO.Directory.Exists(Directory))
			{
				return blueprintLoadResult;
			}
			List<string> list = new List<string>();
			string[] files = System.IO.Directory.GetFiles(Directory);
			foreach (string text in files)
			{
				if (text.EndsWith(".blueprint", StringComparison.OrdinalIgnoreCase) || text.EndsWith(".vbuild", StringComparison.OrdinalIgnoreCase))
				{
					list.Add(text);
				}
			}
			list.Sort((string a, string b) => string.Compare(Path.GetFileName(a), Path.GetFileName(b), StringComparison.OrdinalIgnoreCase));
			foreach (string item in list)
			{
				string fileName = Path.GetFileName(item);
				string text2;
				try
				{
					text2 = File.ReadAllText(item, Encoding.UTF8);
				}
				catch (Exception ex) when (ex is IOException || ex is UnauthorizedAccessException)
				{
					blueprintLoadResult.Problems.Add(fileName + ": could not be read (" + ex.Message + ")");
					continue;
				}
				if (BlueprintFile.TryParse(text2, fileName, out var blueprint, out var error))
				{
					blueprintLoadResult.Blueprints.Add(blueprint);
					_files[blueprint.Name] = item;
				}
				else
				{
					blueprintLoadResult.Problems.Add(fileName + ": " + error);
				}
			}
			return blueprintLoadResult;
		}

		public bool Delete(string name)
		{
			string path = PathFor(name);
			_files.Remove(name);
			if (!File.Exists(path))
			{
				return false;
			}
			File.Delete(path);
			return true;
		}
	}
	internal static class CellValidator
	{
		public static bool IsValid(Player player, Piece piece, GameObject cellGhost, Vector3 pos)
		{
			//IL_0013: 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_0028: Unknown result type (might be due to invalid IL or missing references)
			if (piece.m_noClipping && GameApi.IsClipping(player, cellGhost))
			{
				return false;
			}
			if (!GameApi.HasWardAccess(pos))
			{
				return false;
			}
			if (GameApi.InNoBuildZone(pos))
			{
				return false;
			}
			if (GameApi.WrongBiome(piece, pos))
			{
				return false;
			}
			return true;
		}
	}
	internal static class ControlPanel
	{
		private delegate void AxisSetter(int axis, float value);

		private static readonly CultureInfo Invariant = CultureInfo.InvariantCulture;

		private const float PanelWidth = 460f;

		private const float LegendWidth = 460f;

		private const float LegendKeyWidth = 180f;

		private const float Pad = 16f;

		private const float RowHeight = 28f;

		private const float RowGap = 6f;

		private const float LabelWidth = 118f;

		private const float SmallButton = 28f;

		private const float FieldWidth = 64f;

		private static GameObject _window;

		private static RectTransform _windowRect;

		private static readonly List<InputField> _fields = new List<InputField>();

		private static InputField _sizeX;

		private static InputField _sizeY;

		private static InputField _sizeZ;

		private static InputField _gapX;

		private static InputField _gapY;

		private static InputField _gapZ;

		private static InputField _staggerX;

		private static InputField _staggerY;

		private static InputField _staggerZ;

		private static InputField _rotationField;

		private static InputField _rotationZField;

		private static Toggle _terrainToggle;

		private static Toggle _hudToggle;

		private static Text _blueprintStatus;

		private static Text _cellCount;

		private static Text _holdLabel;

		private static readonly Color DividerColor = new Color(1f, 1f, 1f, 0.14f);

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

		public static bool Typing
		{
			get
			{
				foreach (InputField field in _fields)
				{
					if ((Object)(object)field != (Object)null && field.isFocused)
					{
						return true;
					}
				}
				return false;
			}
		}

		public static bool CapturingInput
		{
			get
			{
				if (!Typing)
				{
					return CursorOverPanel();
				}
				return true;
			}
		}

		public static bool CursorOverPanel()
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			if (IsOpen && (Object)(object)_windowRect != (Object)null)
			{
				return RectTransformUtility.RectangleContainsScreenPoint(_windowRect, Vector2.op_Implicit(Input.mousePosition), (Camera)null);
			}
			return false;
		}

		public static void Init()
		{
			GUIManager.OnCustomGUIAvailable += Build;
		}

		public static void Toggle()
		{
			Build();
			if (!((Object)(object)_window == (Object)null))
			{
				bool flag = !_window.activeSelf;
				_window.SetActive(flag);
				if (flag)
				{
					Refresh();
				}
			}
		}

		public static void Close()
		{
			if ((Object)(object)_window != (Object)null)
			{
				_window.SetActive(false);
			}
		}

		public static void Refresh()
		{
			if (!IsOpen)
			{
				return;
			}
			try
			{
				RefreshLive();
			}
			catch (Exception ex)
			{
				Plugin.LogOnce("panel-refresh", "Updating the control panel failed: " + ex);
			}
		}

		private static void RefreshLive()
		{
			//IL_018a: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01be: Unknown result type (might be due to invalid IL or missing references)
			SyncField(_sizeX, GridState.Cols.ToString(Invariant));
			SyncField(_sizeY, GridState.Layers.ToString(Invariant));
			SyncField(_sizeZ, GridState.Rows.ToString(Invariant));
			SyncField(_gapX, Format(GridState.GapX));
			SyncField(_gapY, Format(GridState.GapY));
			SyncField(_gapZ, Format(GridState.GapZ));
			SyncField(_staggerX, Format(GridState.StaggerX));
			SyncField(_staggerY, Format(GridState.StaggerY));
			SyncField(_staggerZ, Format(GridState.StaggerZ));
			SyncField(_rotationField, Format(GridState.Rotation));
			SyncField(_rotationZField, Format(GridState.RotationZ));
			if ((Object)(object)_terrainToggle != (Object)null)
			{
				_terrainToggle.SetIsOnWithoutNotify(GridState.FollowTerrain);
			}
			if ((Object)(object)_hudToggle != (Object)null)
			{
				_hudToggle.SetIsOnWithoutNotify(ModConfig.ShowHud.Value);
			}
			if ((Object)(object)_blueprintStatus != (Object)null)
			{
				_blueprintStatus.text = ((BlueprintSession.Count == 0) ? ("Aim at built pieces and press " + GridPreview.KeyName(ModConfig.KeyBlueprintSelect.Value) + " to select them (" + GridPreview.KeyName(ModConfig.BigStepModifier.Value) + " + " + GridPreview.KeyName(ModConfig.KeyBlueprintSelect.Value) + ": the whole group).") : (BlueprintSession.Count + ((BlueprintSession.Count == 1) ? " piece selected. The gold one is the origin." : " pieces selected. The gold one is the origin.")));
			}
			if ((Object)(object)_cellCount != (Object)null)
			{
				_cellCount.text = GridState.Cells + ((GridState.Cells == 1) ? " piece" : " pieces");
			}
			if ((Object)(object)_holdLabel != (Object)null)
			{
				_holdLabel.text = (GridHold.Frozen ? "Release hold" : "Hold");
			}
		}

		private static void SyncField(InputField field, string value)
		{
			if (!((Object)(object)field == (Object)null) && !field.isFocused && !(field.text == value))
			{
				field.SetTextWithoutNotify(value);
			}
		}

		private static string Format(float value)
		{
			return value.ToString("0.###", Invariant);
		}

		private static bool TryParse(string text, out float value)
		{
			return float.TryParse(text.Replace(',', '.'), NumberStyles.Float, Invariant, out value);
		}

		private static void Build()
		{
			if ((Object)(object)_window != (Object)null)
			{
				return;
			}
			try
			{
				BuildWindow();
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("Could not build the SmartBuild panel: " + ex));
			}
		}

		private static void BuildWindow()
		{
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Expected O, but got Unknown
			//IL_05c5: Unknown result type (might be due to invalid IL or missing references)
			_fields.Clear();
			GUIManager instance = GUIManager.Instance;
			Transform transform = GUIManager.CustomGUIFront.transform;
			_window = instance.CreateWoodpanel(transform, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), Vector2.zero, 920f, 400f);
			((Object)_window).name = "SmartBuildPanel";
			_windowRect = (RectTransform)_window.transform;
			_window.SetActive(false);
			float y = -16f;
			AddTitle(ref y, "SmartBuild");
			float top = y;
			AddHeader(ref y, "Size (negative grows the other way, 1 = one piece)");
			_sizeX = AddSizeRow(ref y, "Width (X)", 0);
			_sizeY = AddSizeRow(ref y, "Height (Y)", 1);
			_sizeZ = AddSizeRow(ref y, "Depth (Z)", 2);
			AddHeader(ref y, "Gap (m)");
			InputField[] array = AddAxisRow(ref y);
			_gapX = array[0];
			_gapY = array[1];
			_gapZ = array[2];
			WireAxisFields(array, GridState.SetGap);
			AddHeader(ref y, "Stagger (m, accumulates)");
			InputField[] array2 = AddAxisRow(ref y);
			_staggerX = array2[0];
			_staggerY = array2[1];
			_staggerZ = array2[2];
			WireAxisFields(array2, GridState.SetStagger);
			AddNote(ref y, "X: each row sideways   Y: each layer sideways   Z: each column forward/back");
			AddHeader(ref y, "Rotation (degrees added per piece)");
			_rotationField = AddLabeledPlusMinusRow(ref y, "Along a row", delegate
			{
				GridState.AdjustRotation(0f - ModConfig.RotationStep.Value);
			}, delegate
			{
				GridState.AdjustRotation(ModConfig.RotationStep.Value);
			}, delegate(string text)
			{
				if (TryParse(text, out var value))
				{
					GridState.SetRotation(value);
				}
			});
			_rotationZField = AddLabeledPlusMinusRow(ref y, "Per row (spiral)", delegate
			{
				GridState.AdjustRotationZ(0f - ModConfig.RotationStep.Value);
			}, delegate
			{
				GridState.AdjustRotationZ(ModConfig.RotationStep.Value);
			}, delegate(string text)
			{
				if (TryParse(text, out var value))
				{
					GridState.SetRotationZ(value);
				}
			});
			_terrainToggle = AddToggle(ref y, "Follow terrain (pieces rise and fall with the ground)", delegate(bool v)
			{
				GridState.SetFollowTerrain(v);
			});
			_hudToggle = AddToggle(ref y, "Show HUD (readout and controls legend)", delegate(bool v)
			{
				ModConfig.ShowHud.Value = v;
			});
			AddHeader(ref y, "Blueprint");
			_blueprintStatus = AddWrappedText(ref y, "", 40f);
			AddButtonRow(ref y, ("Save...", delegate
			{
				BlueprintSavePrompt.Show(Player.m_localPlayer);
			}), ("Clear", delegate
			{
				BlueprintSession.Clear();
				BlueprintPlacer.Disarm();
			}));
			_cellCount = AddPlainText(ref y, "");
			AddButtonRow(ref y, ("Reset", delegate
			{
				GridState.Reset();
			}), ("Hold", delegate
			{
				GridHold.Toggle(Player.m_localPlayer);
			}), ("Undo last", delegate
			{
				PlacementHistory.Undo(Player.m_localPlayer);
			}), ("Close", Close));
			_holdLabel = FindButtonLabel(_window, "Hold");
			AddHeader(ref y, "Nudge (holds the grid)");
			AddButtonRow(ref y, ("Left", delegate
			{
				NudgeClick(-1, 0, 0);
			}), ("Forward", delegate
			{
				NudgeClick(0, 1, 0);
			}), ("Back", delegate
			{
				NudgeClick(0, -1, 0);
			}), ("Right", delegate
			{
				NudgeClick(1, 0, 0);
			}));
			AddButtonRow(ref y, ("Up", delegate
			{
				NudgeClick(0, 0, 1);
			}), ("Down", delegate
			{
				NudgeClick(0, 0, -1);
			}));
			AddNote(ref y, "Relative to the camera. " + ModConfig.NudgeStep.Value.ToString("0.##", Invariant) + " m per click, " + GridPreview.KeyName(ModConfig.BigStepModifier.Value) + ": " + ModConfig.NudgeBigStep.Value.ToString("0.##", Invariant) + " m");
			float num = BuildLegendColumn(top);
			float num2 = Mathf.Min(y, num) - 10f;
			_windowRect.SetSizeWithCurrentAnchors((Axis)1, 0f - num2);
		}

		private static float BuildLegendColumn(float top)
		{
			//IL_002d: 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_0111: 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)
			float num = 460f;
			float num2 = 444f;
			float num3 = top - 6f;
			AddColumnText("Controls", num, num3, num2, GUIManager.Instance.AveriaSerifBold, 16, GUIManager.Instance.ValheimOrange);
			num3 -= 24f;
			string[] array = GridPreview.LegendLines();
			for (int i = 0; i < array.Length; i++)
			{
				string text = array[i];
				int num4 = text.IndexOf("    ", StringComparison.Ordinal);
				float num5;
				if (i == 0 || num4 < 0)
				{
					num5 = Height(AddColumnText(text.Trim(), num, num3, num2, (i == 0) ? GUIManager.Instance.AveriaSerifBold : GUIManager.Instance.AveriaSerif, 13, Color.white));
				}
				else
				{
					float num6 = num2 - 180f - 8f;
					Text t = AddColumnText(Capitalize(text.Substring(num4).Trim()), num, num3, num6, GUIManager.Instance.AveriaSerif, 13, Color.white);
					num5 = Mathf.Max(Height(AddColumnText(text.Substring(0, num4).Trim(), num + num6 + 8f, num3, 180f, GUIManager.Instance.AveriaSerifBold, 13, Color.white)), Height(t));
				}
				num3 -= num5 + 3f;
				if (i < array.Length - 1)
				{
					AddDivider(num, num3, num2);
				}
				num3 -= 4f;
			}
			return num3;
		}

		private static void AddDivider(float left, float top, float width)
		{
			//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_0040: 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_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: 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_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: 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_0082: 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_00a2: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject("SmartBuildDivider", new Type[2]
			{
				typeof(RectTransform),
				typeof(Image)
			});
			val.transform.SetParent(_window.transform, false);
			RectTransform val2 = (RectTransform)val.transform;
			Vector2 val3 = default(Vector2);
			((Vector2)(ref val3))..ctor(0f, 1f);
			val2.anchorMax = val3;
			val2.anchorMin = val3;
			val2.pivot = new Vector2(0f, 1f);
			val2.anchoredPosition = new Vector2(left, top);
			val2.sizeDelta = new Vector2(width, 1f);
			Image component = val.GetComponent<Image>();
			((Graphic)component).color = DividerColor;
			((Graphic)component).raycastTarget = false;
		}

		private static Text AddColumnText(string text, float left, float top, float width, Font font, int fontSize, Color color)
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: 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_008a: 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)
			GameObject obj = GUIManager.Instance.CreateText(text, _window.transform, new Vector2(0f, 1f), new Vector2(0f, 1f), Vector2.zero, font, fontSize, color, true, Color.black, width, (float)fontSize + 6f, false);
			Text component = obj.GetComponent<Text>();
			component.alignment = (TextAnchor)0;
			component.horizontalOverflow = (HorizontalWrapMode)0;
			component.verticalOverflow = (VerticalWrapMode)1;
			RectTransform val = (RectTransform)obj.transform;
			val.pivot = new Vector2(0f, 1f);
			val.anchoredPosition = new Vector2(left, top);
			val.SetSizeWithCurrentAnchors((Axis)1, Height(component));
			return component;
		}

		private static float Height(Text t)
		{
			return Mathf.Max((float)t.fontSize + 4f, t.preferredHeight);
		}

		private static string Capitalize(string text)
		{
			if (!string.IsNullOrEmpty(text))
			{
				return char.ToUpperInvariant(text[0]) + text.Substring(1);
			}
			return text;
		}

		private static void NudgeClick(int right, int forward, int up)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			Player localPlayer = Player.m_localPlayer;
			if (!((Object)(object)localPlayer == (Object)null))
			{
				float step = (Keys.Held(ModConfig.BigStepModifier.Value) ? ModConfig.NudgeBigStep.Value : ModConfig.NudgeSte