Decompiled source of Buildheim v1.2.0

plugins/Buildheim.dll

Decompiled 12 hours ago
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Serialization;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using Jotunn;
using Jotunn.Managers;
using Jotunn.Utils;
using PlanBuild.Blueprints;
using PlanBuild.Client;
using SimpleJson;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.Rendering;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("Buildheim")]
[assembly: AssemblyDescription("Client-only blueprint planning and hammer building for Valheim")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Buildheim")]
[assembly: AssemblyCopyright("WTFPL")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("e3243d22-4307-4008-ba36-9f326008cde5")]
[assembly: AssemblyFileVersion("1.2.0.0")]
[assembly: IgnoresAccessChecksTo("assembly_valheim")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.2.0.0")]
[module: UnverifiableCode]
namespace PlanBuild
{
	[BepInPlugin("augusdogus.Buildheim", "Buildheim", "1.2.0")]
	[BepInDependency("com.jotunn.jotunn", "2.30.0")]
	[BepInIncompatibility("marcopogo.PlanBuild")]
	[NetworkCompatibility(/*Could not decode attribute arguments.*/)]
	internal class PlanBuildPlugin : BaseUnityPlugin
	{
		public const string PluginGUID = "augusdogus.Buildheim";

		public const string PluginName = "Buildheim";

		public const string PluginVersion = "1.2.0";

		private ClientPlanner planner;

		private void Awake()
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Invalid comparison between Unknown and I4
			if ((int)SystemInfo.graphicsDeviceType == 4)
			{
				((Behaviour)this).enabled = false;
				return;
			}
			if (!ClientPaths.TryMigrateConfig(Paths.ConfigPath, ((BaseUnityPlugin)this).Config.ConfigFilePath, out var copied, out var error))
			{
				((BaseUnityPlugin)this).Logger.LogError((object)error);
				((Behaviour)this).enabled = false;
				return;
			}
			if (copied)
			{
				((BaseUnityPlugin)this).Config.Reload();
			}
			planner = new ClientPlanner(new ClientConfig(((BaseUnityPlugin)this).Config));
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Buildheim loaded. Press End in a world to open the planner.");
		}

		private void Update()
		{
			planner?.Update();
		}

		private void LateUpdate()
		{
			planner?.DrawProjection();
		}

		private void OnDestroy()
		{
			planner?.Dispose();
		}
	}
}
namespace PlanBuild.Blueprints
{
	public class PieceEntry
	{
		public string line;

		public string name;

		public string category;

		public float posX;

		public float posY;

		public float posZ;

		public float rotX;

		public float rotY;

		public float rotZ;

		public float rotW;

		public string additionalInfo;

		public float scaleX;

		public float scaleY;

		public float scaleZ;

		public static PieceEntry FromBlueprint(string line)
		{
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: 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_0098: 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_00d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_010f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0111: 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)
			if (line.IndexOf(',') > -1)
			{
				line = line.Replace(',', '.');
			}
			string[] array = line.Split(new char[1] { ';' });
			string text = array[0];
			string text2 = array[1];
			float num = InvariantFloat(array[2]);
			float num2 = InvariantFloat(array[3]);
			float num3 = InvariantFloat(array[4]);
			float num4 = InvariantFloat(array[5]);
			float num5 = InvariantFloat(array[6]);
			float num6 = InvariantFloat(array[7]);
			float num7 = InvariantFloat(array[8]);
			Vector3 pos = default(Vector3);
			((Vector3)(ref pos))..ctor(num, num2, num3);
			Quaternion val = new Quaternion(num4, num5, num6, num7);
			Quaternion normalized = ((Quaternion)(ref val)).normalized;
			string text3 = array[9];
			if (string.IsNullOrEmpty(text3) || text3.Equals("\"\""))
			{
				text3 = null;
			}
			else
			{
				try
				{
					text3 = SimpleJson.DeserializeObject<string>(text3);
				}
				catch
				{
					text3 = array[9];
				}
			}
			Vector3 one = Vector3.one;
			if (array.Length > 10)
			{
				float num8 = InvariantFloat(array[10]);
				float num9 = InvariantFloat(array[11]);
				float num10 = InvariantFloat(array[12]);
				((Vector3)(ref one))..ctor(num8, num9, num10);
			}
			return new PieceEntry(text, text2, pos, normalized, text3, one);
		}

		public static PieceEntry FromVBuild(string line)
		{
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a1: 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_00a7: Unknown result type (might be due to invalid IL or missing references)
			if (line.IndexOf(',') > -1)
			{
				line = line.Replace(',', '.');
			}
			string[] array = line.Split(new char[1] { ' ' });
			string text = array[0];
			float num = InvariantFloat(array[1]);
			float num2 = InvariantFloat(array[2]);
			float num3 = InvariantFloat(array[3]);
			float num4 = InvariantFloat(array[4]);
			float num5 = InvariantFloat(array[5]);
			float num6 = InvariantFloat(array[6]);
			float num7 = InvariantFloat(array[7]);
			string text2 = "Building";
			Quaternion val = new Quaternion(num, num2, num3, num4);
			Quaternion normalized = ((Quaternion)(ref val)).normalized;
			Vector3 pos = default(Vector3);
			((Vector3)(ref pos))..ctor(num5, num6, num7);
			string empty = string.Empty;
			return new PieceEntry(text, text2, pos, normalized, empty, Vector3.one);
		}

		public PieceEntry(string name, string category, Vector3 pos, Quaternion rot, string additionalInfo, Vector3 scale)
		{
			//IL_0027: 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_004b: 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_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_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
			this.name = name.Split(new char[1] { '(' })[0];
			this.category = category;
			posX = pos.x;
			posY = pos.y;
			posZ = pos.z;
			rotX = rot.x;
			rotY = rot.y;
			rotZ = rot.z;
			rotW = rot.w;
			this.additionalInfo = additionalInfo;
			if (!string.IsNullOrEmpty(this.additionalInfo))
			{
				this.additionalInfo = string.Concat(this.additionalInfo.Split(new char[1] { ';' }));
			}
			scaleX = scale.x;
			scaleY = scale.y;
			scaleZ = scale.z;
			line = string.Join(";", this.name, this.category, InvariantString(posX), InvariantString(posY), InvariantString(posZ), InvariantString(rotX), InvariantString(rotY), InvariantString(rotZ), InvariantString(rotW), SimpleJson.SerializeObject((object)additionalInfo), InvariantString(scaleX), InvariantString(scaleY), InvariantString(scaleZ));
		}

		public Vector3 GetPosition()
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			return new Vector3(posX, posY, posZ);
		}

		public Quaternion GetRotation()
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			return new Quaternion(rotX, rotY, rotZ, rotW);
		}

		public Vector3 GetScale()
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			return new Vector3(scaleX, scaleY, scaleZ);
		}

		internal static float InvariantFloat(string s)
		{
			if (string.IsNullOrEmpty(s))
			{
				return 0f;
			}
			return float.Parse(s, NumberStyles.Any, NumberFormatInfo.InvariantInfo);
		}

		internal static string InvariantString(float f)
		{
			return f.ToString(NumberFormatInfo.InvariantInfo);
		}
	}
}
namespace PlanBuild.Client
{
	internal sealed class AutoBuilder
	{
		private readonly AutoBuildQueue queue = new AutoBuildQueue();

		public HammerTarget Find(BlueprintProjection projection, Player player)
		{
			HammerTarget selected = null;
			queue.Next(projection.Pieces.Count, Time.time, delegate(int index)
			{
				//IL_0069: Unknown result type (might be due to invalid IL or missing references)
				HammerTarget hammerTarget = HammerTarget.FromPiece(projection, projection.Pieces[index], player);
				if (hammerTarget == null || !player.m_knownRecipes.Contains(hammerTarget.Piece.m_name) || !player.IsPieceAvailable(hammerTarget.Piece) || !hammerTarget.HasInventoryResources() || ZoneSystem.instance.GetGlobalKey(hammerTarget.Piece.FreeBuildKey()) || !player.HaveRequirements(hammerTarget.Piece, (RequirementMode)0))
				{
					return false;
				}
				selected = hammerTarget;
				return true;
			});
			return selected;
		}

		public bool TryAttempt(Player player)
		{
			ItemData rightItem = ((Humanoid)player).GetRightItem();
			if (rightItem != null && (!rightItem.m_shared.m_useDurability || rightItem.m_durability > 0f) && ((Character)player).HaveStamina(rightItem.m_shared.m_attack.m_attackStamina) && Time.time - player.m_lastToolUseTime > player.m_placeDelay)
			{
				return queue.TryAttempt(Time.time);
			}
			return false;
		}

		public void Reset()
		{
			queue.Reset();
		}
	}
	internal sealed class AutoBuildQueue
	{
		private int nextIndex;

		private int? selectedIndex;

		private float nextAttempt;

		private float nextScan;

		public int? Next(int count, float now, Func<int, bool> eligible)
		{
			if (selectedIndex.HasValue && selectedIndex.Value < count && eligible(selectedIndex.Value))
			{
				return selectedIndex;
			}
			selectedIndex = null;
			if (count <= 0 || now < nextScan)
			{
				return null;
			}
			for (int i = 0; i < count; i++)
			{
				int num = nextIndex % count;
				nextIndex = (num + 1) % count;
				if (eligible(num))
				{
					selectedIndex = num;
					nextAttempt = now + 0.5f;
					return num;
				}
			}
			nextScan = now + 0.5f;
			return null;
		}

		public bool TryAttempt(float now)
		{
			if (!selectedIndex.HasValue || now < nextAttempt)
			{
				return false;
			}
			selectedIndex = null;
			return true;
		}

		public void Reset()
		{
			nextIndex = 0;
			selectedIndex = null;
			nextAttempt = 0f;
			nextScan = 0f;
		}
	}
	internal sealed class BlueprintDocument
	{
		public const int MaxPieces = 10000;

		public string Name { get; }

		public IReadOnlyList<PieceEntry> Pieces { get; }

		public BlueprintDocument(string name, IReadOnlyList<PieceEntry> pieces)
		{
			Name = name;
			Pieces = pieces;
		}

		public static bool TryParse(string name, IEnumerable<string> lines, bool vbuild, out BlueprintDocument document, out string error)
		{
			document = null;
			error = string.Empty;
			List<PieceEntry> list = new List<PieceEntry>();
			bool flag = true;
			int num = 0;
			try
			{
				foreach (string line in lines)
				{
					num++;
					string text = line.Trim();
					if (text.Length == 0)
					{
						continue;
					}
					if (text.StartsWith("#Name:"))
					{
						name = text.Substring(6).Trim();
					}
					if (text == "#Pieces")
					{
						flag = true;
					}
					switch (text)
					{
					case "#SnapPoints":
					case "#Terrain":
					case "#Description":
						flag = false;
						break;
					}
					if (!text.StartsWith("#") && flag)
					{
						PieceEntry pieceEntry = (vbuild ? PieceEntry.FromVBuild(text) : PieceEntry.FromBlueprint(text));
						if (string.IsNullOrWhiteSpace(pieceEntry.name) || !ValidTransform(pieceEntry))
						{
							error = $"Invalid piece transform on line {num}. Fix the file and reload it.";
							return false;
						}
						list.Add(pieceEntry);
						if (list.Count > 10000)
						{
							error = $"Blueprint exceeds the {10000} piece limit. Split it into smaller blueprints.";
							return false;
						}
					}
				}
			}
			catch (Exception ex) when (ex is FormatException || ex is IndexOutOfRangeException || ex is OverflowException)
			{
				error = $"Cannot read piece on line {num}: {ex.Message} Fix the file and reload it.";
				return false;
			}
			if (list.Count == 0)
			{
				error = "This file contains no building pieces. Choose another blueprint.";
				return false;
			}
			document = new BlueprintDocument(name, list);
			return true;
		}

		private static bool ValidTransform(PieceEntry entry)
		{
			if (new float[10] { entry.posX, entry.posY, entry.posZ, entry.rotX, entry.rotY, entry.rotZ, entry.rotW, entry.scaleX, entry.scaleY, entry.scaleZ }.All((float x) => !float.IsNaN(x) && !float.IsInfinity(x)) && entry.scaleX > 0f && entry.scaleY > 0f)
			{
				return entry.scaleZ > 0f;
			}
			return false;
		}

		public IEnumerable<string> Serialize()
		{
			yield return "#Name:" + Name;
			yield return "#Pieces";
			foreach (PieceEntry piece in Pieces)
			{
				yield return piece.line;
			}
		}
	}
	internal sealed class BlueprintHitTest
	{
		private readonly Vector3[] vertices;

		private readonly int[] triangles;

		public BlueprintHitTest(Vector3[] vertices, int[] triangles)
		{
			this.vertices = vertices;
			this.triangles = triangles;
		}

		public bool Intersect(Ray ray, out float distance)
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_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)
			//IL_0069: 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_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ef: 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)
			distance = float.PositiveInfinity;
			for (int i = 0; i < triangles.Length; i += 3)
			{
				Vector3 val = vertices[triangles[i]];
				Vector3 val2 = vertices[triangles[i + 1]] - val;
				Vector3 val3 = vertices[triangles[i + 2]] - val;
				Vector3 val4 = Vector3.Cross(((Ray)(ref ray)).direction, val3);
				float num = Vector3.Dot(val2, val4);
				if (Mathf.Abs(num) < 1E-06f)
				{
					continue;
				}
				float num2 = 1f / num;
				Vector3 val5 = ((Ray)(ref ray)).origin - val;
				float num3 = Vector3.Dot(val5, val4) * num2;
				if (num3 < 0f || num3 > 1f)
				{
					continue;
				}
				Vector3 val6 = Vector3.Cross(val5, val2);
				float num4 = Vector3.Dot(((Ray)(ref ray)).direction, val6) * num2;
				if (!(num4 < 0f) && !(num3 + num4 > 1f))
				{
					float num5 = Vector3.Dot(val3, val6) * num2;
					if (num5 >= 0f && num5 < distance)
					{
						distance = num5;
					}
				}
			}
			return !float.IsPositiveInfinity(distance);
		}
	}
	internal sealed class BlueprintLayers
	{
		private readonly float[] origins;

		private int[] available;

		public float Height { get; private set; } = 2f;

		public int? Selected { get; private set; }

		public int Count => available.Length;

		public int Ordinal
		{
			get
			{
				if (!Selected.HasValue)
				{
					return 0;
				}
				return Array.IndexOf(available, Selected.Value) + 1;
			}
		}

		public BlueprintLayers(IEnumerable<float> origins)
		{
			this.origins = origins.ToArray();
			available = GetAvailable();
		}

		private int Index(float height)
		{
			return (int)Math.Floor((double)height / (double)Height);
		}

		private int[] GetAvailable()
		{
			return (from index in origins.Select(Index).Distinct()
				orderby index
				select index).ToArray();
		}

		public bool Contains(float originHeight)
		{
			if (Selected.HasValue)
			{
				return Index(originHeight) == Selected.Value;
			}
			return true;
		}

		public void All()
		{
			Selected = null;
		}

		public void Bottom()
		{
			if (available.Length != 0)
			{
				Selected = available[0];
			}
		}

		public bool Select(int? layer)
		{
			if (layer.HasValue && Array.IndexOf(available, layer.Value) < 0)
			{
				return false;
			}
			Selected = layer;
			return true;
		}

		public void Move(int direction)
		{
			if (available.Length != 0)
			{
				if (!Selected.HasValue)
				{
					Bottom();
					return;
				}
				int num = Array.IndexOf(available, Selected.Value);
				num = Math.Max(0, Math.Min(available.Length - 1, num + Math.Sign(direction)));
				Selected = available[num];
			}
		}

		public bool SetHeight(float height)
		{
			if (float.IsNaN(height) || float.IsInfinity(height) || height < 0.5f || height > 4f)
			{
				return false;
			}
			if (height == Height)
			{
				return true;
			}
			Height = height;
			available = GetAvailable();
			if (Selected.HasValue)
			{
				Bottom();
			}
			return true;
		}
	}
	internal static class BlueprintLibrary
	{
		public static bool TryList(string directory, out string[] files, out string error)
		{
			files = Array.Empty<string>();
			error = string.Empty;
			try
			{
				Directory.CreateDirectory(directory);
				files = (from x in Directory.EnumerateFiles(directory, "*", SearchOption.AllDirectories)
					where Path.GetExtension(x).Equals(".blueprint", StringComparison.OrdinalIgnoreCase) || Path.GetExtension(x).Equals(".vbuild", StringComparison.OrdinalIgnoreCase)
					select x).OrderBy<string, string>((string x) => x, StringComparer.OrdinalIgnoreCase).ToArray();
				return true;
			}
			catch (Exception ex) when (IsFileError(ex))
			{
				error = "Cannot list blueprints in " + directory + ": " + ex.Message + " Check the directory setting and permissions.";
				return false;
			}
		}

		public static bool TryLoad(string path, out BlueprintDocument document, out string error)
		{
			document = null;
			error = string.Empty;
			try
			{
				if (new FileInfo(path).Length > 8388608)
				{
					error = "Blueprint exceeds 8 MB. Split it into smaller files before loading.";
					return false;
				}
				return BlueprintDocument.TryParse(Path.GetFileNameWithoutExtension(path), File.ReadLines(path), Path.GetExtension(path).Equals(".vbuild", StringComparison.OrdinalIgnoreCase), out document, out error);
			}
			catch (Exception ex) when (IsFileError(ex))
			{
				error = "Cannot load " + Path.GetFileName(path) + ": " + ex.Message + " Check the file and try again.";
				return false;
			}
		}

		public static List<PieceEntry> CapturePieces(CaptureBounds bounds, Vector3 origin)
		{
			//IL_003a: 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_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b4: 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_00c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
			List<PieceEntry> list = new List<PieceEntry>();
			foreach (Piece s_allPiece in Piece.s_allPieces)
			{
				if (!Object.op_Implicit((Object)(object)s_allPiece) || !s_allPiece.IsPlacedByPlayer())
				{
					continue;
				}
				Vector3 position = ((Component)s_allPiece).transform.position;
				if (!bounds.Contains(position.x, position.y, position.z))
				{
					continue;
				}
				ZNetView component = ((Component)s_allPiece).GetComponent<ZNetView>();
				if (Object.op_Implicit((Object)(object)component) && component.IsValid())
				{
					GameObject prefab = ZNetScene.instance.GetPrefab(component.GetZDO().GetPrefab());
					if (Object.op_Implicit((Object)(object)prefab) && Object.op_Implicit((Object)(object)prefab.GetComponent<Piece>()))
					{
						list.Add(new PieceEntry(((Object)prefab).name, "Building", position - origin, ((Component)s_allPiece).transform.rotation, string.Empty, ((Component)s_allPiece).transform.lossyScale));
					}
				}
			}
			return list;
		}

		public static bool TryCapture(string directory, string name, CaptureBounds bounds, Vector3 origin, out string error)
		{
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			error = string.Empty;
			name = name.Trim();
			if (name.Length == 0 || name.Length > 80 || name.Any((char c) => !char.IsLetterOrDigit(c) && c != ' ' && c != '_' && c != '-'))
			{
				error = "Use a name of 1 to 80 letters, numbers, spaces, underscores or hyphens.";
				return false;
			}
			if (bounds == null || !bounds.HasVolume)
			{
				error = "Select two corners with width, height and depth. Use Alt + wheel to raise the upper corner.";
				return false;
			}
			List<PieceEntry> list = CapturePieces(bounds, origin);
			if (list.Count == 0 || list.Count > 10000)
			{
				error = $"Found {list.Count} pieces. Adjust the box to contain 1 to {10000} loaded, player-built pieces.";
				return false;
			}
			try
			{
				Directory.CreateDirectory(directory);
				using (FileStream stream = new FileStream(Path.Combine(directory, name + ".blueprint"), FileMode.CreateNew, FileAccess.Write))
				{
					using StreamWriter streamWriter = new StreamWriter(stream);
					foreach (string item in new BlueprintDocument(name, list).Serialize())
					{
						streamWriter.WriteLine(item);
					}
				}
				return true;
			}
			catch (Exception ex) when (IsFileError(ex))
			{
				error = "Cannot save " + name + ": " + ex.Message + " Existing files were not overwritten. Choose a new name or check directory permissions.";
				return false;
			}
		}

		private static bool IsFileError(Exception ex)
		{
			if (!(ex is IOException) && !(ex is UnauthorizedAccessException) && !(ex is ArgumentException))
			{
				return ex is NotSupportedException;
			}
			return true;
		}
	}
	internal sealed class BlueprintMaterialsHud : IDisposable
	{
		private static BlueprintMaterialsHud instance;

		private readonly Harmony harmony = new Harmony("augusdogus.Buildheim.materialHud");

		private readonly Func<Piece> hoveredPiece;

		public BlueprintMaterialsHud(Func<Piece> hoveredPiece)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Expected O, but got Unknown
			this.hoveredPiece = hoveredPiece;
			instance = this;
			harmony.PatchAll(typeof(BlueprintMaterialsHud));
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(Hud), "SetupPieceInfo")]
		private static void ShowBlueprintMaterials(Hud __instance, Piece piece)
		{
			if (instance != null && Object.op_Implicit((Object)(object)piece) && piece.m_repairPiece)
			{
				Piece val = instance.hoveredPiece();
				if (Object.op_Implicit((Object)(object)val) && !val.m_repairPiece && !val.m_removePiece)
				{
					__instance.SetupPieceInfo(val);
				}
			}
		}

		public void Dispose()
		{
			harmony.UnpatchSelf();
			instance = null;
		}
	}
	internal sealed class BlueprintProjection : IDisposable
	{
		internal sealed class Part
		{
			public Mesh Mesh { get; }

			public Matrix4x4 LocalMatrix { get; }

			public BlueprintHitTest HitTest { get; }

			public Part(Mesh mesh, Matrix4x4 matrix, BlueprintHitTest hitTest)
			{
				//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)
				Mesh = mesh;
				LocalMatrix = matrix;
				HitTest = hitTest;
			}
		}

		internal sealed class ProjectedPiece
		{
			public PieceEntry Entry { get; }

			public GameObject Prefab { get; }

			public IReadOnlyList<Part> Parts { get; }

			public Bounds LocalBounds { get; }

			public bool Completed { get; set; }

			public ProjectedPiece(PieceEntry entry, GameObject prefab, IReadOnlyList<Part> parts)
			{
				//IL_001d: Unknown result type (might be due to invalid IL or missing references)
				//IL_003e: Unknown result type (might be due to invalid IL or missing references)
				//IL_0043: Unknown result type (might be due to invalid IL or missing references)
				//IL_00dd: 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_004c: 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_0077: Unknown result type (might be due to invalid IL or missing references)
				//IL_007c: Unknown result type (might be due to invalid IL or missing references)
				//IL_007f: Unknown result type (might be due to invalid IL or missing references)
				//IL_0084: Unknown result type (might be due to invalid IL or missing references)
				//IL_008a: Unknown result type (might be due to invalid IL or missing references)
				//IL_008f: 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_0096: Unknown result type (might be due to invalid IL or missing references)
				//IL_009b: Unknown result type (might be due to invalid IL or missing references)
				//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
				//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
				//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
				//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
				//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
				Entry = entry;
				Prefab = prefab;
				Parts = parts;
				Bounds val = default(Bounds);
				bool flag = true;
				foreach (Part part in parts)
				{
					Bounds bounds = part.Mesh.bounds;
					for (int i = 0; i < 8; i++)
					{
						Vector3 val2 = Vector3.Scale(((Bounds)(ref bounds)).extents, new Vector3((float)(((i & 1) != 0) ? 1 : (-1)), (float)(((i & 2) != 0) ? 1 : (-1)), (float)(((i & 4) != 0) ? 1 : (-1))));
						Matrix4x4 localMatrix = part.LocalMatrix;
						Vector3 val3 = ((Matrix4x4)(ref localMatrix)).MultiplyPoint3x4(((Bounds)(ref bounds)).center + val2);
						if (flag)
						{
							val = new Bounds(val3, Vector3.zero);
							flag = false;
						}
						else
						{
							((Bounds)(ref val)).Encapsulate(val3);
						}
					}
				}
				LocalBounds = val;
			}
		}

		private readonly Material material;

		private readonly Material aimedMaterial;

		public string Name { get; }

		public IReadOnlyList<ProjectedPiece> Pieces { get; }

		public BlueprintLayers Layers { get; }

		public int MissingPrefabs { get; }

		public Vector3 Position { get; set; }

		public float Yaw { get; set; }

		public bool Enabled { get; set; } = true;

		public Quaternion Rotation => Quaternion.Euler(0f, Yaw, 0f);

		private BlueprintProjection(BlueprintDocument document, List<ProjectedPiece> pieces, int missing, Material material)
		{
			//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)
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Expected O, but got Unknown
			Name = document.Name;
			Pieces = pieces;
			Layers = new BlueprintLayers(pieces.Select((ProjectedPiece piece) => piece.Entry.posY));
			MissingPrefabs = missing;
			this.material = material;
			aimedMaterial = new Material(material)
			{
				color = new Color(1f, 0.85f, 0.2f, 0.3f)
			};
		}

		public static bool TryCreate(BlueprintDocument document, out BlueprintProjection projection, out string error)
		{
			//IL_025d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0262: Unknown result type (might be due to invalid IL or missing references)
			//IL_0277: Unknown result type (might be due to invalid IL or missing references)
			//IL_0281: Unknown result type (might be due to invalid IL or missing references)
			//IL_028e: Expected O, but got Unknown
			//IL_01de: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ef: Unknown result type (might be due to invalid IL or missing references)
			projection = null;
			error = string.Empty;
			Shader prefab = Cache.GetPrefab<Shader>("Lux Lit Particles/ Bumped");
			if (!Object.op_Implicit((Object)(object)prefab))
			{
				error = "The blueprint shader is unavailable. Rejoin the world and try again.";
				return false;
			}
			List<ProjectedPiece> list = new List<ProjectedPiece>();
			int num = 0;
			Dictionary<Mesh, BlueprintHitTest> dictionary = new Dictionary<Mesh, BlueprintHitTest>();
			foreach (PieceEntry piece in document.Pieces)
			{
				GameObject prefab2 = ZNetScene.instance.GetPrefab(piece.name);
				if (!Object.op_Implicit((Object)(object)prefab2))
				{
					num++;
					continue;
				}
				List<Part> list2 = new List<Part>();
				HashSet<Renderer> hashSet = new HashSet<Renderer>(prefab2.GetComponentsInChildren<LODGroup>(true).SelectMany((LODGroup group) => group.GetLODs().Skip(1)).SelectMany((LOD lod) => lod.renderers));
				MeshFilter[] componentsInChildren = prefab2.GetComponentsInChildren<MeshFilter>(true);
				foreach (MeshFilter val in componentsInChildren)
				{
					MeshRenderer component = ((Component)val).GetComponent<MeshRenderer>();
					if (!Object.op_Implicit((Object)(object)val.sharedMesh) || !Object.op_Implicit((Object)(object)component) || !((Renderer)component).enabled || hashSet.Contains((Renderer)(object)component) || !ActiveBelowRoot(((Component)val).transform, prefab2.transform))
					{
						continue;
					}
					Mesh mesh = val.sharedMesh;
					if (!dictionary.TryGetValue(mesh, out var value))
					{
						value = ((mesh.isReadable && Enumerable.Range(0, mesh.subMeshCount).All((int i) => (int)mesh.GetTopology(i) == 0)) ? new BlueprintHitTest(mesh.vertices, mesh.triangles) : null);
						dictionary.Add(mesh, value);
					}
					list2.Add(new Part(mesh, prefab2.transform.worldToLocalMatrix * ((Component)val).transform.localToWorldMatrix, value));
				}
				if (list2.Count == 0)
				{
					num++;
				}
				else
				{
					list.Add(new ProjectedPiece(piece, prefab2, list2));
				}
			}
			if (list.Count == 0)
			{
				error = "No pieces in this blueprint can be displayed. Check its required piece mods.";
				return false;
			}
			Material val2 = new Material(prefab)
			{
				color = new Color(0.15f, 0.8f, 1f, 0.3f),
				renderQueue = 3000
			};
			val2.SetOverrideTag("RenderType", "Transparent");
			val2.SetFloat("_ZWrite", 0f);
			val2.SetFloat("_SrcBlend", 5f);
			val2.SetFloat("_DstBlend", 10f);
			val2.DisableKeyword("DIRECTIONAL");
			projection = new BlueprintProjection(document, list, num, val2);
			return true;
		}

		private static bool ActiveBelowRoot(Transform node, Transform root)
		{
			while ((Object)(object)node != (Object)(object)root)
			{
				if (!((Component)node).gameObject.activeSelf)
				{
					return false;
				}
				node = node.parent;
			}
			return true;
		}

		public Vector3 PiecePosition(ProjectedPiece piece)
		{
			//IL_0001: 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_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_001c: Unknown result type (might be due to invalid IL or missing references)
			return Position + Rotation * piece.Entry.GetPosition();
		}

		public Quaternion PieceRotation(ProjectedPiece piece)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			return Rotation * piece.Entry.GetRotation();
		}

		public Matrix4x4 PieceMatrix(ProjectedPiece piece)
		{
			//IL_0002: 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_0014: 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)
			return Matrix4x4.TRS(PiecePosition(piece), PieceRotation(piece), piece.Entry.GetScale());
		}

		public void Draw(ProjectedPiece selected = null, ProjectedPiece aimed = null)
		{
			//IL_006a: 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_0094: 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_009c: Unknown result type (might be due to invalid IL or missing references)
			Camera val = (Object.op_Implicit((Object)(object)GameCamera.instance) ? ((Component)GameCamera.instance).GetComponent<Camera>() : null);
			if (!Object.op_Implicit((Object)(object)val))
			{
				return;
			}
			foreach (ProjectedPiece piece in Pieces)
			{
				if (piece.Completed || !Layers.Contains(piece.Entry.posY) || piece == selected)
				{
					continue;
				}
				Matrix4x4 val2 = PieceMatrix(piece);
				foreach (Part part in piece.Parts)
				{
					for (int i = 0; i < part.Mesh.subMeshCount; i++)
					{
						Graphics.DrawMesh(part.Mesh, val2 * part.LocalMatrix, (piece == aimed) ? aimedMaterial : material, 0, val, i, (MaterialPropertyBlock)null, (ShadowCastingMode)0, false);
					}
				}
			}
		}

		public void Dispose()
		{
			Object.Destroy((Object)(object)material);
			Object.Destroy((Object)(object)aimedMaterial);
		}
	}
	internal static class BlueprintRotation
	{
		public const float Step = 22.5f;

		public static float Turn(float yaw, int direction, bool coarse)
		{
			double num = (yaw % 360f + 360f) % 360f / 22.5f;
			int num2 = ((!coarse) ? 1 : 4);
			return (float)((((direction > 0) ? (Math.Floor(num + 1E-06) + (double)num2) : (Math.Ceiling(num - 1E-06) - (double)num2)) * 22.5 % 360.0 + 360.0) % 360.0);
		}
	}
	internal abstract class BlueprintSelection
	{
		internal sealed class Hammer : BlueprintSelection
		{
			public HammerTarget Target { get; }

			public Hammer(HammerTarget target)
				: base(target.Planned)
			{
				Target = target;
			}
		}

		internal sealed class Guide : BlueprintSelection
		{
			public string Reason { get; }

			public Guide(BlueprintProjection.ProjectedPiece planned, string reason)
				: base(planned)
			{
				Reason = reason;
			}
		}

		public BlueprintProjection.ProjectedPiece Planned { get; }

		private BlueprintSelection(BlueprintProjection.ProjectedPiece planned)
		{
			Planned = planned;
		}
	}
	internal static class BuildMaterials
	{
		public static bool HasAll(IEnumerable<KeyValuePair<string, int>> costs, Func<string, int> inventoryCount)
		{
			foreach (KeyValuePair<string, long> item in Total(costs))
			{
				if (inventoryCount(item.Key) < item.Value)
				{
					return false;
				}
			}
			return true;
		}

		public static Dictionary<string, long> Missing(IEnumerable<KeyValuePair<string, int>> costs, Func<string, int> inventoryCount)
		{
			Dictionary<string, long> dictionary = new Dictionary<string, long>();
			foreach (KeyValuePair<string, long> item in Total(costs))
			{
				long num = item.Value - inventoryCount(item.Key);
				if (num > 0)
				{
					dictionary.Add(item.Key, num);
				}
			}
			return dictionary;
		}

		public static Dictionary<string, long> Total(IEnumerable<KeyValuePair<string, int>> costs)
		{
			Dictionary<string, long> dictionary = new Dictionary<string, long>();
			foreach (KeyValuePair<string, int> cost in costs)
			{
				if (cost.Value > 0)
				{
					dictionary.TryGetValue(cost.Key, out var value);
					dictionary[cost.Key] = value + cost.Value;
				}
			}
			return dictionary;
		}
	}
	internal enum BuildMode
	{
		Guide,
		Assisted,
		Automatic
	}
	internal sealed class CaptureBounds
	{
		public float MinX { get; }

		public float MinY { get; }

		public float MinZ { get; }

		public float MaxX { get; }

		public float MaxY { get; }

		public float MaxZ { get; }

		public float Width => MaxX - MinX;

		public float Height => MaxY - MinY;

		public float Depth => MaxZ - MinZ;

		public bool HasVolume
		{
			get
			{
				if (Width > 0f && Height > 0f)
				{
					return Depth > 0f;
				}
				return false;
			}
		}

		private CaptureBounds(float ax, float ay, float az, float bx, float by, float bz)
		{
			MinX = Math.Min(ax, bx);
			MinY = Math.Min(ay, by);
			MinZ = Math.Min(az, bz);
			MaxX = Math.Max(ax, bx);
			MaxY = Math.Max(ay, by);
			MaxZ = Math.Max(az, bz);
		}

		public bool Contains(float x, float y, float z)
		{
			if (x >= MinX && x <= MaxX && y >= MinY && y <= MaxY && z >= MinZ)
			{
				return z <= MaxZ;
			}
			return false;
		}

		public static bool TryCreate(float ax, float ay, float az, float bx, float by, float bz, out CaptureBounds bounds)
		{
			bounds = null;
			float[] array = new float[6] { ax, ay, az, bx, by, bz };
			foreach (float f in array)
			{
				if (float.IsNaN(f) || float.IsInfinity(f))
				{
					return false;
				}
			}
			CaptureBounds captureBounds = new CaptureBounds(ax, ay, az, bx, by, bz);
			if (float.IsInfinity(captureBounds.Width) || float.IsInfinity(captureBounds.Height) || float.IsInfinity(captureBounds.Depth))
			{
				return false;
			}
			bounds = captureBounds;
			return true;
		}
	}
	internal sealed class CaptureOutline : IDisposable
	{
		private Mesh mesh;

		private Material material;

		private readonly MaterialPropertyBlock color = new MaterialPropertyBlock();

		public void Draw(Vector3? first, Vector3? second)
		{
			//IL_0045: 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_005a: Expected O, but got Unknown
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c6: Expected O, but got Unknown
			//IL_01b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_014d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0154: 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_0160: Unknown result type (might be due to invalid IL or missing references)
			//IL_0167: 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)
			//IL_0173: Unknown result type (might be due to invalid IL or missing references)
			//IL_017a: Unknown result type (might be due to invalid IL or missing references)
			//IL_017f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0184: Unknown result type (might be due to invalid IL or missing references)
			//IL_019d: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0202: Unknown result type (might be due to invalid IL or missing references)
			//IL_0207: Unknown result type (might be due to invalid IL or missing references)
			//IL_020c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0216: Unknown result type (might be due to invalid IL or missing references)
			//IL_022a: 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)
			if ((!first.HasValue && !second.HasValue) || !Object.op_Implicit((Object)(object)GameCamera.instance))
			{
				return;
			}
			if (!Object.op_Implicit((Object)(object)material))
			{
				Shader val = Shader.Find("Hidden/Internal-Colored");
				if (!Object.op_Implicit((Object)(object)val))
				{
					return;
				}
				material = new Material(val)
				{
					renderQueue = 4000
				};
				material.SetInt("_SrcBlend", 5);
				material.SetInt("_DstBlend", 10);
				material.SetInt("_Cull", 0);
				material.SetInt("_ZWrite", 0);
				material.SetInt("_ZTest", 8);
				mesh = new Mesh
				{
					name = "Buildheim capture box"
				};
				Vector3[] array = (Vector3[])(object)new Vector3[8];
				for (int i = 0; i < array.Length; i++)
				{
					array[i] = new Vector3((float)(i & 1), (float)((i >> 1) & 1), (float)((i >> 2) & 1));
				}
				mesh.vertices = array;
				mesh.SetIndices(new int[24]
				{
					0, 1, 2, 3, 4, 5, 6, 7, 0, 2,
					1, 3, 4, 6, 5, 7, 0, 4, 1, 5,
					2, 6, 3, 7
				}, (MeshTopology)3, 0);
				mesh.RecalculateBounds();
			}
			Camera component = ((Component)GameCamera.instance).GetComponent<Camera>();
			if (first.HasValue && second.HasValue)
			{
				Box(Vector3.Min(first.Value, second.Value), Vector3.Max(first.Value, second.Value) - Vector3.Min(first.Value, second.Value), new Color(0.3f, 0.9f, 1f, 0.8f), component);
			}
			if (first.HasValue)
			{
				Box(first.Value - Vector3.one * 0.12f, Vector3.one * 0.24f, Color.cyan, component);
			}
			if (second.HasValue)
			{
				Box(second.Value - Vector3.one * 0.12f, Vector3.one * 0.24f, new Color(1f, 0.65f, 0.15f), component);
			}
		}

		private void Box(Vector3 position, Vector3 size, Color tint, Camera camera)
		{
			//IL_000b: 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_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			color.SetColor("_Color", tint);
			Graphics.DrawMesh(mesh, Matrix4x4.TRS(position, Quaternion.identity, size), material, 0, camera, 0, color, (ShadowCastingMode)0, false);
		}

		public void Dispose()
		{
			if (Object.op_Implicit((Object)(object)mesh))
			{
				Object.Destroy((Object)(object)mesh);
			}
			if (Object.op_Implicit((Object)(object)material))
			{
				Object.Destroy((Object)(object)material);
			}
		}
	}
	internal sealed class CapturePanel
	{
		private readonly ClientPlanner planner;

		private readonly Text corners;

		private readonly Text dimensions;

		private readonly Button save;

		public CapturePanel(ClientPlanner planner, Transform parent)
		{
			this.planner = planner;
			PlannerWidgets.Label(parent, "Capture a building", 0f, 0f, 644f, 32f, 22, heading: true);
			InputField name = PlannerWidgets.Input(parent, "Blueprint name", 0f, 46f, 644f);
			PlannerWidgets.Button(parent, "Select / edit box", 0f, 100f, 314f, planner.BeginCapture);
			PlannerWidgets.Button(parent, "Clear selection", 330f, 100f, 314f, planner.Selection.Clear);
			corners = PlannerWidgets.Label(parent, "", 0f, 150f, 644f, 58f);
			dimensions = PlannerWidgets.Label(parent, "", 0f, 216f, 644f, 48f, 20, heading: true);
			PlannerWidgets.Label(parent, "Pick opposite corners with left and right click. The wheel adjusts the selected corner; Alt changes its height. Middle click switches corners.", 0f, 274f, 644f, 60f, 17);
			PlannerWidgets.Label(parent, "Includes loaded, player-built pieces whose placement point is inside the box. Corner A is the blueprint origin.", 0f, 340f, 644f, 48f, 16);
			save = PlannerWidgets.Button(parent, "Save blueprint", 0f, 400f, 644f, delegate
			{
				planner.Capture(name.text);
			});
		}

		public void Refresh()
		{
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			CaptureSelection selection = planner.Selection;
			corners.text = "A (cyan): " + Position(selection.First) + "\nB (orange): " + Position(selection.Second);
			CaptureBounds bounds;
			bool flag = selection.TryBounds(out bounds) && bounds.HasVolume;
			int num = (flag ? BlueprintLibrary.CapturePieces(bounds, selection.First.Value).Count : 0);
			dimensions.text = ((bounds == null) ? "Select two opposite corners to start." : $"{bounds.Width:0.0} × {bounds.Height:0.0} × {bounds.Depth:0.0} m | {num} pieces");
			if (bounds != null && !flag)
			{
				Text obj = dimensions;
				obj.text += "\nGive the box width, height and depth.";
			}
			else if (flag && num == 0)
			{
				Text obj2 = dimensions;
				obj2.text += "\nMove the box around a player-built structure.";
			}
			else if (num > 10000)
			{
				Text obj3 = dimensions;
				obj3.text += $"\nReduce the selection to {10000} pieces.";
			}
			((Selectable)save).interactable = flag && num > 0 && num <= 10000;
		}

		private static string Position(Vector3? point)
		{
			//IL_0016: 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_0038: Unknown result type (might be due to invalid IL or missing references)
			if (!point.HasValue)
			{
				return "not selected";
			}
			return $"{point.Value.x:0.0}, {point.Value.y:0.0}, {point.Value.z:0.0}";
		}
	}
	internal sealed class CaptureSelection : IDisposable
	{
		private static CaptureSelection instance;

		private readonly Harmony harmony = new Harmony("augusdogus.Buildheim.capture");

		private readonly Func<bool> canTakeInput;

		private readonly CaptureOutline outline = new CaptureOutline();

		private int startedFrame;

		private int scrollFrame = -1;

		private bool firstSelected = true;

		public const string Hints = "Left click: corner A | Right click: corner B | Middle click: switch corner\nCtrl + wheel: forward/back | Ctrl + X + wheel: sideways | Alt + wheel: height | Shift: 1 m";

		public Vector3? First { get; private set; }

		public Vector3? Second { get; private set; }

		public bool Editing { get; private set; }

		public string SelectedCorner
		{
			get
			{
				if (!firstSelected)
				{
					return "B";
				}
				return "A";
			}
		}

		public string Status { get; private set; } = "Aim at the ground or a building and pick two opposite corners.";

		private bool Active
		{
			get
			{
				if (Editing)
				{
					return canTakeInput();
				}
				return false;
			}
		}

		private static bool Control
		{
			get
			{
				if (!Input.GetKey((KeyCode)306))
				{
					return Input.GetKey((KeyCode)305);
				}
				return true;
			}
		}

		private static bool Alt
		{
			get
			{
				if (!Input.GetKey((KeyCode)308))
				{
					return Input.GetKey((KeyCode)307);
				}
				return true;
			}
		}

		public CaptureSelection(Func<bool> canTakeInput)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Expected O, but got Unknown
			this.canTakeInput = canTakeInput;
			instance = this;
			harmony.PatchAll(typeof(CaptureSelection));
		}

		public void Begin()
		{
			Editing = true;
			startedFrame = Time.frameCount;
		}

		public void End()
		{
			Editing = false;
		}

		public void Clear()
		{
			End();
			Vector3? first = (Second = null);
			First = first;
		}

		public bool TryBounds(out CaptureBounds bounds)
		{
			//IL_002f: 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_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			bounds = null;
			if (First.HasValue && Second.HasValue)
			{
				return CaptureBounds.TryCreate(First.Value.x, First.Value.y, First.Value.z, Second.Value.x, Second.Value.y, Second.Value.z, out bounds);
			}
			return false;
		}

		public void Update()
		{
			//IL_0069: 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)
			if (!Active || Time.frameCount == startedFrame)
			{
				return;
			}
			if (Input.GetMouseButtonDown(2))
			{
				firstSelected = !firstSelected;
			}
			if (Input.GetMouseButtonDown(0) || Input.GetMouseButtonDown(1))
			{
				firstSelected = Input.GetMouseButtonDown(0);
				if (TryAim(out var point))
				{
					if (firstSelected)
					{
						First = point;
					}
					else
					{
						Second = point;
					}
					Status = "Corner " + SelectedCorner + " set. Use modifier keys and the wheel to adjust it.";
				}
				else
				{
					Status = "No surface within 100 m. Aim at the ground or a building to set a corner.";
				}
			}
			if (Control || Alt)
			{
				ZInput.GetMouseScrollWheel();
			}
		}

		private static bool TryAim(out Vector3 point)
		{
			//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_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			point = Vector3.zero;
			if (!Object.op_Implicit((Object)(object)GameCamera.instance))
			{
				return false;
			}
			Camera component = ((Component)GameCamera.instance).GetComponent<Camera>();
			int mask = LayerMask.GetMask(new string[5] { "Default", "static_solid", "Default_small", "piece", "terrain" });
			RaycastHit val = default(RaycastHit);
			if (!Physics.Raycast(component.ViewportPointToRay(new Vector3(0.5f, 0.5f)), ref val, 100f, mask, (QueryTriggerInteraction)1))
			{
				return false;
			}
			point = ((RaycastHit)(ref val)).point;
			return true;
		}

		public void Draw(bool showInMenu)
		{
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			if (Editing || showInMenu)
			{
				Vector3? second = Second;
				if (Active && First.HasValue && !second.HasValue && TryAim(out var point))
				{
					second = point;
				}
				outline.Draw(First, second);
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(ZInput), "GetMouseScrollWheel")]
		private static void Scroll(ref float __result)
		{
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_0101: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d8: 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_011a: 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_0144: Unknown result type (might be due to invalid IL or missing references)
			//IL_0132: Unknown result type (might be due to invalid IL or missing references)
			if (instance == null || !instance.Active || (!Control && !Alt))
			{
				return;
			}
			float num = __result;
			__result = 0f;
			if (num == 0f || instance.scrollFrame == Time.frameCount)
			{
				return;
			}
			instance.scrollFrame = Time.frameCount;
			Vector3? val = (instance.firstSelected ? instance.First : instance.Second);
			if (val.HasValue)
			{
				Vector3 val2 = Vector3.up;
				if (!Alt && Object.op_Implicit((Object)(object)GameCamera.instance))
				{
					val2 = Quaternion.Euler(0f, ((Component)GameCamera.instance).transform.eulerAngles.y, 0f) * (Input.GetKey((KeyCode)120) ? Vector3.right : Vector3.forward);
				}
				bool flag = Input.GetKey((KeyCode)304) || Input.GetKey((KeyCode)303);
				Vector3 value = val.Value + val2 * Mathf.Sign(num) * (flag ? 1f : 0.1f);
				if (instance.firstSelected)
				{
					instance.First = value;
				}
				else
				{
					instance.Second = value;
				}
			}
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(Player), "SetControls")]
		private static void SuppressActions(Player __instance, ref bool attack, ref bool attackHold, ref bool secondaryAttack, ref bool secondaryAttackHold, ref bool block, ref bool blockHold, ref bool crouch, ref bool dodge)
		{
			if (instance != null && instance.Active && !((Object)(object)__instance != (Object)(object)Player.m_localPlayer))
			{
				attack = (attackHold = (secondaryAttack = (secondaryAttackHold = (block = (blockHold = (dodge = false))))));
				if (Control)
				{
					crouch = false;
				}
			}
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(Player), "UpdatePlacement")]
		private static bool SuppressPlacement(Player __instance)
		{
			if (instance == null || !instance.Editing || (Object)(object)__instance != (Object)(object)Player.m_localPlayer)
			{
				return true;
			}
			__instance.m_placePressedTime = (__instance.m_removePressedTime = -9999f);
			if (Object.op_Implicit((Object)(object)__instance.m_placementGhost))
			{
				__instance.m_placementGhost.SetActive(false);
			}
			if (Object.op_Implicit((Object)(object)__instance.m_placementMarkerInstance))
			{
				__instance.m_placementMarkerInstance.SetActive(false);
			}
			return false;
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(ZInput), "GetButtonDown")]
		private static bool SuppressSit(string name, ref bool __result)
		{
			if (instance == null || !instance.Active || !Control || name != "Sit" || !Input.GetKey((KeyCode)120))
			{
				return true;
			}
			__result = false;
			return false;
		}

		public void Dispose()
		{
			harmony.UnpatchSelf();
			instance = null;
			outline.Dispose();
		}
	}
	internal sealed class ChestObservation : IDisposable
	{
		private sealed class Subscription : IDisposable
		{
			private readonly Inventory inventory;

			private readonly Action refresh;

			public Container Container { get; }

			public Subscription(Container container, Action refresh)
			{
				Container = container;
				inventory = container.GetInventory();
				this.refresh = refresh;
				Inventory obj = inventory;
				obj.m_onChanged = (Action)Delegate.Combine(obj.m_onChanged, refresh);
				refresh();
			}

			public void Dispose()
			{
				Inventory obj = inventory;
				obj.m_onChanged = (Action)Delegate.Remove(obj.m_onChanged, refresh);
			}
		}

		private readonly MaterialChecklist checklist;

		private readonly Dictionary<string, Subscription> subscriptions = new Dictionary<string, Subscription>();

		public ChestObservation(MaterialChecklist checklist)
		{
			this.checklist = checklist;
		}

		public static IEnumerable<KeyValuePair<string, int>> Items(Inventory inventory)
		{
			return from item in inventory.GetAllItems()
				select new KeyValuePair<string, int>(item.m_shared.m_name, item.m_stack);
		}

		public void Update()
		{
			KeyValuePair<string, Subscription>[] array = subscriptions.Where((KeyValuePair<string, Subscription> entry) => !Object.op_Implicit((Object)(object)entry.Value.Container)).ToArray();
			for (int num = 0; num < array.Length; num++)
			{
				KeyValuePair<string, Subscription> keyValuePair = array[num];
				keyValuePair.Value.Dispose();
				subscriptions.Remove(keyValuePair.Key);
			}
			Container container = (Object.op_Implicit((Object)(object)InventoryGui.instance) ? InventoryGui.instance.m_currentContainer : null);
			if (!Object.op_Implicit((Object)(object)container) || !InventoryGui.IsVisible() || !container.IsOwner())
			{
				return;
			}
			ZNetView component = ((Component)container).GetComponent<ZNetView>();
			if (!Object.op_Implicit((Object)(object)component) || !component.IsValid())
			{
				return;
			}
			string key = ((object)Unsafe.As<ZDOID, ZDOID>(ref component.GetZDO().m_uid)/*cast due to .constrained prefix*/).ToString();
			if (!subscriptions.ContainsKey(key))
			{
				subscriptions.Add(key, new Subscription(container, delegate
				{
					checklist.Observe(key, Items(container.GetInventory()));
				}));
			}
		}

		public void Dispose()
		{
			foreach (Subscription value in subscriptions.Values)
			{
				value.Dispose();
			}
			subscriptions.Clear();
			checklist.ForgetChests();
		}
	}
	internal sealed class ClientConfig
	{
		public string DataDirectory { get; }

		public ConfigEntry<string> Directory { get; }

		public ConfigEntry<KeyCode> ToggleKey { get; }

		public ConfigEntry<KeyCode> AutoBuildKey { get; }

		public ConfigEntry<KeyCode> PlacementKey { get; }

		public ConfigEntry<KeyCode> HudKey { get; }

		public ConfigEntry<bool> ShowHud { get; }

		public ClientConfig(ConfigFile config)
		{
			DataDirectory = ClientPaths.DataDirectory(Paths.ConfigPath);
			Directory = config.Bind<string>("Client", "Blueprint directory", Path.Combine(DataDirectory, "blueprints"), "Local directory for .blueprint and .vbuild files. Nothing is uploaded to the server.");
			ToggleKey = config.Bind<KeyCode>("Client", "Planner key", (KeyCode)279, "Open or close the blueprint planner.");
			AutoBuildKey = config.Bind<KeyCode>("Client", "Autobuild key", (KeyCode)278, "Toggle autobuild for the current blueprint while the planner is closed.");
			PlacementKey = config.Bind<KeyCode>("Client", "Placement key", (KeyCode)288, "Enable or disable the current placement without clearing it. Disabling hides the hologram and stops build assistance.");
			HudKey = config.Bind<KeyCode>("Client", "HUD key", (KeyCode)289, "Show or hide Buildheim's HUD while the planner is closed. Does not change placement or build mode.");
			ShowHud = config.Bind<bool>("Client", "Show HUD", true, "Show Buildheim's build status and shortcut hints. Hiding the HUD does not stop autobuild or hide the hologram.");
		}
	}
	internal static class ClientPaths
	{
		public static string DataDirectory(string configRoot)
		{
			string text = Path.Combine(configRoot, "Buildheim");
			string text2 = Path.Combine(configRoot, "PlanBuild");
			if (!Directory.Exists(text) && Directory.Exists(text2))
			{
				return text2;
			}
			return text;
		}

		public static bool TryMigrateConfig(string configRoot, string destination, out bool copied, out string error)
		{
			copied = false;
			error = string.Empty;
			string text = Path.Combine(configRoot, "marcopogo.PlanBuild.cfg");
			try
			{
				if (File.Exists(destination) || !File.Exists(text))
				{
					return true;
				}
				File.Copy(text, destination, overwrite: false);
				copied = true;
				return true;
			}
			catch (Exception ex) when (ex is IOException || ex is UnauthorizedAccessException)
			{
				error = "Buildheim could not copy settings from " + text + " to " + destination + ": " + ex.Message + " The original settings and blueprints are preserved. Check permissions before restarting.";
				return false;
			}
		}
	}
	internal sealed class ClientPlanner : IDisposable
	{
		private readonly HammerAssistance assistance = new HammerAssistance();

		private readonly ProjectionControls controls;

		private readonly ChestObservation chests;

		private readonly PlannerWindow view;

		private readonly PlacementSession session = new PlacementSession();

		private ZNetScene scene;

		private float nextProgressCheck;

		public ClientConfig Config { get; }

		public BlueprintProjection Projection { get; private set; }

		public CaptureSelection Selection { get; }

		public bool PlacementEnabled
		{
			get
			{
				if (Projection != null)
				{
					return Projection.Enabled;
				}
				return false;
			}
		}

		private BlueprintProjection ActiveProjection
		{
			get
			{
				if (!PlacementEnabled || Selection.Editing)
				{
					return null;
				}
				return Projection;
			}
		}

		public BuildMode Mode { get; private set; } = BuildMode.Assisted;

		public MaterialChecklist Materials { get; } = new MaterialChecklist();

		public string[] Files { get; private set; } = Array.Empty<string>();

		public string Status { get; private set; } = "Choose a blueprint or select a box to capture a building.";

		public bool Visible { get; private set; }

		public string BuildStatus
		{
			get
			{
				if (!controls.Held)
				{
					return assistance.Status;
				}
				return "Positioning hologram. Release modifiers to build.";
			}
		}

		public bool HasHudActivity
		{
			get
			{
				if (!Selection.Editing && !controls.Held)
				{
					return assistance.AimedPiece != null;
				}
				return true;
			}
		}

		public Piece HoveredPiece
		{
			get
			{
				BlueprintProjection.ProjectedPiece aimedPiece = assistance.AimedPiece;
				if (aimedPiece == null)
				{
					return null;
				}
				return aimedPiece.Prefab.GetComponent<Piece>();
			}
		}

		public ClientPlanner(ClientConfig config)
		{
			Config = config;
			Selection = new CaptureSelection(() => !Visible && Object.op_Implicit((Object)(object)Player.m_localPlayer) && !((Character)Player.m_localPlayer).IsDead() && ((Character)Player.m_localPlayer).TakeInput() && !Hud.IsPieceSelectionVisible());
			controls = new ProjectionControls(() => (Visible || !Object.op_Implicit((Object)(object)Player.m_localPlayer) || ((Character)Player.m_localPlayer).IsDead() || !((Character)Player.m_localPlayer).TakeInput() || Hud.IsPieceSelectionVisible()) ? null : ActiveProjection);
			chests = new ChestObservation(Materials);
			view = new PlannerWindow(this);
		}

		public void Update()
		{
			//IL_021f: Unknown result type (might be due to invalid IL or missing references)
			//IL_019d: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f5: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)scene != (Object)(object)ZNetScene.instance)
			{
				SaveSession();
				ResetProjection();
				session.Reset();
				chests.Dispose();
				Selection.Clear();
				SetVisible(value: false, playSound: false);
				view.Hide();
				scene = ZNetScene.instance;
			}
			if (!Object.op_Implicit((Object)(object)Player.m_localPlayer) || ((Character)Player.m_localPlayer).IsDead())
			{
				SaveSession();
				if (Mode == BuildMode.Automatic)
				{
					Mode = BuildMode.Assisted;
				}
				Selection.End();
				SetVisible(value: false, playSound: false);
				assistance.SetProjection(null);
				view.Hide();
				return;
			}
			if (!session.Active && Object.op_Implicit((Object)(object)ZNet.instance) && ZNet.instance.GetWorld() != null && Player.m_localPlayer.GetPlayerID() != 0L)
			{
				RestoreSession();
			}
			if (Projection != null)
			{
				chests.Update();
				if (Time.time >= nextProgressCheck)
				{
					if (PlacementEnabled)
					{
						ProjectionProgress.Refresh(Projection);
					}
					nextProgressCheck = Time.time + 0.5f;
					SaveSession();
				}
			}
			if ((!Object.op_Implicit((Object)(object)Settings.instance) || !((Behaviour)Settings.instance).isActiveAndEnabled) && !Console.IsVisible() && (!Object.op_Implicit((Object)(object)Chat.instance) || !Chat.instance.HasFocus()))
			{
				if (!Visible && ((Character)Player.m_localPlayer).TakeInput())
				{
					if (Input.GetKeyDown(Config.PlacementKey.Value))
					{
						SetPlacementEnabled(!PlacementEnabled);
					}
					if (Input.GetKeyDown(Config.HudKey.Value))
					{
						ToggleHud();
					}
					if (!Selection.Editing && PlacementEnabled && Input.GetKeyDown(Config.AutoBuildKey.Value))
					{
						SetMode((Mode == BuildMode.Automatic) ? BuildMode.Assisted : BuildMode.Automatic);
					}
				}
				if (Input.GetKeyDown(Config.ToggleKey.Value))
				{
					if (Visible)
					{
						SetVisible(value: false);
					}
					else if (((Character)Player.m_localPlayer).TakeInput())
					{
						Refresh();
						SetVisible(value: true);
					}
				}
				if (Visible && Input.GetKeyDown((KeyCode)27))
				{
					SetVisible(value: false);
				}
			}
			controls.Update();
			Selection.Update();
			UpdateAssistance();
			view.Update();
		}

		public void SetVisible(bool value, bool playSound = true)
		{
			if (Visible != value)
			{
				Visible = value;
				if (value)
				{
					Hud.HidePieceSelection();
				}
				if (playSound)
				{
					PlannerPresentation.PlaySound(value);
				}
				if (value && Selection.Editing)
				{
					Selection.End();
					view.SelectCapture();
				}
				UpdateAssistance();
				GUIManager.BlockInput(value);
				if (Object.op_Implicit((Object)(object)Player.m_localPlayer))
				{
					Player.m_localPlayer.m_placePressedTime = -9999f;
					Player.m_localPlayer.m_removePressedTime = -9999f;
					ZInput.ResetButtonStatus("Attack");
					ZInput.ResetButtonStatus("JoyPlace");
				}
			}
		}

		public void SetPlacementEnabled(bool enabled)
		{
			if (Projection != null && Projection.Enabled != enabled)
			{
				Projection.Enabled = enabled;
				if (!enabled && Mode == BuildMode.Automatic)
				{
					Mode = BuildMode.Assisted;
				}
				if (enabled)
				{
					ProjectionProgress.Refresh(Projection);
				}
				UpdateAssistance();
				Status = (enabled ? "Placement enabled. Autobuild is off." : "Placement disabled. Its position, layers and checklist are preserved.");
				SaveSession();
			}
		}

		public void SetMode(BuildMode mode)
		{
			if (PlacementEnabled)
			{
				Mode = mode;
				UpdateAssistance();
			}
		}

		public void ToggleHud()
		{
			Config.ShowHud.Value = !Config.ShowHud.Value;
		}

		private void UpdateAssistance()
		{
			assistance.SetProjection((!Visible && !controls.Held && Mode != BuildMode.Guide) ? ActiveProjection : null, Mode);
		}

		public void DrawProjection()
		{
			if (Object.op_Implicit((Object)(object)Player.m_localPlayer) && !((Character)Player.m_localPlayer).IsDead() && (Object)(object)scene == (Object)(object)ZNetScene.instance)
			{
				ActiveProjection?.Draw(assistance.PreviewPiece, assistance.AimedPiece);
				Selection.Draw(Visible && view.CaptureVisible);
			}
		}

		public void Refresh()
		{
			if (BlueprintLibrary.TryList(Config.Directory.Value, out var files, out var error))
			{
				Files = files;
			}
			else
			{
				Status = error;
			}
		}

		public void OpenBlueprintFolder()
		{
			try
			{
				string fullPath = Path.GetFullPath(Config.Directory.Value);
				Directory.CreateDirectory(fullPath);
				using (Process.Start(new ProcessStartInfo(fullPath)
				{
					UseShellExecute = true,
					Verb = "open"
				}))
				{
				}
				Status = "Folder open requested. Add .blueprint or .vbuild files, then click Refresh.";
			}
			catch (Exception ex) when (ex is IOException || ex is UnauthorizedAccessException || ex is ArgumentException || ex is NotSupportedException || ex is Win32Exception || ex is SecurityException || ex is InvalidOperationException)
			{
				Status = "Cannot open the blueprint folder. Check the game log for its path and open it manually.";
				Logger.LogWarning((object)("Cannot open blueprint folder '" + Config.Directory.Value + "': " + ex.Message + " Check the directory setting, permissions, and default file manager."));
			}
		}

		public void BeginCapture()
		{
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			if (Mode == BuildMode.Automatic)
			{
				Mode = BuildMode.Assisted;
			}
			Selection.Begin();
			SetVisible(value: false);
			Status = $"Select corners A and B, then press {Config.ToggleKey.Value} to review and save.";
		}

		public void Capture(string name)
		{
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			string error;
			if (!Selection.TryBounds(out var bounds) || !Selection.First.HasValue)
			{
				Status = "Select both corners before saving a blueprint.";
			}
			else if (BlueprintLibrary.TryCapture(Config.Directory.Value, name, bounds, Selection.First.Value, out error))
			{
				Refresh();
				Selection.End();
				Status = "Saved " + name + ".blueprint. Corner A defines its origin.";
			}
			else
			{
				Status = error;
			}
		}

		public bool Load(string file)
		{
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			if (!BlueprintLibrary.TryLoad(file, out var document, out var error) || !BlueprintProjection.TryCreate(document, out var projection, out error))
			{
				Status = error;
				return false;
			}
			ResetProjection();
			session.SetDocument(document);
			Projection = projection;
			Projection.Position = ((Component)Player.m_localPlayer).transform.position;
			ProjectionProgress.Refresh(Projection);
			Status = "Blueprint loaded. Close the planner to position it with modifier keys and the wheel.";
			SaveSession();
			return true;
		}

		public IReadOnlyList<MaterialChecklist.Row> MaterialRows(bool selectedLayer)
		{
			List<KeyValuePair<string, int>> list = new List<KeyValuePair<string, int>>();
			if (Projection != null)
			{
				foreach (BlueprintProjection.ProjectedPiece item in Projection.Pieces.Where((BlueprintProjection.ProjectedPiece piece) => !piece.Completed && (!selectedLayer || Projection.Layers.Contains(piece.Entry.posY))))
				{
					Piece component = item.Prefab.GetComponent<Piece>();
					if (!Object.op_Implicit((Object)(object)component))
					{
						continue;
					}
					foreach (Requirement item2 in component.m_resources.Where((Requirement resource) => Object.op_Implicit((Object)(object)resource.m_resItem)))
					{
						list.Add(new KeyValuePair<string, int>(item2.m_resItem.m_itemData.m_shared.m_name, item2.GetAmount(0)));
					}
				}
			}
			return Materials.Rows(list, ChestObservation.Items(((Humanoid)Player.m_localPlayer).GetInventory()));
		}

		public void ForgetChests()
		{
			chests.Dispose();
		}

		public void MoveToFeet()
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			if (Projection != null)
			{
				Projection.Position = ((Component)Player.m_localPlayer).transform.position;
				ProjectionProgress.Refresh(Projection);
			}
		}

		private void RestoreSession()
		{
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			//IL_018f: Unknown result type (might be due to invalid IL or missing references)
			string directory = Path.Combine(Config.DataDirectory, "placements");
			if (!session.Open(directory, ZNet.instance.GetWorldUID(), Player.m_localPlayer.GetPlayerID(), out var save, out var error))
			{
				if (error.Length > 0)
				{
					Status = error;
				}
				return;
			}
			if (!BlueprintDocument.TryParse(save.Name, save.Blueprint, vbuild: false, out var document, out error) || !BlueprintProjection.TryCreate(document, out var projection, out error))
			{
				Status = "Saved hologram could not be restored. " + error;
				return;
			}
			Projection = projection;
			session.SetDocument(document);
			Projection.Position = new Vector3(save.X, save.Y, save.Z);
			Projection.Yaw = save.Yaw;
			Projection.Enabled = !save.Disabled;
			Projection.Layers.SetHeight(save.LayerHeight);
			bool flag = Projection.Layers.Select(save.Layer);
			Mode = ((!save.PreviewOnly) ? BuildMode.Assisted : BuildMode.Guide);
			string[] checkedMaterials = save.CheckedMaterials;
			foreach (string name in checkedMaterials)
			{
				Materials.Check(name, value: true);
			}
			ProjectionProgress.Refresh(Projection);
			Status = (flag ? "Saved hologram restored. Autobuild is off." : "Saved hologram restored with all layers; its saved layer was invalid. Autobuild is off.");
			if (!PlacementEnabled)
			{
				Status += $" Placement is disabled. Press {Config.PlacementKey.Value} or enable it in Build.";
			}
		}

		private void SaveSession()
		{
			if (!session.Save(Projection, Mode, Materials, out var error))
			{
				if (Status != error)
				{
					Logger.LogError((object)error);
				}
				Status = error;
			}
		}

		public void Clear()
		{
			ResetProjection();
			Status = (session.Clear(out var error) ? "Hologram cleared, including its saved placement." : error);
		}

		private void ResetProjection()
		{
			Mode = BuildMode.Assisted;
			assistance.SetProjection(null);
			Projection?.Dispose();
			Projection = null;
			Materials.ClearChecks();
		}

		public void Dispose()
		{
			SaveSession();
			ResetProjection();
			SetVisible(value: false, playSound: false);
			chests.Dispose();
			view.Dispose();
			assistance.Dispose();
			controls.Dispose();
			Selection.Dispose();
		}
	}
	internal sealed class HammerAssistance : IDisposable
	{
		private static HammerAssistance instance;

		private static HammerTarget ghostTarget;

		private readonly Harmony harmony = new Harmony("augusdogus.Buildheim.hammer");

		private readonly AutoBuilder autoBuilder = new AutoBuilder();

		private BuildMode mode;

		private BlueprintProjection projection;

		private BlueprintSelection selection;

		private HammerTarget target => (selection as BlueprintSelection.Hammer)?.Target;

		public BlueprintProjection.ProjectedPiece AimedPiece
		{
			get
			{
				if (!Active(Player.m_localPlayer) || !((Character)Player.m_localPlayer).TakeInput() || Hud.IsPieceSelectionVisible())
				{
					return null;
				}
				return selection?.Planned;
			}
		}

		public BlueprintProjection.ProjectedPiece PreviewPiece
		{
			get
			{
				if (target == null || !target.RecipeKnown || !Object.op_Implicit((Object)(object)target.Player.m_placementGhost) || !((Object)(object)target.Player.GetSelectedPiece() == (Object)(object)target.Piece))
				{
					return null;
				}
				return target.Planned;
			}
		}

		public string Status { get; private set; } = "Equip a hammer and aim at a missing piece.";

		public bool Ready { get; private set; }

		public HammerAssistance()
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Expected O, but got Unknown
			instance = this;
			try
			{
				harmony.PatchAll(typeof(HammerAssistance));
				Ready = true;
			}
			catch (Exception arg)
			{
				harmony.UnpatchSelf();
				Status = "Hammer assistance could not load for this game version. Holograms still work; check the BepInEx log.";
				Logger.LogError((object)$"Cannot patch hammer placement: {arg}");
			}
		}

		public void SetProjection(BlueprintProjection value, BuildMode buildMode = BuildMode.Assisted)
		{
			if (projection != value || mode != buildMode)
			{
				projection = value;
				mode = buildMode;
				selection = null;
				autoBuilder.Reset();
				HideGhost(Player.m_localPlayer);
			}
		}

		private bool Active(Player player)
		{
			if (Ready && projection != null && Object.op_Implicit((Object)(object)player) && (Object)(object)player == (Object)(object)Player.m_localPlayer && !((Character)player).IsDead())
			{
				ItemData rightItem = ((Humanoid)player).GetRightItem();
				object obj;
				if (rightItem == null)
				{
					obj = null;
				}
				else
				{
					GameObject dropPrefab = rightItem.m_dropPrefab;
					obj = ((dropPrefab != null) ? ((Object)dropPrefab).name : null);
				}
				return (string?)obj == "Hammer";
			}
			return false;
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(Player), "UpdatePlacement")]
		private static bool SelectPiece(Player __instance, bool takeInput, out bool __state)
		{
			//IL_01f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_023b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0334: Unknown result type (might be due to invalid IL or missing references)
			//IL_0341: Unknown result type (might be due to invalid IL or missing references)
			__state = false;
			if (instance == null || (Object)(object)__instance != (Object)(object)Player.m_localPlayer)
			{
				return true;
			}
			if (ProjectionControls.Adjusting)
			{
				instance.selection = null;
				__instance.m_placePressedTime = -9999f;
				__instance.m_removePressedTime = -9999f;
				if (Object.op_Implicit((Object)(object)__instance.m_placementGhost))
				{
					__instance.m_placementGhost.SetActive(false);
				}
				if (Object.op_Implicit((Object)(object)__instance.m_placementMarkerInstance))
				{
					__instance.m_placementMarkerInstance.SetActive(false);
				}
				return false;
			}
			if (!instance.Active(__instance) || !takeInput || Hud.IsPieceSelectionVisible())
			{
				instance.selection = null;
				return true;
			}
			if (instance.mode == BuildMode.Automatic && (ZInput.GetButton("Remove") || ZInput.GetButton("JoyRemove") || ZInput.GetButton("JoyAltKeys")))
			{
				instance.selection = null;
				return true;
			}
			if (instance.mode == BuildMode.Automatic)
			{
				HammerTarget hammerTarget = instance.autoBuilder.Find(instance.projection, __instance);
				instance.selection = ((hammerTarget == null) ? null : new BlueprintSelection.Hammer(hammerTarget));
			}
			else
			{
				instance.selection = HammerTarget.Find(instance.projection, __instance);
			}
			if (instance.selection is BlueprintSelection.Guide guide)
			{
				instance.Status = guide.Reason;
				SelectRepair(__instance);
				return true;
			}
			HammerTarget hammerTarget2 = instance.target;
			if (hammerTarget2 == null)
			{
				instance.Status = ((instance.mode == BuildMode.Automatic) ? "Waiting for a nearby buildable piece, materials or stamina." : "Aim at a missing piece within hammer reach.");
				SelectRepair(__instance);
				return true;
			}
			string text = hammerTarget2.RecipeError();
			if (text != null)
			{
				instance.Status = text;
				SelectRepair(__instance);
				return true;
			}
			if (!hammerTarget2.HasInventoryResources())
			{
				instance.Status = Localization.instance.Localize(hammerTarget2.Piece.m_name) + ": missing materials in your inventory.";
				SelectRepair(__instance);
				return true;
			}
			if (ZoneSystem.instance.GetGlobalKey(hammerTarget2.Piece.FreeBuildKey()) || !__instance.HaveRequirements(hammerTarget2.Piece, (RequirementMode)0))
			{
				instance.Status = Localization.instance.Localize(hammerTarget2.Piece.m_name) + ": " + (ZoneSystem.instance.GetGlobalKey(hammerTarget2.Piece.FreeBuildKey()) ? "disable the world's free-build setting to use hammer assistance." : "check the required crafting station and recipe requirements.");
				SelectRepair(__instance);
				return true;
			}
			if ((Object)(object)__instance.GetSelectedPiece() != (Object)(object)hammerTarget2.Piece && !__instance.SetSelectedPiece(hammerTarget2.Piece))
			{
				instance.Status = Localization.instance.Localize(hammerTarget2.Piece.m_name) + ": the hammer could not select this recipe. Reopen its build menu and try again.";
				SelectRepair(__instance);
				return true;
			}
			instance.Status = "Click to build " + Localization.instance.Localize(hammerTarget2.Piece.m_name);
			if (instance.mode != BuildMode.Automatic)
			{
				return true;
			}
			instance.Status = "Next: " + Localization.instance.Localize(hammerTarget2.Piece.m_name);
			if (!instance.autoBuilder.TryAttempt(__instance))
			{
				return true;
			}
			__instance.UpdatePlacementGhost(false);
			if ((int)__instance.m_placementStatus != 0)
			{
				instance.Status = PlacementFeedback.Describe(__instance.m_placementStatus);
				return true;
			}
			__instance.m_placePressedTime = Time.time;
			__state = true;
			instance.Status = "Autobuilding " + Localization.instance.Localize(hammerTarget2.Piece.m_name);
			return true;
		}

		private static void SelectRepair(Player player)
		{
			Piece selectedPiece = player.GetSelectedPiece();
			if ((selectedPiece == null || !selectedPiece.m_repairPiece) && Object.op_Implicit((Object)(object)player.m_buildPieces))
			{
				Piece val = (from prefab in player.m_buildPieces.m_pieces
					where Object.op_Implicit((Object)(object)prefab)
					select prefab.GetComponent<Piece>()).FirstOrDefault((Func<Piece, bool>)((Piece piece) => Object.op_Implicit((Object)(object)piece) && piece.m_repairPiece));
				if (Object.op_Implicit((Object)(object)val) && !player.SetSelectedPiece(val))
				{
					Logger.LogWarning((object)"The hammer could not select Repair after clearing a blueprint recipe.");
				}
			}
			HideGhost(player);
		}

		private static void HideGhost(Player player)
		{
			if (Object.op_Implicit((Object)(object)player))
			{
				player.m_placePressedTime = -9999f;
				if (Object.op_Implicit((Object)(object)player.m_placementGhost))
				{
					player.m_placementGhost.SetActive(false);
				}
				if (Object.op_Implicit((Object)(object)player.m_placementMarkerInstance))
				{
					player.m_placementMarkerInstance.SetActive(false);
				}
			}
		}

		[HarmonyTranspiler]
		[HarmonyPatch(typeof(Player), "UpdatePlacement")]
		private static IEnumerable<CodeInstruction> GuardSelectedRecipe(IEnumerable<CodeInstruction> instructions)
		{
			MethodInfo getPiece = AccessTools.Method(typeof(PieceTable), "GetSelectedPiece", (Type[])null, (Type[])null);
			List<CodeInstruction> list = instructions.ToList();
			if (list.Count((CodeInstruction val) => CodeInstructionExtensions.Calls(val, getPiece)) != 1)
			{
				throw new InvalidOperationException("UpdatePlacement no longer reads one build recipe; assistance was disabled.");
			}
			foreach (CodeInstruction instruction in list)
			{
				yield return instruction;
				if (CodeInstructionExtensions.Calls(instruction, getPiece))
				{
					yield return new CodeInstruction(OpCodes.Ldarg_0, (object)null);
					yield return new CodeInstruction(OpCodes.Call, (object)AccessTools.Method(typeof(HammerAssistance), "BuildRecipe", (Type[])null, (Type[])null));
				}
			}
		}

		private static Piece BuildRecipe(Piece recipe, Player player)
		{
			if (instance == null || !instance.Active(player))
			{
				return recipe;
			}
			if (Object.op_Implicit((Object)(object)recipe) && recipe.m_repairPiece)
			{
				if (instance.selection != null)
				{
					return null;
				}
				return recipe;
			}
			HammerTarget hammerTarget = instance.target;
			if (hammerTarget == null || !hammerTarget.RecipeKnown || !((Object)(object)hammerTarget.Piece == (Object)(object)recipe) || hammerTarget.Planned.Completed || !instance.projection.Layers.Contains(hammerTarget.Planned.Entry.posY))
			{
				return null;
			}
			return recipe;
		}

		[HarmonyFinalizer]
		[HarmonyPatch(typeof(Player), "UpdatePlacement")]
		private static void ClearAutoClick(Player __instance, bool __state)
		{
			if (__state)
			{
				__instance.m_placePressedTime = -9999f;
			}
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(Player), "TryPlacePiece")]
		private static bool CheckBuild(Player __instance, Piece piece, ref bool __result)
		{
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			if (instance == null || !instance.Active(__instance))
			{
				return true;
			}
			HammerTarget hammerTarget = instance.target;
			ProjectionProgress.Refresh(instance.projection);
			string text = null;
			if (hammerTarget == null || (Object)(object)hammerTarget.Piece != (Object)(object)piece || hammerTarget.Planned.Completed || !instance.projection.Layers.Contains(hammerTarget.Planned.Entry.posY))
			{
				text = "Aim at a missing blueprint piece to build it.";
			}
			else
			{
				string text2 = hammerTarget.RecipeError();
				if (text2 != null)
				{
					text = text2;
				}
				else if (ZoneSystem.instance.GetGlobalKey(piece.FreeBuildKey()))
				{
					text = "Hammer assistance requires resource costs. Disable the world's free-build setting first.";
				}
				else
				{
					string text3 = hammerTarget.MissingMaterialsError();
					if (text3 != null)
					{
						text = text3;
					}
					else if (!__instance.HaveRequirements(piece, (RequirementMode)0))
					{
						text = "Check the required crafting station and recipe requirements.";
					}
				}
			}
			if (text == null)
			{
				return true;
			}
			__result = false;
			((Character)__instance).Message((MessageType)2, text, 0, (Sprite)null, false);
			return false;
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(Player), "TryPlacePiece")]
		private static void Built(Player __instance, bool __result)
		{
			if (__result && instance != null && instance.Active(__instance) && instance.target != null)
			{
				instance.target.Planned.Completed = true;
			}
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(Player), "UpdatePlacementGhost")]
		private static void BeginGhost(Player __instance, out HammerTarget __state)
		{
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			__state = ghostTarget;
			ghostTarget = null;
			if (instance != null && instance.Active(__instance))
			{
				HammerTarget hammerTarget = instance.target;
				if (hammerTarget != null && hammerTarget.RecipeKnown && !hammerTarget.Planned.Completed && instance.projection.Layers.Contains(hammerTarget.Planned.Entry.posY) && !((Object)(object)__instance.GetSelectedPiece() != (Object)(object)hammerTarget.Piece) && Object.op_Implicit((Object)(object)__instance.m_placementGhost))
				{
					ghostTarget = hammerTarget;
					__instance.m_placementGhost.transform.SetPositionAndRotation(hammerTarget.Position, hammerTarget.Rotation);
				}
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(Player), "UpdatePlacementGhost")]
		private static void EndGhost(Player __instance)
		{
			//IL_0059: 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_0088: 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_00bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			if (instance == null || !instance.Active(__instance))
			{
				return;
			}
			if (ghostTarget == null)
			{
				__instance.m_placementStatus = (PlacementStatus)1;
				if (Object.op_Implicit((Object)(object)__instance.m_placementGhost))
				{
					__instance.m_placementGhost.SetActive(false);
				}
				return;
			}
			__instance.m_placementGhost.SetActive(true);
			__instance.m_placementGhost.transform.SetPositionAndRotation(ghostTarget.Position, ghostTarget.Rotation);
			string text = Localization.instance.Localize(ghostTarget.Piece.m_name);
			if ((int)__instance.m_placementStatus != 0)
			{
				instance.Status = text + ": " + PlacementFeedback.Describe(__instance.m_placementStatus);
			}
			else if (ZoneSystem.instance.GetGlobalKey(ghostTarget.Piece.FreeBuildKey()))
			{
				instance.Status = text + ": disable the world's free-build setting to use hammer assistance.";
			}
			else if (!ghostTarget.HasInventoryResources())
			{
				instance.Status = text + ": missing materials in your inventory.";
			}
			else if (!__instance.HaveRequirements(ghostTarget.Piece, (RequirementMode)0))
			{
				instance.Status = text + ": check the required crafting station and recipe requirements.";
			}
		}

		[HarmonyFinalizer]
		[HarmonyPatch(typeof(Player), "UpdatePlacementGhost")]
		private static void ClearGhost(HammerTarget __state)
		{
			ghostTarget = __state;
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(Player), "PieceRayTest")]
		private static bool BlueprintSurface(Player __instance, bool water, ref Vector3 point, ref Vector3 normal, ref Piece piece, ref Heightmap heightmap, ref Collider waterSurface, ref bool __result)
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//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_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//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 (ghostTarget == null || (Object)(object)ghostTarget.Player != (Object)(object)__instance)
			{
				return true;
			}
			point = Vector3.zero;
			normal = Vector3.zero;
			piece = null;
			heightmap = null;
			waterSurface = null;
			__result = ghostTarget.TrySurface(water, out var hit);
			if (__result)
			{
				point = ((RaycastHit)(ref hit)).point;
				normal = ((RaycastHit)(ref hit)).normal;
				piece = ((Component)((RaycastHit)(ref hit)).collider).GetComponentInParent<Piece>();
				heightmap = ((Component)((RaycastHit)(ref hit)).collider).GetComponent<Heightmap>();
				if (((Component)((RaycastHit)(ref hit)).collider).gameObject.layer == LayerMask.NameToLayer("Water"))
				{
					waterSurface = ((RaycastHit)(ref hit)).collider;
				}
			}
			return false;
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(Player), "PieceRayTest")]
		private static void CheckSurface(Player __instance, ref Vector3 point, ref bool __result)
		{
			//IL_0024: 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_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			if (ghostTarget != null && !((Object)(object)ghostTarget.Player != (Object)(object)__instance) && __result)
			{
				if (!ghostTarget.NearSurface(point) || Vector3.Distance(((Character)__instance).m_eye.position, point) >= __instance.m_maxPlaceDistance + (float)ghostTarget.Piece.m_extraPlacementDistance)
				{
					__result = false;
					instance.Status = "No reachable surface touches this piece. Move closer or build its foundation first.";
				}
				else
				{
					point = ghostTarget.Position;
				}
			}
		}

		[HarmonyTranspiler]
		[HarmonyPatch(typeof(Player), "UpdatePlacementGhost")]
		private static IEnumerable<CodeInstruction> AlignGhost(IEnumerable<CodeInstruction> instructions)
		{
			List<CodeInstruction> list = instructions.ToList();
			MethodInfo methodInfo = AccessTools.PropertySetter(typeof(Transform), "position");
			MethodInfo methodInfo2 = AccessTools.PropertySetter(typeof(Transform), "rotation");
			int num = 0;
			int num2 = 0;
			foreach (CodeInstruction item in list)
			{
				if (item.operand is MethodInfo methodInfo3 && methodInfo3.DeclaringType == typeof(Transform) && (methodInfo3.Name == "SetPositionAndRotation" || methodInfo3.Name == "SetLocalPositionAndRotation" || methodInfo3.Name == "set_localPosition" || methodInfo3.Name == "set_localRotation"))
				{
					throw new InvalidOperationException("UpdatePlacementGhost uses unsupported transform writes; assistance was disabled.");
				}
				if (CodeInstructionExtensions.Calls(item, methodInfo))
				{
					item.opcode = OpCodes.Call;
					item.operand = AccessTools.Method(typeof(HammerAssistance), "SetPosition", (Type[])null, (Type[])null);
					num++;
				}
				else if (CodeInstructionExtensions.Calls(item, methodInfo2))
				{
					item.opcode = OpCodes.Call;
					item.operand = AccessTools.Method(typeof(HammerAssistance), "SetRotation", (Type[])null, (Type[])null);
					num2++;
				}
			}
			if (num == 0 || num2 == 0)
			{
				throw new InvalidOperationException("UpdatePlacementGhost no longer sets position and rotation as expected; assistance was disabled.");
			}
			return list;
		}

		private static void SetPosition(Transform transform, Vector3 position)
		{
			//IL_0024: 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)
			transform.position = ((ghostTarget != null && (Object)(object)transform == (Object)(object)ghostTarget.Player.m_placementGhost.transform) ? ghostTarget.Position : position);
		}

		private static void SetRotation(Transform transform, Quaternion rotation)
		{
			//IL_0024: 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)
			transform.rotation = ((ghostTarget != null && (Object)(object)transform == (Object)(object)ghostTarget.Player.m_placementGhost.transform) ? ghostTarget.Rotation : rotation);
		}

		public void Dispose()
		{
			harmony.UnpatchSelf();
			projection = null;
			selection = null;
			ghostTarget = null;
			instance = null;
		}
	}
	internal sealed class HammerTarget
	{
		private enum Rejection
		{
			None,
			Completed,
			OtherLayer,
			NotAHammerPiece,
			Scaled,
			OutOfReach
		}

		public BlueprintProjection Projection { get; }

		public BlueprintProjection.ProjectedPiece Planned { get; }

		public Player Player { get; }

		public Piece Piece { get; }

		public Vector3 Position => Projection.PiecePosition(Planned);

		public Quaternion Rotation => Projection.PieceRotation(Planned);

		public bool RecipeKnown => Player.m_knownRecipes.Contains(Piece.m_name);

		private IEnumerable<KeyValuePair<string, int>> ResourceCosts => from resource in Piece.m_resources
			where Object.op_Implicit((Object)(object)resource.m_resItem)
			select new KeyValuePair<string, int>(resource.m_resItem.m_itemData.m_shared.m_name, resource.GetAmount(0));

		public string RecipeError()
		{
			string text = Localization.instance.Localize(Piece.m_name);
			if (!RecipeKnown)
			{
				string[] array = (from resource in (from resource in Piece.m_resources
						where Object.op_Implicit((Object)(object)resource.m_resItem) && resource.GetAmount(0) > 0
						select resource.m_resItem.m_itemData.m_shared.m_name into resource
						where !Player.m_knownMaterial.Contains(resource)
						select resource).Distinct()
					select Localization.instance.Localize(resource)).ToArray();
				if (array.Length == 0)
				{
					return text + ": recipe locked. Discover its required materials and crafting station first.";
				}
				return text + ": recipe locked. Discover " + string.Join(", ", array) + " to unlock it.";
			}
			if (!Player.IsPieceAvailable(Piece))
			{
				return text + ": recipe learned, but unavailable in this hammer's build menu.";
			}
			return null;
		}

		private HammerTarget(BlueprintProjection projection, BlueprintProjection.ProjectedPiece planned, Player player, Piece piece)
		{
			Projection = projection;
			Planned = planned;
			Player = player;
			Piece = piece;
		}

		public static HammerTarget FromPiece(BlueprintProjection projection, BlueprintProjection.ProjectedPiece planned, Player player)
		{
			Rejection rejection;
			return FromPiece(projection, planned, player, out rejection);
		}

		private static HammerTarget FromPiece(BlueprintProjection projection, BlueprintProjection.ProjectedPiece planned, Player player, out Rejection rejection)
		{
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: 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_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: 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_00a8: 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_00b2: 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_00c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
			rejection = Rejection.Completed;
			if (planned.Completed)
			{
				return null;
			}
			rejection = Rejection.OtherLayer;
			if (!projection.Layers.Contains(planned.Entry.posY))
			{
				return null;
			}
			Piece component = planned.Prefab.GetComponent<Piece>();
			rejection = Rejection.NotAHammerPiece;
			if (!Object.op_Implicit((Object)(object)component) || component.m_repairPiece || component.m_removePiece)
			{
				return null;
			}
			rejection = Rejection.Scaled;
			if (Vector3.Distance(planned.Entry.GetScale(), planned.Prefab.transform.localScale) > 0.01f)
			{
				return null;
			}
			Matrix4x4 val = projection.PieceMatrix(planned);
			Bounds localBounds = planned.LocalBounds;
			Matrix4x4 inverse = ((Matrix4x4)(ref val)).inverse;
			Vector3 val2 = ((Matrix4x4)(ref val)).MultiplyPoint3x4(((Bounds)(ref localBounds)).ClosestPoint(((Matrix4x4)(ref inverse)).MultiplyPoint3x4(((Character)player).m_eye.position)));
			rejection = Rejection.OutOfReach;
			if (Vector3.Distance(((Character)player).m_eye.position, val2) >= player.m_maxPlaceDistance + (float)component.m_extraPlacementDistance)
			{
				return null;
			}
			rejection = Rejection.None;
			retur