Decompiled source of WispShovel v1.0.1

WispShovel.dll

Decompiled 3 days ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Security.Cryptography;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using Jotunn.Configs;
using Jotunn.Entities;
using Jotunn.Managers;
using Jotunn.Utils;
using Microsoft.CodeAnalysis;
using Newtonsoft.Json;
using TMPro;
using UnityEngine;
using WispShovel.Core;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyVersion("0.0.0.0")]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
}
namespace WispShovel.Valheim
{
	public sealed class AreaPreview : MonoBehaviour
	{
		private static readonly FieldInfo PlaceMask = AccessTools.Field(typeof(Player), "m_placeRayMask");

		private readonly Vector3[] points = (Vector3[])(object)new Vector3[64];

		private readonly float[] offsetsX = new float[64];

		private readonly float[] offsetsZ = new float[64];

		private readonly float[] relativeHeights = new float[64];

		private ShovelControls? controls;

		private Func<Vector3, Area, bool>? canApply;

		private LineRenderer? line;

		private Material? material;

		private float nextUpdate;

		private bool sampledValid;

		private bool sampled;

		public void Initialize(ShovelControls input, Func<Vector3, Area, bool> actionCheck)
		{
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Invalid comparison between Unknown and I4
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Expected O, but got Unknown
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Expected O, but got Unknown
			if ((Object)(object)controls != (Object)null)
			{
				throw new InvalidOperationException("Area preview is already initialized.");
			}
			if (!Object.op_Implicit((Object)(object)input))
			{
				throw new ArgumentNullException("input");
			}
			controls = input;
			canApply = actionCheck ?? throw new ArgumentNullException("actionCheck");
			if ((int)SystemInfo.graphicsDeviceType != 4)
			{
				Shader val = Shader.Find("Sprites/Default");
				if (!Object.op_Implicit((Object)(object)val))
				{
					throw new InvalidOperationException("No suitable preview shader is available.");
				}
				material = new Material(val);
				GameObject val2 = new GameObject("WispShovelAreaPreview");
				val2.transform.SetParent(((Component)this).transform, false);
				line = val2.AddComponent<LineRenderer>();
				((Renderer)line).sharedMaterial = material;
				line.useWorldSpace = true;
				line.loop = true;
				line.positionCount = points.Length;
				LineRenderer? obj = line;
				float startWidth = (line.endWidth = 0.035f);
				obj.startWidth = startWidth;
				((Renderer)line).enabled = false;
			}
		}

		private void Update()
		{
			//IL_00bc: 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_010b: Unknown result type (might be due to invalid IL or missing references)
			//IL_028f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0290: Unknown result type (might be due to invalid IL or missing references)
			//IL_029a: Unknown result type (might be due to invalid IL or missing references)
			//IL_029f: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_02bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_02db: Unknown result type (might be due to invalid IL or missing references)
			//IL_031e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0317: Unknown result type (might be due to invalid IL or missing references)
			//IL_0323: Unknown result type (might be due to invalid IL or missing references)
			//IL_0324: Unknown result type (might be due to invalid IL or missing references)
			//IL_032b: Unknown result type (might be due to invalid IL or missing references)
			//IL_021b: Unknown result type (might be due to invalid IL or missing references)
			//IL_021c: Unknown result type (might be due to invalid IL or missing references)
			//IL_021f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0224: Unknown result type (might be due to invalid IL or missing references)
			//IL_0229: Unknown result type (might be due to invalid IL or missing references)
			//IL_022c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0231: Unknown result type (might be due to invalid IL or missing references)
			//IL_0249: Unknown result type (might be due to invalid IL or missing references)
			if (line == null || !Object.op_Implicit((Object)(object)line) || (Object)(object)controls == (Object)null || !controls.IsControllable || !controls.PreviewEnabled || !Object.op_Implicit((Object)(object)GameCamera.instance) || !Object.op_Implicit((Object)(object)Player.m_localPlayer))
			{
				if (line != null && Object.op_Implicit((Object)(object)line))
				{
					((Renderer)line).enabled = false;
				}
				sampled = false;
				return;
			}
			if (!TryGetTarget(out var center, out var area))
			{
				((Renderer)line).enabled = false;
				sampled = false;
				return;
			}
			Vector3 val = default(Vector3);
			((Vector3)(ref val))..ctor((float)area.ForwardX, 0f, (float)area.ForwardZ);
			Vector3 val2 = default(Vector3);
			((Vector3)(ref val2))..ctor(val.z, 0f, 0f - val.x);
			if (!sampled || Time.unscaledTime >= nextUpdate)
			{
				nextUpdate = Time.unscaledTime + 0.1f;
				sampledValid = canApply != null && canApply(center, area);
				float num8 = default(float);
				for (int i = 0; i < points.Length; i++)
				{
					float num2;
					float num3;
					if (area.Shape == AreaShape.Circle)
					{
						float num = (float)i * (float)Math.PI * 2f / (float)points.Length;
						num2 = Mathf.Cos(num) * (float)area.Radius;
						num3 = Mathf.Sin(num) * (float)area.Radius;
					}
					else
					{
						int num4 = i / 16;
						float num5 = (float)(i % 16) / 16f;
						float num6 = (float)area.Width / 2f;
						float num7 = (float)area.Length / 2f;
						num2 = num4 switch
						{
							2 => Mathf.Lerp(num6, 0f - num6, num5), 
							1 => num6, 
							0 => Mathf.Lerp(0f - num6, num6, num5), 
							_ => 0f - num6, 
						};
						num3 = num4 switch
						{
							2 => num7, 
							1 => Mathf.Lerp(0f - num7, num7, num5), 
							0 => 0f - num7, 
							_ => Mathf.Lerp(num7, 0f - num7, num5), 
						};
					}
					offsetsX[i] = num2;
					offsetsZ[i] = num3;
					if (Heightmap.GetHeight(center + val2 * num2 + val * num3, ref num8))
					{
						relativeHeights[i] = num8 - center.y + 0.05f;
						continue;
					}
					sampledValid = false;
					relativeHeights[i] = 0.05f;
				}
				sampled = true;
			}
			for (int j = 0; j < points.Length; j++)
			{
				Vector3 val3 = center + val2 * offsetsX[j] + val * offsetsZ[j];
				val3.y = center.y + relativeHeights[j];
				points[j] = val3;
			}
			line.SetPositions(points);
			LineRenderer? obj = line;
			Color startColor = (line.endColor = (sampledValid ? Color.green : Color.red));
			obj.startColor = startColor;
			((Renderer)line).enabled = true;
		}

		public bool TryGetTarget(out Vector3 center, out Area area)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: 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_00bc: 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_00fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_0103: Unknown result type (might be due to invalid IL or missing references)
			//IL_0114: Unknown result type (might be due to invalid IL or missing references)
			//IL_011c: Unknown result type (might be due to invalid IL or missing references)
			center = default(Vector3);
			area = default(Area);
			if ((Object)(object)controls == (Object)null || !controls.IsControllable || !Object.op_Implicit((Object)(object)GameCamera.instance) || !Object.op_Implicit((Object)(object)Player.m_localPlayer))
			{
				return false;
			}
			Transform transform = ((Component)GameCamera.instance).transform;
			Player localPlayer = Player.m_localPlayer;
			int num = (int)PlaceMask.GetValue(localPlayer);
			RaycastHit val = default(RaycastHit);
			if (!Physics.Raycast(transform.position, transform.forward, ref val, 50f, num) || !Object.op_Implicit((Object)(object)((RaycastHit)(ref val)).collider) || Object.op_Implicit((Object)(object)((RaycastHit)(ref val)).collider.attachedRigidbody) || !Object.op_Implicit((Object)(object)((Component)((RaycastHit)(ref val)).collider).GetComponent<Heightmap>()) || Vector3.Distance(((Character)localPlayer).GetEyePoint(), ((RaycastHit)(ref val)).point) >= localPlayer.m_maxPlaceDistance)
			{
				return false;
			}
			Vector3 lookDir = ((Character)localPlayer).GetLookDir();
			lookDir.y = 0f;
			if (((Vector3)(ref lookDir)).sqrMagnitude < 0.0001f)
			{
				return false;
			}
			((Vector3)(ref lookDir)).Normalize();
			center = ((RaycastHit)(ref val)).point;
			area = controls.State.AreaFacing(lookDir.x, lookDir.z);
			return true;
		}

		private void OnDestroy()
		{
			if (line != null && Object.op_Implicit((Object)(object)line))
			{
				Object.Destroy((Object)(object)((Component)line).gameObject);
			}
			if (Object.op_Implicit((Object)(object)material))
			{
				Object.Destroy((Object)(object)material);
			}
		}
	}
	internal static class ConfigLayout
	{
		internal static void Initialize(ConfigFile file)
		{
			file.SettingChanged += delegate
			{
				Save(file);
			};
			Save(file);
		}

		private static void Save(ConfigFile file)
		{
			try
			{
				file.Save();
				Dictionary<string, List<string>> dictionary = new Dictionary<string, List<string>>();
				string text = "";
				string[] array = File.ReadAllLines(file.ConfigFilePath);
				foreach (string text2 in array)
				{
					string text3 = text2.Trim();
					if (text3.StartsWith("[") && text3.EndsWith("]"))
					{
						text = text3.Substring(1, text3.Length - 2);
						if (!dictionary.ContainsKey(text))
						{
							dictionary.Add(text, new List<string>());
						}
						continue;
					}
					switch (text)
					{
					case "":
					case "Recipe":
					case "Durability":
					case "Dig":
						continue;
					}
					if (!text3.StartsWith("# Setting type:") && !text3.StartsWith("# Acceptable values:") && (!(text == "General") || !(text3 == "# Default value: false")) && !text3.StartsWith("DurableActionSaves =") && !text3.StartsWith("ActionCooldown =") && !text3.StartsWith("Height =") && !text3.StartsWith("MinHeight =") && !text3.StartsWith("MaxHeight =") && !text3.Contains("Save the character and world for every action"))
					{
						dictionary[text].Add(text2);
					}
				}
				StringBuilder stringBuilder = new StringBuilder();
				array = new string[5] { "General", "Area", "Controls", "Raise", "Visual" };
				foreach (string text4 in array)
				{
					if (!dictionary.TryGetValue(text4, out var value))
					{
						continue;
					}
					stringBuilder.Append('[').Append(text4).AppendLine("]");
					foreach (string item in value)
					{
						stringBuilder.AppendLine(item);
					}
					stringBuilder.AppendLine();
				}
				File.WriteAllText(file.ConfigFilePath, stringBuilder.ToString(), new UTF8Encoding(encoderShouldEmitUTF8Identifier: false));
			}
			catch (Exception ex)
			{
				Debug.LogError((object)("WispShovel: could not save configuration: " + ex));
			}
		}
	}
	internal readonly struct TerrainRevision
	{
		internal readonly ZDOID Id;

		internal readonly uint Revision;

		internal TerrainRevision(ZDOID id, uint revision)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			Id = id;
			Revision = revision;
		}
	}
	internal sealed class TerrainActionFailedException : Exception
	{
		public TerrainActionFailedException(Exception inner)
			: base("Terrain action was rolled back before saving.", inner)
		{
		}
	}
	internal sealed class TerrainOutcomeIndeterminateException : Exception
	{
		public TerrainOutcomeIndeterminateException(Exception inner)
			: base("Terrain action outcome is indeterminate.", inner)
		{
		}
	}
	internal static class LocalTerrainMutation
	{
		private sealed class Chunk
		{
			public Heightmap Map;

			public TerrainComp Compiler;

			public float[] Level;

			public float[] Smooth;

			public bool[] ModifiedHeight;

			public Color[] Paint;

			public bool[] ModifiedPaint;

			public float[]? LevelBefore;

			public float[]? SmoothBefore;

			public bool[]? HeightBefore;

			public Color[]? PaintBefore;

			public bool[]? PaintModifiedBefore;

			public readonly List<Sample> Samples = new List<Sample>();
		}

		private struct Sample
		{
			public int Index;

			public int X;

			public int Y;

			public float Level;

			public float Smooth;

			public float Height;

			public bool Modified;

			public Color Paint;

			public Color VisiblePaint;
		}

		private static readonly FieldInfo Initialized = RequiredField("m_initialized");

		private static readonly FieldInfo HeightmapField = RequiredField("m_hmap");

		private static readonly FieldInfo Width = RequiredField("m_width");

		private static readonly FieldInfo Level = RequiredField("m_levelDelta");

		private static readonly FieldInfo Smooth = RequiredField("m_smoothDelta");

		private static readonly FieldInfo ModifiedHeight = RequiredField("m_modifiedHeight");

		private static readonly FieldInfo Paint = RequiredField("m_paintMask");

		private static readonly FieldInfo ModifiedPaint = RequiredField("m_modifiedPaint");

		private static readonly FieldInfo Operations = RequiredField("m_operations");

		private static readonly FieldInfo LastPoint = RequiredField("m_lastOpPoint");

		private static readonly FieldInfo LastRadius = RequiredField("m_lastOpRadius");

		private static readonly MethodInfo Save = AccessTools.Method(typeof(TerrainComp), "Save", new Type[1] { typeof(bool) }, (Type[])null) ?? throw new MissingMethodException("TerrainComp.Save(bool)");

		private static readonly MethodInfo RaiseTerrain = AccessTools.Method(typeof(TerrainComp), "RaiseTerrain", new Type[5]
		{
			typeof(Vector3),
			typeof(float),
			typeof(float),
			typeof(bool),
			typeof(float)
		}, (Type[])null) ?? throw new MissingMethodException("TerrainComp.RaiseTerrain");

		private static readonly MethodInfo LevelTerrain = AccessTools.Method(typeof(TerrainComp), "LevelTerrain", new Type[3]
		{
			typeof(Vector3),
			typeof(float),
			typeof(bool)
		}, (Type[])null) ?? throw new MissingMethodException("TerrainComp.LevelTerrain");

		private static readonly MethodInfo PaintCleared = AccessTools.Method(typeof(TerrainComp), "PaintCleared", new Type[3]
		{
			typeof(Vector3),
			typeof(Vector3),
			typeof(Settings)
		}, (Type[])null) ?? throw new MissingMethodException("TerrainComp.PaintCleared");

		private const float Epsilon = 0.0001f;

		private static FieldInfo RequiredField(string name)
		{
			return AccessTools.Field(typeof(TerrainComp), name) ?? throw new MissingFieldException("TerrainComp", name);
		}

		internal static bool Apply(Vector3 center, Area area, ToolMode mode, float magnitude)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				return ApplyCore(center, area, mode, magnitude, serverAuthorized: false);
			}
			catch (TerrainOutcomeIndeterminateException)
			{
				throw;
			}
			catch (Exception inner)
			{
				throw new TerrainActionFailedException(inner);
			}
		}

		internal static bool ApplyServerAuthorized(Vector3 center, Area area, ToolMode mode, float magnitude)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				return ApplyCore(center, area, mode, magnitude, serverAuthorized: true);
			}
			catch (TerrainOutcomeIndeterminateException)
			{
				throw;
			}
			catch (Exception inner)
			{
				throw new TerrainActionFailedException(inner);
			}
		}

		internal static bool ApplyOwnerAuthorized(Vector3 center, Area area, ToolMode mode, float magnitude, List<TerrainRevision> revisions)
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			if (revisions == null)
			{
				throw new ArgumentNullException("revisions");
			}
			try
			{
				return ApplyCore(center, area, mode, magnitude, serverAuthorized: false, ownerAuthorized: true, revisions);
			}
			catch (TerrainOutcomeIndeterminateException)
			{
				throw;
			}
			catch (Exception inner)
			{
				throw new TerrainActionFailedException(inner);
			}
		}

		private static bool ApplyCore(Vector3 center, Area area, ToolMode mode, float magnitude, bool serverAuthorized, bool ownerAuthorized = false, List<TerrainRevision>? revisions = null)
		{
			//IL_00ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_0110: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0550: Unknown result type (might be due to invalid IL or missing references)
			//IL_0829: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0878: Unknown result type (might be due to invalid IL or missing references)
			//IL_0749: Unknown result type (might be due to invalid IL or missing references)
			//IL_074e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0a45: Unknown result type (might be due to invalid IL or missing references)
			//IL_0a4a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0a4c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0a55: Unknown result type (might be due to invalid IL or missing references)
			//IL_08eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0929: Unknown result type (might be due to invalid IL or missing references)
			//IL_092e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0933: Unknown result type (might be due to invalid IL or missing references)
			//IL_0937: Unknown result type (might be due to invalid IL or missing references)
			//IL_093e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0946: Unknown result type (might be due to invalid IL or missing references)
			//IL_094d: Unknown result type (might be due to invalid IL or missing references)
			//IL_075b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0754: Unknown result type (might be due to invalid IL or missing references)
			//IL_0637: Unknown result type (might be due to invalid IL or missing references)
			//IL_063c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0643: Unknown result type (might be due to invalid IL or missing references)
			//IL_064a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0651: Unknown result type (might be due to invalid IL or missing references)
			//IL_0658: Unknown result type (might be due to invalid IL or missing references)
			//IL_0a6c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0a75: Unknown result type (might be due to invalid IL or missing references)
			//IL_034f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0760: Unknown result type (might be due to invalid IL or missing references)
			//IL_0770: Unknown result type (might be due to invalid IL or missing references)
			//IL_0777: Unknown result type (might be due to invalid IL or missing references)
			//IL_077e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0785: Unknown result type (might be due to invalid IL or missing references)
			//IL_078c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0791: Unknown result type (might be due to invalid IL or missing references)
			//IL_0a8c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0a95: Unknown result type (might be due to invalid IL or missing references)
			//IL_0354: Unknown result type (might be due to invalid IL or missing references)
			//IL_0358: Unknown result type (might be due to invalid IL or missing references)
			//IL_035f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0367: Unknown result type (might be due to invalid IL or missing references)
			//IL_036e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0309: Unknown result type (might be due to invalid IL or missing references)
			//IL_033d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0342: Unknown result type (might be due to invalid IL or missing references)
			//IL_0661: Unknown result type (might be due to invalid IL or missing references)
			//IL_0666: Unknown result type (might be due to invalid IL or missing references)
			//IL_067a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0685: Unknown result type (might be due to invalid IL or missing references)
			//IL_0690: Unknown result type (might be due to invalid IL or missing references)
			//IL_0699: Expected O, but got Unknown
			//IL_06ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_06b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_03c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_03cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_03e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_03ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_03f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_03f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_0d50: Unknown result type (might be due to invalid IL or missing references)
			//IL_0403: Unknown result type (might be due to invalid IL or missing references)
			//IL_03fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_06ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_06ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_0408: Unknown result type (might be due to invalid IL or missing references)
			//IL_040a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0411: Unknown result type (might be due to invalid IL or missing references)
			//IL_0425: Unknown result type (might be due to invalid IL or missing references)
			//IL_042c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0440: Unknown result type (might be due to invalid IL or missing references)
			//IL_0447: Unknown result type (might be due to invalid IL or missing references)
			//IL_0dd8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0df5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0e35: Unknown result type (might be due to invalid IL or missing references)
			if (!Object.op_Implicit((Object)(object)ZNet.instance))
			{
				return false;
			}
			bool num;
			if (!serverAuthorized)
			{
				if (!ownerAuthorized && !ZNet.IsSinglePlayer)
				{
					goto IL_003e;
				}
				num = !Object.op_Implicit((Object)(object)Player.m_localPlayer);
			}
			else
			{
				num = !ZNet.instance.IsServer();
			}
			if (!num)
			{
				if (float.IsNaN(magnitude) || float.IsInfinity(magnitude) || magnitude < 0.1f || magnitude > 3f)
				{
					throw new ArgumentOutOfRangeException("magnitude");
				}
				if (!Enum.IsDefined(typeof(ToolMode), mode))
				{
					throw new ArgumentOutOfRangeException("mode");
				}
				float num2 = ((area.Shape == AreaShape.Circle) ? ((float)area.Radius) : ((float)Math.Sqrt(area.Width * area.Width + area.Length * area.Length) / 2f));
				if (float.IsNaN(num2) || float.IsInfinity(num2) || num2 <= 0f || num2 > 32f)
				{
					return false;
				}
				if (!TerrainProtection.OutsideNoBuildLocations(center, num2))
				{
					return false;
				}
				if (!serverAuthorized && !PrivateArea.CheckAccess(center, num2, false, true))
				{
					return false;
				}
				List<Heightmap> list = new List<Heightmap>();
				Heightmap.FindHeightmap(center, num2 + 1f, list);
				if (list.Count == 0)
				{
					return false;
				}
				List<Chunk> list2 = new List<Chunk>();
				HashSet<Heightmap> hashSet = new HashSet<Heightmap>();
				int num5 = default(int);
				int num6 = default(int);
				foreach (Heightmap item2 in list)
				{
					if (!Object.op_Implicit((Object)(object)item2) || !hashSet.Add(item2))
					{
						continue;
					}
					TerrainComp andCreateTerrainCompiler = item2.GetAndCreateTerrainCompiler();
					if (!Object.op_Implicit((Object)(object)andCreateTerrainCompiler) || !andCreateTerrainCompiler.IsOwner() || !(bool)Initialized.GetValue(andCreateTerrainCompiler) || HeightmapField.GetValue(andCreateTerrainCompiler) != item2)
					{
						return false;
					}
					Chunk chunk = new Chunk
					{
						Map = item2,
						Compiler = andCreateTerrainCompiler,
						Level = (float[])Level.GetValue(andCreateTerrainCompiler),
						Smooth = (float[])Smooth.GetValue(andCreateTerrainCompiler),
						ModifiedHeight = (bool[])ModifiedHeight.GetValue(andCreateTerrainCompiler),
						Paint = (Color[])Paint.GetValue(andCreateTerrainCompiler),
						ModifiedPaint = (bool[])ModifiedPaint.GetValue(andCreateTerrainCompiler)
					};
					int num3 = (int)Width.GetValue(andCreateTerrainCompiler);
					int num4 = num3 + 1;
					if (num3 != item2.m_width || chunk.Level.Length != num4 * num4 || chunk.Smooth.Length != num4 * num4 || chunk.ModifiedHeight.Length != num4 * num4 || chunk.Paint.Length != num4 * num4 || chunk.ModifiedPaint.Length != num4 * num4)
					{
						throw new InvalidOperationException("Unexpected terrain compiler dimensions.");
					}
					item2.WorldToVertex(center, ref num5, ref num6);
					int num7 = Mathf.CeilToInt((num2 + item2.m_scale) / item2.m_scale);
					for (int i = Math.Max(0, num6 - num7); i <= Math.Min(num3, num6 + num7); i++)
					{
						for (int j = Math.Max(0, num5 - num7); j <= Math.Min(num3, num5 + num7); j++)
						{
							Vector3 val = ((mode == ToolMode.Road || mode == ToolMode.Path) ? item2.VertexMaskToWorld(j, i) : (((Component)item2).transform.position + new Vector3(((float)j - (float)num3 * 0.5f) * item2.m_scale, 0f, ((float)i - (float)num3 * 0.5f) * item2.m_scale)));
							if (!area.Contains(val.x - center.x, val.z - center.z))
							{
								continue;
							}
							int num8 = i * num4 + j;
							Sample item = new Sample
							{
								Index = num8,
								X = j,
								Y = i
							};
							if (mode == ToolMode.Road || mode == ToolMode.Path)
							{
								item.Paint = chunk.Paint[num8];
								item.Modified = chunk.ModifiedPaint[num8];
								Color val2 = (item.VisiblePaint = item2.GetPaintMask(j, i));
								Color val3 = ((mode == ToolMode.Path) ? Heightmap.m_paintMaskDirt : Heightmap.m_paintMaskPaved);
								if (Mathf.Abs(val2.r - val3.r) < 0.0001f && Mathf.Abs(val2.g - val3.g) < 0.0001f && Mathf.Abs(val2.b - val3.b) < 0.0001f)
								{
									continue;
								}
							}
							else
							{
								item.Level = chunk.Level[num8];
								item.Smooth = chunk.Smooth[num8];
								item.Modified = chunk.ModifiedHeight[num8];
								item.Height = item2.GetHeight(j, i);
							}
							chunk.Samples.Add(item);
						}
					}
					if (chunk.Samples.Count > 0)
					{
						list2.Add(chunk);
					}
				}
				if (list2.Count == 0)
				{
					return false;
				}
				bool flag = mode == ToolMode.Road || mode == ToolMode.Path;
				try
				{
					int num9 = default(int);
					int num10 = default(int);
					foreach (Chunk item3 in list2)
					{
						if (flag)
						{
							item3.Map.WorldToVertexMask(center, ref num9, ref num10);
							int num11 = Mathf.CeilToInt((num2 + item3.Map.m_scale) / item3.Map.m_scale);
							if (num9 - num11 > 0 && num10 - num11 > 0 && num9 + num11 < item3.Map.m_width && num10 + num11 < item3.Map.m_width)
							{
								item3.PaintBefore = (Color[])item3.Paint.Clone();
								item3.PaintModifiedBefore = (bool[])item3.ModifiedPaint.Clone();
								bool[] array = new bool[item3.Paint.Length];
								foreach (Sample sample2 in item3.Samples)
								{
									array[sample2.Index] = true;
								}
								Settings val4 = new Settings
								{
									m_level = false,
									m_raise = false,
									m_smooth = false,
									m_paintCleared = true,
									m_paintType = (PaintType)((mode != ToolMode.Path) ? 2 : 0),
									m_paintRadius = num2 + item3.Map.m_scale,
									m_paintStrength = 1f,
									m_paintExp = 0f,
									m_halfOffset = false
								};
								PaintCleared.Invoke(item3.Compiler, new object[3]
								{
									center,
									Vector3.zero,
									val4
								});
								for (int k = 0; k < array.Length; k++)
								{
									if (!array[k])
									{
										item3.Paint[k] = item3.PaintBefore[k];
										item3.ModifiedPaint[k] = item3.PaintModifiedBefore[k];
									}
								}
							}
							else
							{
								foreach (Sample sample3 in item3.Samples)
								{
									Color paintMask = item3.Map.GetPaintMask(sample3.X, sample3.Y);
									Color val5 = ((mode == ToolMode.Path) ? Heightmap.m_paintMaskDirt : Heightmap.m_paintMaskPaved);
									item3.Paint[sample3.Index] = new Color(val5.r, val5.g, val5.b, paintMask.a);
									item3.ModifiedPaint[sample3.Index] = true;
								}
							}
						}
						if (!flag)
						{
							item3.LevelBefore = (float[])item3.Level.Clone();
							item3.SmoothBefore = (float[])item3.Smooth.Clone();
							item3.HeightBefore = (bool[])item3.ModifiedHeight.Clone();
							if (mode == ToolMode.Level)
							{
								LevelTerrain.Invoke(item3.Compiler, new object[3]
								{
									center,
									num2 + item3.Map.m_scale,
									true
								});
							}
							else
							{
								float num12 = ((mode == ToolMode.Dig) ? (-0.5f) : magnitude);
								RaiseTerrain.Invoke(item3.Compiler, new object[5]
								{
									center,
									num2 + item3.Map.m_scale,
									num12,
									true,
									0f
								});
							}
							int width = item3.Map.m_width;
							int num13 = width + 1;
							for (int l = 0; l <= width; l++)
							{
								for (int m = 0; m <= width; m++)
								{
									Vector3 val6 = ((Component)item3.Map).transform.position + new Vector3(((float)m - (float)width * 0.5f) * item3.Map.m_scale, 0f, ((float)l - (float)width * 0.5f) * item3.Map.m_scale);
									if (!area.Contains(val6.x - center.x, val6.z - center.z))
									{
										int num14 = l * num13 + m;
										item3.Level[num14] = item3.LevelBefore[num14];
										item3.Smooth[num14] = item3.SmoothBefore[num14];
										item3.ModifiedHeight[num14] = item3.HeightBefore[num14];
									}
								}
							}
						}
						item3.Map.Poke(0, flag);
					}
					foreach (Chunk item4 in list2)
					{
						bool flag2 = false;
						for (int num15 = item4.Samples.Count - 1; num15 >= 0; num15--)
						{
							Sample sample = item4.Samples[num15];
							bool flag3;
							if (flag)
							{
								Color paintMask2 = item4.Map.GetPaintMask(sample.X, sample.Y);
								flag3 = Mathf.Abs(paintMask2.r - sample.VisiblePaint.r) > 0.0001f || Mathf.Abs(paintMask2.g - sample.VisiblePaint.g) > 0.0001f || Mathf.Abs(paintMask2.b - sample.VisiblePaint.b) > 0.0001f;
							}
							else
							{
								float num16 = item4.Map.GetHeight(sample.X, sample.Y) - sample.Height;
								flag3 = mode switch
								{
									ToolMode.Dig => num16 < -0.0001f, 
									ToolMode.Level => Mathf.Abs(num16) > 0.0001f, 
									_ => num16 > 0.0001f, 
								};
							}
							if (!flag3)
							{
								Restore(item4, sample, flag);
								item4.Samples.RemoveAt(num15);
								flag2 = true;
							}
						}
						if (flag2)
						{
							item4.Map.Poke(0, flag);
						}
					}
					list2.RemoveAll((Chunk c) => c.Samples.Count == 0);
				}
				catch (Exception ex)
				{
					try
					{
						foreach (Chunk item5 in list2)
						{
							if (!flag && item5.LevelBefore != null && item5.SmoothBefore != null && item5.HeightBefore != null)
							{
								Array.Copy(item5.LevelBefore, item5.Level, item5.Level.Length);
								Array.Copy(item5.SmoothBefore, item5.Smooth, item5.Smooth.Length);
								Array.Copy(item5.HeightBefore, item5.ModifiedHeight, item5.ModifiedHeight.Length);
							}
							else if (flag && item5.PaintBefore != null && item5.PaintModifiedBefore != null)
							{
								Array.Copy(item5.PaintBefore, item5.Paint, item5.Paint.Length);
								Array.Copy(item5.PaintModifiedBefore, item5.ModifiedPaint, item5.ModifiedPaint.Length);
							}
							else
							{
								foreach (Sample sample4 in item5.Samples)
								{
									Restore(item5, sample4, flag);
								}
							}
							item5.Map.Poke(0, flag);
						}
					}
					catch (Exception ex2)
					{
						throw new TerrainOutcomeIndeterminateException(new AggregateException(ex, ex2));
					}
					throw;
				}
				if (list2.Count == 0)
				{
					return false;
				}
				try
				{
					foreach (Chunk item6 in list2)
					{
						Operations.SetValue(item6.Compiler, (int)Operations.GetValue(item6.Compiler) + 1);
						LastPoint.SetValue(item6.Compiler, center);
						LastRadius.SetValue(item6.Compiler, num2);
						Save.Invoke(item6.Compiler, new object[1] { false });
						if (revisions != null)
						{
							ZNetView component = ((Component)item6.Compiler).GetComponent<ZNetView>();
							if (!Object.op_Implicit((Object)(object)component) || !component.IsValid() || !component.IsOwner())
							{
								throw new InvalidOperationException("Terrain ownership changed while saving.");
							}
							ZDO zDO = component.GetZDO();
							revisions.Add(new TerrainRevision(zDO.m_uid, zDO.DataRevision));
							ZDOMan.instance.ForceSendZDO(zDO.m_uid);
						}
					}
				}
				catch (Exception inner)
				{
					throw new TerrainOutcomeIndeterminateException(inner);
				}
				try
				{
					if (Object.op_Implicit((Object)(object)ClutterSystem.instance))
					{
						ClutterSystem.instance.ResetGrass(center, num2);
					}
				}
				catch (Exception ex3)
				{
					Debug.LogWarning((object)("WispShovel: could not refresh clutter: " + ex3.Message));
				}
				return true;
			}
			goto IL_003e;
			IL_003e:
			return false;
		}

		private static void Restore(Chunk chunk, Sample sample, bool paintOnly)
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			if (paintOnly)
			{
				chunk.Paint[sample.Index] = sample.Paint;
				chunk.ModifiedPaint[sample.Index] = sample.Modified;
			}
			else
			{
				chunk.Level[sample.Index] = sample.Level;
				chunk.Smooth[sample.Index] = sample.Smooth;
				chunk.ModifiedHeight[sample.Index] = sample.Modified;
			}
		}
	}
	internal sealed class LocationProtectionIndex : IDisposable
	{
		private readonly Dictionary<string, float> radii = new Dictionary<string, float>(StringComparer.Ordinal);

		private bool ready;

		private bool disposed;

		internal bool Ready
		{
			get
			{
				if (ready && !disposed)
				{
					return Object.op_Implicit((Object)(object)ZoneSystem.instance);
				}
				return false;
			}
		}

		internal IEnumerator Initialize()
		{
			ready = false;
			radii.Clear();
			while (!disposed && (!Object.op_Implicit((Object)(object)ZNet.instance) || !ZNet.instance.IsServer() || !Object.op_Implicit((Object)(object)ZoneSystem.instance)))
			{
				yield return null;
			}
			if (disposed)
			{
				yield break;
			}
			ZoneSystem zones = ZoneSystem.instance;
			foreach (ZoneLocation location in zones.m_locations)
			{
				if (disposed || !Object.op_Implicit((Object)(object)zones) || (Object)(object)zones != (Object)(object)ZoneSystem.instance)
				{
					yield break;
				}
				if (location == null || !location.m_prefab.IsValid)
				{
					yield return null;
					continue;
				}
				string name = location.m_prefab.Name;
				if (radii.ContainsKey(name))
				{
					yield return null;
					continue;
				}
				bool flag = false;
				try
				{
					location.m_prefab.Load();
					flag = true;
					GameObject asset = location.m_prefab.Asset;
					Location val = (Object.op_Implicit((Object)(object)asset) ? asset.GetComponent<Location>() : null);
					if (!Object.op_Implicit((Object)(object)val))
					{
						throw new InvalidOperationException("Location prefab is unavailable: " + name);
					}
					float num = ((!val.m_noBuild) ? (-1f) : ((val.m_noBuildRadiusOverride > 0f) ? val.m_noBuildRadiusOverride : val.GetMaxRadius()));
					if (float.IsNaN(num) || float.IsInfinity(num) || (val.m_noBuild && num < 0f) || num > 4096f)
					{
						throw new InvalidOperationException("Invalid no-build radius: " + name);
					}
					radii.Add(name, num);
				}
				catch (Exception ex)
				{
					Debug.LogError((object)("WispShovel: cannot index location protection: " + ex));
					yield break;
				}
				finally
				{
					if (flag)
					{
						location.m_prefab.Release();
					}
				}
				yield return null;
			}
			ready = !disposed && Object.op_Implicit((Object)(object)zones) && (Object)(object)zones == (Object)(object)ZoneSystem.instance;
		}

		internal bool OutsideNoBuildLocations(Vector3 center, float areaRadius)
		{
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: 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_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: 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_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c8: 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_00cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0108: Unknown result type (might be due to invalid IL or missing references)
			//IL_010e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0115: Unknown result type (might be due to invalid IL or missing references)
			//IL_011b: Unknown result type (might be due to invalid IL or missing references)
			if (!ready || disposed || !Object.op_Implicit((Object)(object)ZoneSystem.instance) || areaRadius <= 0f || areaRadius > 32f || float.IsNaN(center.x) || float.IsInfinity(center.x) || float.IsNaN(center.z) || float.IsInfinity(center.z))
			{
				return false;
			}
			foreach (LocationInstance location in ZoneSystem.instance.GetLocationList())
			{
				if (location.m_location == null || !location.m_location.m_prefab.IsValid || !radii.TryGetValue(location.m_location.m_prefab.Name, out var value))
				{
					return false;
				}
				if (!(value < 0f))
				{
					Vector3 position = location.m_position;
					if (float.IsNaN(position.x) || float.IsInfinity(position.x) || float.IsNaN(position.z) || float.IsInfinity(position.z))
					{
						return false;
					}
					float num = center.x - position.x;
					float num2 = center.z - position.z;
					float num3 = value + areaRadius;
					if (num * num + num2 * num2 < num3 * num3)
					{
						return false;
					}
				}
			}
			return true;
		}

		public void Dispose()
		{
			disposed = true;
			ready = false;
			radii.Clear();
		}
	}
	internal static class NativeShovelMenu
	{
		private static readonly Dictionary<Piece, ToolMode> Modes = new Dictionary<Piece, ToolMode>();

		private static readonly Dictionary<Piece, string> Descriptions = new Dictionary<Piece, string>();

		internal static PieceTable? Table { get; private set; }

		internal static PieceTable Create(PieceTable source, Sprite shovelIcon)
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Expected O, but got Unknown
			//IL_01ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b4: Expected O, but got Unknown
			//IL_01fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_024a: Unknown result type (might be due to invalid IL or missing references)
			//IL_024f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0257: Unknown result type (might be due to invalid IL or missing references)
			//IL_025e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0265: Unknown result type (might be due to invalid IL or missing references)
			//IL_026d: Expected O, but got Unknown
			if (!Object.op_Implicit((Object)(object)source))
			{
				throw new InvalidOperationException("The vanilla hoe piece table is unavailable.");
			}
			GameObject val = new GameObject("WispShovelModeTable");
			val.SetActive(false);
			Object.DontDestroyOnLoad((Object)(object)val);
			PieceTable val2 = val.AddComponent<PieceTable>();
			val2.m_hideAdvancedMenu = true;
			val2.m_canRemovePieces = false;
			string[] array = new string[5] { "$wispshovel_dig", "$wispshovel_raise", "$wispshovel_pave", "$wispshovel_level", "$wispshovel_path" };
			string[] array2 = new string[5] { "", "raise_v2", "paved_road_v2", "mud_road_v2", "path_v2" };
			for (int i = 0; i < array.Length; i++)
			{
				Sprite icon = shovelIcon;
				string text = "";
				foreach (GameObject piece in source.m_pieces)
				{
					if (!Object.op_Implicit((Object)(object)piece) || ((Object)piece).name != array2[i])
					{
						continue;
					}
					Piece component = piece.GetComponent<Piece>();
					if (Object.op_Implicit((Object)(object)component))
					{
						if (Object.op_Implicit((Object)(object)component.m_icon))
						{
							icon = component.m_icon;
						}
						text = component.m_description;
					}
					break;
				}
				if (array2[i] != "" && text == "")
				{
					GameObject prefab = PrefabManager.Instance.GetPrefab(array2[i]);
					Piece val3 = (Object.op_Implicit((Object)(object)prefab) ? prefab.GetComponent<Piece>() : null);
					if (Object.op_Implicit((Object)(object)val3))
					{
						if (Object.op_Implicit((Object)(object)val3.m_icon))
						{
							icon = val3.m_icon;
						}
						text = val3.m_description;
					}
				}
				GameObject val4 = new GameObject("WispShovelMode" + i);
				val4.transform.SetParent(val.transform, false);
				Piece val5 = val4.AddComponent<Piece>();
				val5.m_name = array[i];
				val5.m_description = ((i == 0) ? "$wispshovel_dig_description" : text);
				val5.m_icon = icon;
				val5.m_category = (PieceCategory)0;
				GameObject prefab2 = PrefabManager.Instance.GetPrefab("Stone");
				ItemDrop val6 = (Object.op_Implicit((Object)(object)prefab2) ? prefab2.GetComponent<ItemDrop>() : null);
				val5.m_resources = (Requirement[])(((i == 1 || i == 2) && Object.op_Implicit((Object)(object)val6)) ? ((Array)new Requirement[1]
				{
					new Requirement
					{
						m_resItem = val6,
						m_amount = 1,
						m_amountPerLevel = 0,
						m_recover = false
					}
				}) : ((Array)Array.Empty<Requirement>()));
				val2.m_pieces.Add(val4);
				Modes.Add(val5, (ToolMode)i);
				Descriptions.Add(val5, val5.m_description);
			}
			Table = val2;
			return val2;
		}

		internal static bool TryGetMode(Piece piece, out ToolMode mode)
		{
			return Modes.TryGetValue(piece, out mode);
		}

		internal static void UpdateDescription(Piece piece, string area)
		{
			if (Descriptions.TryGetValue(piece, out string value))
			{
				piece.m_description = value + (string.IsNullOrEmpty(area) ? "" : ("\n" + area));
			}
		}

		internal static void UpdateCosts(Area area, int raiseCost)
		{
			foreach (KeyValuePair<Piece, ToolMode> mode in Modes)
			{
				if (mode.Key.m_resources.Length != 0)
				{
					mode.Key.m_resources[0].m_amount = ((mode.Value == ToolMode.Road) ? area.PavingStoneCost() : raiseCost);
				}
			}
		}

		internal static void Dispose()
		{
			if (Object.op_Implicit((Object)(object)Table))
			{
				Object.Destroy((Object)(object)((Component)Table).gameObject);
			}
			Table = null;
			Modes.Clear();
			Descriptions.Clear();
		}
	}
	internal sealed class NetworkActions : MonoBehaviour
	{
		private static readonly FieldInfo RightItem = AccessTools.Field(typeof(Humanoid), "m_rightItem");

		private ShovelControls? controls;

		private AreaPreview? preview;

		private ShovelConfig? config;

		private TerrainNetwork? network;

		private GameObject? stonePrefab;

		private string stoneName = "";

		private const float ActionDelay = 0.4f;

		private float nextAction;

		private float nextQuery;

		private static float BuildStamina(Player player, ItemData item)
		{
			float num = item.m_shared.m_attack.m_attackStamina * (1f + ((Character)player).GetEquipmentHomeItemModifier());
			((Character)player).GetSEMan().ModifyHomeItemStaminaUsage(num, ref num, true);
			return Mathf.Max(0f, num);
		}

		internal void Initialize(ShovelControls input, AreaPreview areaPreview, ShovelConfig settings, TerrainNetwork channel, GameObject stone)
		{
			if (!Object.op_Implicit((Object)(object)input) || !Object.op_Implicit((Object)(object)areaPreview) || !Object.op_Implicit((Object)(object)stone) || settings == null || channel == null)
			{
				throw new ArgumentNullException("Missing action dependency.");
			}
			ItemDrop component = stone.GetComponent<ItemDrop>();
			if (!Object.op_Implicit((Object)(object)component) || component.m_itemData?.m_shared == null)
			{
				throw new InvalidOperationException("Stone prefab is not an item.");
			}
			controls = input;
			preview = areaPreview;
			config = settings;
			network = channel;
			stonePrefab = stone;
			stoneName = component.m_itemData.m_shared.m_name;
			network.Response += OnResponse;
		}

		internal bool CanApply(Vector3 center, Area area)
		{
			//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)controls == (Object)null || config == null || !Object.op_Implicit((Object)(object)ZNet.instance) || !controls.IsControllable || Time.time < nextAction)
			{
				return false;
			}
			Player localPlayer = Player.m_localPlayer;
			if (!Object.op_Implicit((Object)(object)localPlayer) || !Object.op_Implicit((Object)(object)stonePrefab))
			{
				return false;
			}
			object? value = RightItem.GetValue(localPlayer);
			ItemData val = (ItemData)((value is ItemData) ? value : null);
			if (val == null || val.m_durability <= 0f || HasPending(((Humanoid)localPlayer).GetInventory()))
			{
				return false;
			}
			if (!((Character)localPlayer).HaveStamina(BuildStamina(localPlayer, val)))
			{
				return false;
			}
			float num = ((area.Shape == AreaShape.Circle) ? ((float)area.Radius) : ((float)Math.Sqrt(area.Width * area.Width + area.Length * area.Length) / 2f));
			if (num > 0f && num <= 32f && TerrainProtection.OutsideNoBuildLocations(center, num) && PrivateArea.CheckAccess(center, num, false, true))
			{
				return ((Humanoid)localPlayer).GetInventory().CountItems(stoneName, -1, false) >= StoneCost(controls.State.Mode, area, config, localPlayer.NoCostCheat());
			}
			return false;
		}

		private static int StoneCost(ToolMode mode, Area area, ShovelConfig settings, bool freeBuild)
		{
			if (!freeBuild)
			{
				return mode switch
				{
					ToolMode.Road => area.PavingStoneCost(), 
					ToolMode.Raise => settings.RaiseStoneCost.Value, 
					_ => 0, 
				};
			}
			return 0;
		}

		private void Update()
		{
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0179: Unknown result type (might be due to invalid IL or missing references)
			//IL_017f: Expected O, but got Unknown
			//IL_01c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0271: Unknown result type (might be due to invalid IL or missing references)
			//IL_0276: Unknown result type (might be due to invalid IL or missing references)
			//IL_028c: Unknown result type (might be due to invalid IL or missing references)
			//IL_042a: Unknown result type (might be due to invalid IL or missing references)
			//IL_042b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0438: Unknown result type (might be due to invalid IL or missing references)
			Player localPlayer = Player.m_localPlayer;
			if (Object.op_Implicit((Object)(object)localPlayer) && Object.op_Implicit((Object)(object)ZNet.instance) && network != null && Time.unscaledTime >= nextQuery)
			{
				nextQuery = Time.unscaledTime + 5f;
				QueryPending(localPlayer);
			}
			if ((Object)(object)controls == (Object)null || (Object)(object)preview == (Object)null || config == null || network == null || !controls.IsControllable || !ZInput.GetButtonDown("Attack") || !preview.TryGetTarget(out var center, out var area))
			{
				return;
			}
			localPlayer = Player.m_localPlayer;
			if (!CanApply(center, area))
			{
				if (Object.op_Implicit((Object)(object)localPlayer) && HasPending(((Humanoid)localPlayer).GetInventory()))
				{
					Message(localPlayer, "wispshovel_wait");
					return;
				}
				if (Object.op_Implicit((Object)(object)localPlayer))
				{
					object? value = RightItem.GetValue(localPlayer);
					ItemData val = (ItemData)((value is ItemData) ? value : null);
					if (val != null && !((Character)localPlayer).HaveStamina(BuildStamina(localPlayer, val)))
					{
						if (Object.op_Implicit((Object)(object)Hud.instance))
						{
							Hud.instance.StaminaBarEmptyFlash();
						}
						Message(localPlayer, "wispshovel_stamina");
						return;
					}
				}
				if (Object.op_Implicit((Object)(object)localPlayer) && ((Humanoid)localPlayer).GetInventory().CountItems(stoneName, -1, false) < StoneCost(controls.State.Mode, area, config, localPlayer.NoCostCheat()))
				{
					Message(localPlayer, "wispshovel_stone");
				}
				return;
			}
			ItemData val2 = (ItemData)RightItem.GetValue(localPlayer);
			ToolMode mode = controls.State.Mode;
			if (mode == ToolMode.Level && !ZInput.GetButton("AltPlace") && !ZInput.GetButton("JoyAltPlace"))
			{
				if (!Object.op_Implicit((Object)(object)ZoneSystem.instance))
				{
					return;
				}
				center.y = ZoneSystem.instance.GetGroundHeight(((Component)localPlayer).transform.position);
			}
			int num = StoneCost(mode, area, config, localPlayer.NoCostCheat());
			int stoneReward = ((mode == ToolMode.Dig && config.EnableStoneDrops.Value) ? ((!(Random.value < 0.5f)) ? Random.Range(1, 3) : 0) : 0);
			float durabilityCost = mode switch
			{
				ToolMode.Level => 1f, 
				ToolMode.Raise => 1f, 
				ToolMode.Dig => 1f, 
				_ => 1f, 
			};
			float magnitude = ((mode == ToolMode.Raise) ? ((float)controls.State.RaiseHeight) : 0.5f);
			Guid id = Guid.NewGuid();
			TerrainRequest terrainRequest;
			try
			{
				terrainRequest = TerrainRequest.Create(id, ((Character)localPlayer).GetZDOID(), center, new Vector3((float)area.ForwardX, 0f, (float)area.ForwardZ), controls.State, magnitude, config.MaxSize.Value);
			}
			catch (ArgumentException ex)
			{
				Debug.LogWarning((object)("WispShovel: invalid area: " + ex.Message));
				return;
			}
			long worldUID = ZNet.instance.GetWorldUID();
			if (worldUID == 0L)
			{
				return;
			}
			Inventory inventory = ((Humanoid)localPlayer).GetInventory();
			if (num > 0)
			{
				int num2 = inventory.CountItems(stoneName, -1, false);
				if (num2 < num)
				{
					return;
				}
				inventory.RemoveItem(stoneName, num, -1, false);
				int num3 = num2 - inventory.CountItems(stoneName, -1, false);
				if (num3 != num)
				{
					if (num3 > 0)
					{
						inventory.AddItem(stonePrefab, num3);
					}
					Debug.LogError((object)"WispShovel: could not reserve the exact stone amount; action was not sent.");
					return;
				}
			}
			PendingTerrainSettlement pendingTerrainSettlement = new PendingTerrainSettlement(id, worldUID, mode, num, stoneReward, durabilityCost, terrainRequest.Write().GetArray());
			pendingTerrainSettlement.Write(val2);
			if (!Checkpoint(localPlayer))
			{
				pendingTerrainSettlement.Result = TerrainReply.Rejected;
				pendingTerrainSettlement.Write(val2);
				Settle(localPlayer, val2, pendingTerrainSettlement);
				Message(localPlayer, "wispshovel_save_error");
				return;
			}
			if (!network.Send(terrainRequest))
			{
				pendingTerrainSettlement.Result = TerrainReply.Rejected;
				pendingTerrainSettlement.Write(val2);
				Settle(localPlayer, val2, pendingTerrainSettlement);
				return;
			}
			nextAction = Time.time + 0.4f;
			localPlayer.FaceLookDirection();
			((Character)localPlayer).GetZAnim().SetTrigger(val2.m_shared.m_attack.m_attackAnimation);
			((Character)localPlayer).UseStamina(BuildStamina(localPlayer, val2));
			EffectList? actionEffects = ShovelItemFactory.GetActionEffects(mode);
			if (actionEffects != null)
			{
				actionEffects.Create(center, Quaternion.identity, (Transform)null, 1f, -1, ((Character)localPlayer).GetZDOID());
			}
		}

		private static bool HasPending(Inventory inventory)
		{
			foreach (ItemData allItem in inventory.GetAllItems())
			{
				if (allItem.m_customData.ContainsKey("org.wispshovel.pending.v1"))
				{
					return true;
				}
			}
			return false;
		}

		private void QueryPending(Player player)
		{
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			if (network == null || !Object.op_Implicit((Object)(object)ZNet.instance))
			{
				return;
			}
			List<ItemData> allItems = ((Humanoid)player).GetInventory().GetAllItems();
			foreach (ItemData item in allItems)
			{
				if (!PendingTerrainSettlement.TryRead(item, out PendingTerrainSettlement pending) || pending == null || pending.World != ZNet.instance.GetWorldUID())
				{
					continue;
				}
				if (CountId(allItems, pending.Id) != 1)
				{
					Debug.LogError((object)"WispShovel: duplicate pending operation on items; settlement suspended.");
					continue;
				}
				if (pending.Result.HasValue && pending.Result.Value != TerrainReply.Indeterminate)
				{
					Settle(player, item, pending);
					break;
				}
				network.Retry(pending.RequestBytes, pending.Id, ((Character)player).GetZDOID());
			}
		}

		private void OnResponse(Guid id, TerrainReply reply)
		{
			Player localPlayer = Player.m_localPlayer;
			if (!Object.op_Implicit((Object)(object)localPlayer) || !Object.op_Implicit((Object)(object)ZNet.instance) || reply == TerrainReply.Indeterminate)
			{
				return;
			}
			List<ItemData> allItems = ((Humanoid)localPlayer).GetInventory().GetAllItems();
			if (CountId(allItems, id) != 1)
			{
				return;
			}
			foreach (ItemData item in allItems)
			{
				if (PendingTerrainSettlement.TryRead(item, out PendingTerrainSettlement pending) && pending != null && !(pending.Id != id) && pending.World == ZNet.instance.GetWorldUID())
				{
					if (pending.Result.HasValue && pending.Result.Value != TerrainReply.Indeterminate && pending.Result.Value != reply)
					{
						Debug.LogError((object)"WispShovel: conflicting settlement results; settlement suspended.");
						break;
					}
					pending.Result = reply;
					pending.Write(item);
					Settle(localPlayer, item, pending);
					break;
				}
			}
		}

		private static int CountId(List<ItemData> items, Guid id)
		{
			int num = 0;
			foreach (ItemData item in items)
			{
				if (PendingTerrainSettlement.TryRead(item, out PendingTerrainSettlement pending) && pending != null && pending.Id == id)
				{
					num++;
				}
			}
			return num;
		}

		private void Settle(Player player, ItemData item, PendingTerrainSettlement pending)
		{
			//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dc: Expected O, but got Unknown
			//IL_010c: 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_0113: Unknown result type (might be due to invalid IL or missing references)
			//IL_013d: Unknown result type (might be due to invalid IL or missing references)
			//IL_013f: Unknown result type (might be due to invalid IL or missing references)
			if (!Object.op_Implicit((Object)(object)stonePrefab) || !pending.Result.HasValue || pending.Result == TerrainReply.Indeterminate)
			{
				return;
			}
			bool flag = pending.Result == TerrainReply.Changed;
			int num = (flag ? pending.StoneReward : pending.ReservedStone);
			Inventory inventory = ((Humanoid)player).GetInventory();
			ItemData val = stonePrefab.GetComponent<ItemDrop>().m_itemData.Clone();
			val.m_dropPrefab = stonePrefab;
			val.m_worldLevel = (byte)Game.m_worldLevel;
			int val2 = Math.Max(1, val.m_shared.m_maxStackSize);
			if (flag && num > 0)
			{
				if (!TerrainRequest.TryRead(new ZPackage(pending.RequestBytes), 100f, out TerrainRequest request) || request == null || request.Id != pending.Id || request.Mode != ToolMode.Dig)
				{
					Debug.LogError((object)"WispShovel: cannot restore the dig location; stone settlement is pending.");
					return;
				}
				Vector3 center = request.Center;
				float num2 = default(float);
				if (!Heightmap.GetHeight(center, ref num2))
				{
					return;
				}
				center.y = num2 + 0.5f;
				while (num > 0)
				{
					int num3 = Math.Min(num, val2);
					if (!Object.op_Implicit((Object)(object)ItemDrop.DropItem(val, num3, center, Quaternion.identity)))
					{
						Debug.LogError((object)"WispShovel: could not spawn stone; settlement is pending.");
						return;
					}
					pending.RecordStoneDelivered(num3, changed: true);
					pending.Write(item);
					num -= num3;
				}
			}
			while (num > 0)
			{
				int num4 = 0;
				int num5 = Math.Min(num, val2);
				while (num4 < num5)
				{
					int num6 = num4 + (num5 - num4 + 1) / 2;
					if (inventory.CanAddItem(val, num6))
					{
						num4 = num6;
					}
					else
					{
						num5 = num6 - 1;
					}
				}
				if (num4 == 0)
				{
					pending.Write(item);
					Checkpoint(player);
					Message(player, "wispshovel_make_room");
					return;
				}
				int num7 = inventory.CountItems(stoneName, -1, false);
				inventory.AddItem(stonePrefab, num4);
				int num8 = Math.Max(0, Math.Min(num4, inventory.CountItems(stoneName, -1, false) - num7));
				pending.RecordStoneDelivered(num8, flag);
				pending.Write(item);
				num -= num8;
				if (num8 != num4)
				{
					Checkpoint(player);
					Debug.LogError((object)"WispShovel: incomplete stone settlement; will retry.");
					return;
				}
			}
			if (flag && !pending.DurabilityApplied)
			{
				item.m_durability = Mathf.Max(0f, item.m_durability - pending.DurabilityCost);
				pending.DurabilityApplied = true;
			}
			item.m_customData.Remove("org.wispshovel.pending.v1");
			if (!Checkpoint(player))
			{
				pending.Write(item);
			}
		}

		private bool Checkpoint(Player player)
		{
			if (config != null)
			{
				return true;
			}
			if (!Object.op_Implicit((Object)(object)player) || !Object.op_Implicit((Object)(object)Game.instance) || SaveSystem.HasSessionFlag((SaveSystemSessionFlags)1))
			{
				return false;
			}
			try
			{
				PlayerProfile playerProfile = Game.instance.GetPlayerProfile();
				if (playerProfile == null)
				{
					return false;
				}
				playerProfile.SavePlayerData(player);
				return playerProfile.Save();
			}
			catch (Exception ex)
			{
				Debug.LogError((object)("WispShovel: character checkpoint failed: " + ex));
				return false;
			}
		}

		private static void Message(Player player, string key)
		{
			((Character)player).Message((MessageType)2, TranslationFiles.Text(key), 0, (Sprite)null, false);
		}

		private void OnDestroy()
		{
			if (network != null)
			{
				network.Response -= OnResponse;
			}
		}
	}
	internal sealed class PendingTerrainSettlement
	{
		internal const string Key = "org.wispshovel.pending.v1";

		internal Guid Id { get; private set; }

		internal long World { get; private set; }

		internal ToolMode Mode { get; private set; }

		internal int ReservedStone { get; private set; }

		internal int StoneReward { get; private set; }

		internal float DurabilityCost { get; private set; }

		internal TerrainReply? Result { get; set; }

		internal bool DurabilityApplied { get; set; }

		internal byte[] RequestBytes { get; private set; }

		internal void RecordStoneDelivered(int amount, bool changed)
		{
			if (amount < 0 || amount > (changed ? StoneReward : ReservedStone))
			{
				throw new ArgumentOutOfRangeException("amount");
			}
			if (changed)
			{
				StoneReward -= amount;
			}
			else
			{
				ReservedStone -= amount;
			}
		}

		internal PendingTerrainSettlement(Guid id, long world, ToolMode mode, int reservedStone, int stoneReward, float durabilityCost, byte[] requestBytes)
		{
			if (id == Guid.Empty || world == 0L || !Enum.IsDefined(typeof(ToolMode), mode) || reservedStone < 0 || reservedStone > 10000 || stoneReward < 0 || stoneReward > 10000 || float.IsNaN(durabilityCost) || float.IsInfinity(durabilityCost) || durabilityCost < 0f || durabilityCost > 100000f || (mode != ToolMode.Raise && mode != ToolMode.Road && reservedStone != 0) || (mode != ToolMode.Dig && stoneReward != 0) || requestBytes == null || requestBytes.Length < 32 || requestBytes.Length > 128)
			{
				throw new ArgumentException("Invalid pending terrain settlement.");
			}
			Id = id;
			World = world;
			Mode = mode;
			ReservedStone = reservedStone;
			StoneReward = stoneReward;
			DurabilityCost = durabilityCost;
			RequestBytes = (byte[])requestBytes.Clone();
		}

		internal void Write(ItemData item)
		{
			item.m_customData["org.wispshovel.pending.v1"] = string.Join("|", "1", Id.ToString("N"), World.ToString(CultureInfo.InvariantCulture), ((int)Mode).ToString(CultureInfo.InvariantCulture), ReservedStone.ToString(CultureInfo.InvariantCulture), StoneReward.ToString(CultureInfo.InvariantCulture), DurabilityCost.ToString("R", CultureInfo.InvariantCulture), ((int)(Result ?? TerrainReply.Indeterminate)).ToString(CultureInfo.InvariantCulture), DurabilityApplied ? "1" : "0", Convert.ToBase64String(RequestBytes));
		}

		internal static bool TryRead(ItemData item, out PendingTerrainSettlement? pending)
		{
			pending = null;
			if (!item.m_customData.TryGetValue("org.wispshovel.pending.v1", out var value) || value == null || value.Length > 512)
			{
				return false;
			}
			string[] array = value.Split('|');
			if (array.Length != 10 || array[0] != "1" || !Guid.TryParseExact(array[1], "N", out var result) || !long.TryParse(array[2], NumberStyles.Integer, CultureInfo.InvariantCulture, out var result2) || !int.TryParse(array[3], NumberStyles.Integer, CultureInfo.InvariantCulture, out var result3) || !int.TryParse(array[4], NumberStyles.Integer, CultureInfo.InvariantCulture, out var result4) || !int.TryParse(array[5], NumberStyles.Integer, CultureInfo.InvariantCulture, out var result5) || !float.TryParse(array[6], NumberStyles.Float, CultureInfo.InvariantCulture, out var result6) || !byte.TryParse(array[7], NumberStyles.Integer, CultureInfo.InvariantCulture, out var result7) || !Enum.IsDefined(typeof(TerrainReply), result7) || (array[8] != "0" && array[8] != "1"))
			{
				return false;
			}
			try
			{
				pending = new PendingTerrainSettlement(result, result2, (ToolMode)result3, result4, result5, result6, Convert.FromBase64String(array[9]));
				pending.Result = (TerrainReply)result7;
				pending.DurabilityApplied = array[8] == "1";
				return true;
			}
			catch (Exception ex) when (ex is ArgumentException || ex is FormatException)
			{
				return false;
			}
		}
	}
	[BepInPlugin("org.wispshovel.valheim", "WispShovel", "1.0.1")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[NetworkCompatibility(/*Could not decode attribute arguments.*/)]
	public sealed class Plugin : BaseUnityPlugin
	{
		private ShovelConfig? settings;

		private ShovelVisuals? visuals;

		private RecipeAndDurabilityPatches? rules;

		private ShovelAttackGuard? attackGuard;

		private CustomItem? registeredItem;

		private TerrainNetwork? terrainNetwork;

		private ShovelControls? controls;

		private AreaPreview? preview;

		private NetworkActions? actions;

		private void Awake()
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Expected O, but got Unknown
			ConfigFile val = new ConfigFile(Path.Combine(Paths.ConfigPath, "WispShovel.cfg"), true);
			val.SaveOnConfigSet = false;
			settings = new ShovelConfig(val);
			ConfigLayout.Initialize(val);
			SynchronizationManager.Instance.RegisterCustomConfig(val);
			terrainNetwork = new TerrainNetwork((MonoBehaviour)(object)this, settings);
			TranslationFiles.Load();
			PrefabManager.OnVanillaPrefabsAvailable += RegisterItem;
			((BaseUnityPlugin)this).Logger.LogWarning((object)"WispShovel has not been verified in Valheim. Use a separate test world first.");
		}

		private void RegisterItem()
		{
			//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f2: Invalid comparison between Unknown and I4
			PrefabManager.OnVanillaPrefabsAvailable -= RegisterItem;
			try
			{
				if (settings == null)
				{
					throw new InvalidOperationException("Configuration is unavailable.");
				}
				visuals = new ShovelVisuals();
				CustomItem val = ShovelItemFactory.Create(settings, visuals.Icon, visuals.DigIcon);
				visuals.Apply(val.ItemPrefab);
				rules = new RecipeAndDurabilityPatches(settings, val.Recipe.Recipe);
				attackGuard = new ShovelAttackGuard();
				if (!ItemManager.Instance.AddItem(val))
				{
					throw new InvalidOperationException("Jötunn rejected the custom item.");
				}
				registeredItem = val;
				OnMistSettingChanged(this, EventArgs.Empty);
				settings.EnableMistClearing.SettingChanged += OnMistSettingChanged;
				settings.EnableMod.SettingChanged += OnMistSettingChanged;
				if ((int)SystemInfo.graphicsDeviceType != 4)
				{
					if (terrainNetwork == null)
					{
						throw new InvalidOperationException("Terrain network is unavailable.");
					}
					GameObject prefab = PrefabManager.Instance.GetPrefab("Stone");
					if (!Object.op_Implicit((Object)(object)prefab))
					{
						throw new InvalidOperationException("Vanilla Stone prefab is unavailable.");
					}
					controls = ((Component)this).gameObject.AddComponent<ShovelControls>();
					preview = ((Component)this).gameObject.AddComponent<AreaPreview>();
					actions = ((Component)this).gameObject.AddComponent<NetworkActions>();
					controls.Initialize(settings);
					preview.Initialize(controls, actions.CanApply);
					actions.Initialize(controls, preview, settings, terrainNetwork, prefab);
				}
				((BaseUnityPlugin)this).Logger.LogInfo((object)"WispShovel item and terrain handlers registered.");
			}
			catch (Exception ex)
			{
				if (Object.op_Implicit((Object)(object)actions))
				{
					Object.Destroy((Object)(object)actions);
				}
				if (Object.op_Implicit((Object)(object)preview))
				{
					Object.Destroy((Object)(object)preview);
				}
				if (Object.op_Implicit((Object)(object)controls))
				{
					Object.Destroy((Object)(object)controls);
				}
				if (registeredItem == null)
				{
					NativeShovelMenu.Dispose();
					terrainNetwork?.Dispose();
					rules?.Dispose();
					rules = null;
					attackGuard?.Dispose();
					attackGuard = null;
					visuals?.Dispose();
					visuals = null;
				}
				((BaseUnityPlugin)this).Logger.LogError((object)("Could not register WispShovel: " + ex));
			}
		}

		private void OnMistSettingChanged(object sender, EventArgs args)
		{
			if (settings == null || registeredItem == null)
			{
				return;
			}
			Demister[] array = Resources.FindObjectsOfTypeAll<Demister>();
			foreach (Demister val in array)
			{
				if (Object.op_Implicit((Object)(object)val) && ((Object)((Component)val).gameObject).name == "WispShovelMistClear")
				{
					((Component)val).gameObject.SetActive(settings.EnableMod.Value && settings.EnableMistClearing.Value);
				}
			}
		}

		private void OnDestroy()
		{
			PrefabManager.OnVanillaPrefabsAvailable -= RegisterItem;
			if (settings != null)
			{
				settings.EnableMistClearing.SettingChanged -= OnMistSettingChanged;
				settings.EnableMod.SettingChanged -= OnMistSettingChanged;
			}
			if (Object.op_Implicit((Object)(object)actions))
			{
				Object.Destroy((Object)(object)actions);
			}
			if (Object.op_Implicit((Object)(object)preview))
			{
				Object.Destroy((Object)(object)preview);
			}
			if (Object.op_Implicit((Object)(object)controls))
			{
				Object.Destroy((Object)(object)controls);
			}
			terrainNetwork?.Dispose();
			attackGuard?.Dispose();
			rules?.Dispose();
			visuals?.Dispose();
			NativeShovelMenu.Dispose();
		}
	}
	public sealed class RecipeAndDurabilityPatches : IDisposable
	{
		private static readonly Dictionary<Requirement, int> Requirements = new Dictionary<Requirement, int>();

		private static ShovelConfig? settings;

		private static Recipe? shovelRecipe;

		private readonly Harmony harmony;

		private readonly ShovelConfig config;

		private readonly Requirement[] recipeRequirements;

		private bool disposed;

		public RecipeAndDurabilityPatches(ShovelConfig config, Recipe recipe)
		{
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: Expected O, but got Unknown
			//IL_0134: Unknown result type (might be due to invalid IL or missing references)
			//IL_0142: Expected O, but got Unknown
			//IL_0181: Unknown result type (might be due to invalid IL or missing references)
			//IL_018f: Expected O, but got Unknown
			//IL_01d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e1: Expected O, but got Unknown
			if (config == null)
			{
				throw new ArgumentNullException("config");
			}
			if ((Object)(object)recipe == (Object)null)
			{
				throw new ArgumentNullException("recipe");
			}
			if (settings != null)
			{
				throw new InvalidOperationException("Shovel rules are already installed.");
			}
			if (recipe.m_resources == null || recipe.m_resources.Length != 4)
			{
				throw new ArgumentException("Expected requirements in Silver, Iron, RoundLog, Demister order.", "recipe");
			}
			HashSet<Requirement> hashSet = new HashSet<Requirement>();
			for (int i = 0; i < recipe.m_resources.Length; i++)
			{
				if (recipe.m_resources[i] == null || !hashSet.Add(recipe.m_resources[i]))
				{
					throw new ArgumentException("Recipe requirements must be distinct and non-null.", "recipe");
				}
			}
			harmony = new Harmony("org.wispshovel.rules");
			this.config = config;
			recipeRequirements = recipe.m_resources;
			settings = config;
			shovelRecipe = recipe;
			for (int j = 0; j < recipe.m_resources.Length; j++)
			{
				Requirements.Add(recipe.m_resources[j], j);
			}
			try
			{
				harmony.Patch((MethodBase)AccessTools.Method(typeof(Requirement), "GetAmount", new Type[1] { typeof(int) }, (Type[])null), new HarmonyMethod(typeof(RecipeAndDurabilityPatches), "RequirementPrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				harmony.Patch((MethodBase)AccessTools.Method(typeof(ItemData), "GetMaxDurability", new Type[1] { typeof(int) }, (Type[])null), new HarmonyMethod(typeof(RecipeAndDurabilityPatches), "DurabilityPrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				harmony.Patch((MethodBase)AccessTools.Method(typeof(Player), "GetAvailableRecipes", new Type[1] { typeof(List<Recipe>).MakeByRefType() }, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(RecipeAndDurabilityPatches), "AvailableRecipesPostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			}
			catch
			{
				harmony.UnpatchSelf();
				Requirements.Clear();
				settings = null;
				shovelRecipe = null;
				throw;
			}
		}

		private void UpdateRecipeAmounts(object sender, EventArgs args)
		{
			for (int i = 0; i < recipeRequirements.Length; i++)
			{
				recipeRequirements[i].m_amount = config.GetResourceCost(1, i);
			}
		}

		private static bool RequirementPrefix(Requirement __instance, int __0, ref int __result)
		{
			if (settings == null || __0 < 1 || __0 > 4 || !Requirements.TryGetValue(__instance, out var value))
			{
				return true;
			}
			__result = settings.GetResourceCost(__0, value);
			return false;
		}

		private static bool DurabilityPrefix(ItemData __instance, int __0, ref float __result)
		{
			if (settings == null || __0 < 1 || __0 > 4 || __instance.m_shared == null || __instance.m_shared.m_name != "$item_wispshovel")
			{
				return true;
			}
			__result = settings.Durability[__0 - 1];
			return false;
		}

		private static void AvailableRecipesPostfix(Player __instance, List<Recipe> __0)
		{
			if (!((Object)(object)shovelRecipe == (Object)null) && settings != null && settings.GetResourceCost(1, 3) > 0)
			{
				ItemDrop resItem = shovelRecipe.m_resources[3].m_resItem;
				if (!Object.op_Implicit((Object)(object)resItem) || resItem.m_itemData?.m_shared == null || !__instance.IsKnownMaterial(resItem.m_itemData.m_shared.m_name))
				{
					__0.Remove(shovelRecipe);
				}
			}
		}

		public void Dispose()
		{
			if (!disposed)
			{
				harmony.UnpatchSelf();
				Requirements.Clear();
				settings = null;
				shovelRecipe = null;
				disposed = true;
			}
		}
	}
	internal static class RemoteAccessValidator
	{
		private static readonly FieldInfo Areas = AccessTools.Field(typeof(PrivateArea), "m_allAreas") ?? throw new MissingFieldException("PrivateArea", "m_allAreas");

		private static readonly FieldInfo WardPiece = AccessTools.Field(typeof(PrivateArea), "m_piece") ?? throw new MissingFieldException("PrivateArea", "m_piece");

		private static readonly MethodInfo IsEnabled = RequiredMethod("IsEnabled", Type.EmptyTypes);

		private static readonly MethodInfo IsInside = RequiredMethod("IsInside", new Type[2]
		{
			typeof(Vector3),
			typeof(float)
		});

		private static readonly MethodInfo IsPermitted = RequiredMethod("IsPermitted", new Type[1] { typeof(long) });

		internal static bool TryResolveActor(long sender, ZDOID actorId, out Player? player, out long playerId)
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			player = null;
			playerId = 0L;
			if (!TryResolveIdentity(sender, actorId, out playerId) || !Object.op_Implicit((Object)(object)ZNetScene.instance))
			{
				return false;
			}
			GameObject val = ZNetScene.instance.FindInstance(actorId);
			if (!Object.op_Implicit((Object)(object)val))
			{
				return false;
			}
			ZNetView component = val.GetComponent<ZNetView>();
			player = val.GetComponent<Player>();
			if (!Object.op_Implicit((Object)(object)component) || !component.IsValid() || component.GetZDO().GetOwner() != sender || !Object.op_Implicit((Object)(object)player))
			{
				return false;
			}
			return player.GetPlayerID() == playerId;
		}

		internal static bool TryResolveIdentity(long sender, ZDOID actorId, out long playerId)
		{
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			playerId = 0L;
			if (sender == 0L || ((ZDOID)(ref actorId)).IsNone() || !Object.op_Implicit((Object)(object)ZNet.instance) || ZDOMan.instance == null)
			{
				return false;
			}
			if (sender != ZNet.GetUID())
			{
				ZNetPeer peer = ZNet.instance.GetPeer(sender);
				if (peer == null || peer.m_characterID != actorId)
				{
					return false;
				}
			}
			else if (!Object.op_Implicit((Object)(object)Player.m_localPlayer) || ((Character)Player.m_localPlayer).GetZDOID() != actorId)
			{
				return false;
			}
			ZDO zDO = ZDOMan.instance.GetZDO(actorId);
			if (zDO == null || zDO.GetOwner() != sender)
			{
				return false;
			}
			playerId = zDO.GetLong(ZDOVars.s_playerID, 0L);
			return playerId != 0;
		}

		internal static bool CanModify(Player player, long playerId, Vector3 center, float radius)
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: 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_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: 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_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_0122: Unknown result type (might be due to invalid IL or missing references)
			if (!Object.op_Implicit((Object)(object)player) || ((Character)player).IsDead() || playerId == 0L || !Finite(center.x) || !Finite(center.y) || !Finite(center.z) || !Finite(radius) || radius <= 0f || radius > 32f)
			{
				return false;
			}
			Vector3 eyePoint = ((Character)player).GetEyePoint();
			if (playerId == 0L || !Finite(eyePoint.x) || !Finite(eyePoint.y) || !Finite(eyePoint.z) || !Finite(player.m_maxPlaceDistance) || player.m_maxPlaceDistance <= 0f || Vector3.Distance(eyePoint, center) >= player.m_maxPlaceDistance)
			{
				return false;
			}
			if (!SurroundingZonesLoaded(center, radius + 64f))
			{
				return false;
			}
			if (!TerrainProtection.OutsideNoBuildLocations(center, radius))
			{
				return false;
			}
			if (!(Areas.GetValue(null) is List<PrivateArea> list))
			{
				return false;
			}
			foreach (PrivateArea item in list)
			{
				if (Object.op_Implicit((Object)(object)item) && (bool)IsEnabled.Invoke(item, null) && (bool)IsInside.Invoke(item, new object[2] { center, radius }))
				{
					object? value = WardPiece.GetValue(item);
					Piece val = (Piece)((value is Piece) ? value : null);
					if (!Object.op_Implicit((Object)(object)val))
					{
						return false;
					}
					if (val.GetCreator() != playerId && !(bool)IsPermitted.Invoke(item, new object[1] { playerId }))
					{
						return false;
					}
				}
			}
			return true;
		}

		private static bool SurroundingZonesLoaded(Vector3 center, float radius)
		{
			//IL_0010: 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_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			ZoneSystem instance = ZoneSystem.instance;
			if (!Object.op_Implicit((Object)(object)instance))
			{
				return false;
			}
			Vector2s zone = ZoneSystem.GetZone(center - new Vector3(radius, 0f, radius));
			Vector2s zone2 = ZoneSystem.GetZone(center + new Vector3(radius, 0f, radius));
			for (int i = zone.x; i <= zone2.x; i++)
			{
				for (int j = zone.y; j <= zone2.y; j++)
				{
					if (!instance.IsZoneLoaded(new Vector2s(i, j)))
					{
						return false;
					}
				}
			}
			return true;
		}

		private static bool Finite(float value)
		{
			if (!float.IsNaN(value))
			{
				return !float.IsInfinity(value);
			}
			return false;
		}

		private static MethodInfo RequiredMethod(string name, Type[] parameters)
		{
			return AccessTools.Method(typeof(PrivateArea), name, parameters, (Type[])null) ?? throw new MissingMethodException("PrivateArea", name);
		}
	}
	public sealed class ShovelAttackGuard : IDisposable
	{
		private static readonly FieldInfo RightItem = AccessTools.Field(typeof(Humanoid), "m_rightItem");

		private readonly Harmony harmony = new Harmony("org.wispshovel.attackguard");

		public ShovelAttackGuard()
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Expected O, but got Unknown
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Expected O, but got Unknown
			if (RightItem == null)
			{
				throw new InvalidOperationException("Cannot find the equipped right-hand item field.");
			}
			harmony.Patch((MethodBase)AccessTools.Method(typeof(Humanoid), "StartAttack", new Type[2]
			{
				typeof(Character),
				typeof(bool)
			}, (Type[])null), new HarmonyMethod(typeof(ShovelAttackGuard), "Prefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
		}

		private static bool Prefix(Humanoid __instance, ref bool __result)
		{
			object? value = RightItem.GetValue(__instance);
			ItemData val = (ItemData)((value is ItemData) ? value : null);
			if (val?.m_shared == null || val.m_shared.m_name != "$item_wispshovel")
			{
				return true;
			}
			__result = false;
			return false;
		}

		public void Dispose()
		{
			harmony.UnpatchSelf();
		}
	}
	public sealed class ShovelConfig
	{
		public const bool DurableActionSaves = false;

		public readonly float[] Durability = new float[4] { 200f, 400f, 600f, 800f };

		public const float DigCost = 1f;

		public const float RaiseCost = 1f;

		public const float RoadCost = 1f;

		public const float LevelCost = 1f;

		private static readonly int[,] recipe = new int[4, 4]
		{
			{ 4, 3, 6, 1 },
			{ 2, 2, 4, 0 },
			{ 4, 3, 6, 0 },
			{ 6, 4, 8, 0 }
		};

		public ConfigEntry<bool> EnableMod { get; }

		public ConfigEntry<bool> EnableMistClearing { get; }

		public ConfigEntry<bool> EnableStoneDrops { get; }

		public ConfigEntry<bool> EnableAreaPreview { get; }

		public ConfigEntry<bool> ShowToolHud { get; }

		public ConfigEntry<int> RaiseStoneCost { get; }

		public ConfigEntry<float> CircleRadius { get; }

		public ConfigEntry<float> SquareSize { get; }

		public ConfigEntry<float> RectangleWidth { get; }

		public ConfigEntry<float> RectangleLength { get; }

		public ConfigEntry<float> MaxSize { get; }

		public ConfigEntry<KeyboardShortcut> ShapeKey { get; }

		public ConfigEntry<KeyboardShortcut> RectangleSideKey { get; }

		public ConfigEntry<KeyCode> SizeModifier { get; }

		public ShovelConfig(ConfigFile file)
		{
			//IL_01a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d0: Unknown result type (might be due to invalid IL or missing references)
			EnableMod = file.Bind<bool>("General", "EnableMod", true, Synced("Enable WispShovel actions and mist clearing."));
			EnableMistClearing = file.Bind<bool>("General", "EnableMistClearing", true, Synced("Enable mist clearing while the shovel is equipped."));
			EnableStoneDrops = file.Bind<bool>("General", "EnableStoneDrops", true, Synced("Give stone only when a dig is confirmed to have changed terrain."));
			EnableAreaPreview = file.Bind<bool>("General", "EnableAreaPreview", true, "Show the targeted area.");
			ShowToolHud = file.Bind<bool>("Visual", "ShowToolHUD", true, "Show shovel controls and current settings.");
			RaiseStoneCost = Integer(file, "Raise", "StoneCost", 1);
			CircleRadius = Float(file, "Area", "CircleRadius", 2f, 0.8f, 50f);
			SquareSize = Float(file, "Area", "SquareSize", 4f, 0.1f, 100f);
			RectangleLength = Float(file, "Area", "RectangleLength", 5f, 0.1f, 100f);
			RectangleWidth = Float(file, "Area", "RectangleWidth", 2f, 0.1f, 100f);
			MaxSize = Float(file, "Area", "MaxSize", 8f, 1.6f, 100f, synced: true);
			ShapeKey = file.Bind<KeyboardShortcut>("Controls", "ShapeKey", new KeyboardShortcut((KeyCode)118, Array.Empty<KeyCode>()), "Cycle the area shape.");
			RectangleSideKey = file.Bind<KeyboardShortcut>("Controls", "RectangleSideKey", new KeyboardShortcut((KeyCode)118, (KeyCode[])(object)new KeyCode[1] { (KeyCode)308 }), "Change the adjustable rectangle side.");
			SizeModifier = file.Bind<KeyCode>("Controls", "SizeModifier", (KeyCode)308, "Hold with the mouse wheel to change area size.");
		}

		public ItemRules ReadItemRules()
		{
			ResourceCost[] array = new ResourceCost[4];
			float[] array2 = new float[4];
			for (int i = 0; i < 4; i++)
			{
				array[i] = new ResourceCost(recipe[i, 0], recipe[i, 1], recipe[i, 2], recipe[i, 3]);
				array2[i] = Durability[i];
			}
			return new ItemRules(array, array2);
		}

		public int GetResourceCost(int quality, int resource)
		{
			if (quality < 1 || quality > 4)
			{
				throw new ArgumentOutOfRangeException("quality");
			}
			if (resource < 0 || resource > 3)
			{
				throw new ArgumentOutOfRangeException("resource");
			}
			return recipe[quality - 1, resource];
		}

		private static ConfigDescription Synced(string description)
		{
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Expected O, but got Unknown
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Expected O, but got Unknown
			return new ConfigDescription(description, (AcceptableValueBase)null, new object[1] { (object)new ConfigurationManagerAttributes
			{
				IsAdminOnly = true
			} });
		}

		private static ConfigEntry<float> Float(ConfigFile f, string section, string key, float value, float min, float max, bool synced = false)
		{
			//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_0045: Expected O, but got Unknown
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Expected O, but got Unknown
			return f.Bind<float>(section, key, value, synced ? new ConfigDescription("", (AcceptableValueBase)(object)new AcceptableValueRange<float>(min, max), new object[1] { (object)new ConfigurationManagerAttributes
			{
				IsAdminOnly = true
			} }) : new ConfigDescription("", (AcceptableValueBase)(object)new AcceptableValueRange<float>(min, max), Array.Empty<object>()));
		}

		private static ConfigEntry<int> Integer(ConfigFile f, string section, string key, int value)
		{
			//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_0029: Expected O, but got Unknown
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Expected O, but got Unknown
			return f.Bind<int>(section, key, value, new ConfigDescription("", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 10000), new object[1] { (object)new ConfigurationManagerAttributes
			{
				IsAdminOnly = true
			} }));
		}
	}
	public sealed class ShovelControls : MonoBehaviour
	{
		private static readonly FieldInfo RightItem = AccessTools.Field(typeof(Humanoid), "m_rightItem");

		private static readonly FieldInfo PlacementGhost = AccessTools.Field(typeof(Player), "m_placementGhost");

		private static readonly MethodInfo BuildInput = AccessTools.Method(typeof(Player), "UpdateBuildGuiInput", (Type[])null, (Type[])null);

		private static ShovelControls? active;

		private ShovelConfig? config;

		private Harmony? harmony;

		private int scrollFrame = -1;

		private TMP_Text? controlHints;

		public ToolState State { get; private set; } = new ToolState();

		public bool IsControllable
		{
			get
			{
				if (CanControl())
				{
					return !Hud.IsPieceSelectionVisible();
				}
				return false;
			}
		}

		public bool PreviewEnabled
		{
			get
			{
				if (config != null)
				{
					return config.EnableAreaPreview.Value;
				}
				return false;
			}
		}

		public void Initialize(ShovelConfig settings)
		{
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ad: Expected O, but got Unknown
			//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ed: Expected O, but got Unknown
			//IL_011a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0128: Expected O, but got Unknown
			//IL_0155: Unknown result type (might be due to invalid IL or missing references)
			//IL_0163: Expected O, but got Unknown
			//IL_0190: Unknown result type (might be due to invalid IL or missing references)
			//IL_019e: Expected O, but got Unknown
			//IL_01de: Unknown result type (might be due to invalid IL or missing references)
			//IL_01eb: Expected O, but got Unknown
			if ((Object)(object)active != (Object)null || config != null)
			{
				throw new InvalidOperationException("Shovel controls are already initialized.");
			}
			config = settings ?? throw new ArgumentNullException("settings");
			State = new ToolState(settings.CircleRadius.Value, settings.SquareSize.Value, settings.RectangleWidth.Value, settings.RectangleLength.Value, settings.MaxSize.Value);
			settings.MaxSize.SettingChanged += OnMaximumChanged;
			harmony = new Harmony("org.wispshovel.controls");
			active = this;
			try
			{
				harmony.Patch((MethodBase)AccessTools.Method(typeof(ZInput), "GetMouseScrollWheel", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(ShovelControls), "ScrollPostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				harmony.Patch((MethodBase)AccessTools.Method(typeof(PieceTable), "UpdateAvailable", (Type[])null, (Type[])null), new HarmonyMethod(typeof(ShovelControls), "AvailablePrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				harmony.Patch((MethodBase)AccessTools.Method(typeof(Player), "UpdatePlacement", (Type[])null, (Type[])null), new HarmonyMethod(typeof(ShovelControls), "PlacementPrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				harmony.Patch((MethodBase)AccessTools.Method(typeof(Player), "SetupPlacementGhost", (Type[])null, (Type[])null), new HarmonyMethod(typeof(ShovelControls), "GhostPrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				harmony.Patch((MethodBase)AccessTools.Method(typeof(Player), "SetSelectedPiece", new Type[1] { typeof(Vector2Int) }, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(ShovelControls), "SelectionPostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			}
			catch
			{
				Unsubscribe(settings);
				harmony.UnpatchSelf();
				active = null;
				config = null;
				throw;
			}
		}

		private void Unsubscribe(ShovelConfig settings)
		{
			settings.MaxSize.SettingChanged -= OnMaximumChanged;
		}

		private void OnMaximumChanged(object sender, EventArgs args)
		{
			if (config != null)
			{
				State.SetMaximumSize(config.MaxSize.Value);
			}
		}

		private bool CanControl()
		{
			Player localPlayer = Player.m_localPlayer;
			if (config == null || !config.EnableMod.Value || !Object.op_Implicit((Object)(object)localPlayer) || ((Character)localPlayer).IsDead() || ((Character)localPlayer).IsTeleporting())
			{
				return false;
			}
			if (Console.IsVisible() || (Object.op_Implicit((Object)(object)Chat.instance) && Chat.instance.HasFocus()) || TextInput.IsVisible() || InventoryGui.IsVisible() || Menu.IsVisible() || StoreGui.IsVisible() || (Object.op_Implicit((Object)(object)TextViewer.instance) && TextViewer.instance.IsVisible()))
			{
				return false;
			}
			object? value = RightItem.GetValue(localPlayer);
			ItemData val = (ItemData)((value is ItemData) ? value : null);
			if (val?.m_shared != null && val.m_shared.m_name == "$item_wispshovel")
			{
				return val.m_equipped;
			}
			return false;
		}

		private static bool Down(KeyboardShortcut shortcut)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			if ((int)((KeyboardShortcut)(ref shortcut)).MainKey == 0 || !ZInput.GetKeyDown(((KeyboardShortcut)(ref shortcut)).MainKey, false))
			{
				return false;
			}
			foreach (KeyCode modifier in ((KeyboardShortcut)(ref shortcut)).Modifiers)
			{
				if (!ZInput.GetKey(modifier, false))
				{
					return false;
				}
			}
			return true;
		}

		private void Update()
		{
			//IL_0028: 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)
			if (IsControllable && config != null)
			{
				SyncSelection();
				ZInput.GetMouseScrollWheel();
				if (Down(config.RectangleSideKey.Value))
				{
					State.ToggleRectangleSide();
				}
				else if (Down(config.ShapeKey.Value))
				{
					State.CycleShape();
				}
				NativeShovelMenu.UpdateCosts(State.AreaFacing(0.0, 1.0), config.RaiseStoneCost.Value);
				Piece val = (Object.op_Implicit((Object)(object)NativeShovelMenu.Table) ? NativeShovelMenu.Table.GetSelectedPiece() : null);
				if (Object.op_Implicit((Object)(object)val))
				{
					NativeShovelMenu.UpdateDescription(val, config.ShowToolHud.Value ? Description() : "");
				}
			}
		}

		private static void ScrollPostfix(ref float __result)
		{
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			ShovelControls shovelControls = active;
			if (shovelControls != null && Object.op_Implicit((Object)(object)shovelControls) && shovelControls.IsControllable && shovelControls.config != null && __result != 0f && ZInput.GetKey(shovelControls.config.SizeModifier.Value, false))
			{
				if (shovelControls.scrollFrame != Time.frameCount)
				{
					shovelControls.scrollFrame = Time.frameCount;
					int steps = ((__result > 0f) ? 1 : (-1));
					shovelControls.State.AdjustSize(steps);
					shovelControls.SaveState();
				}
				__result = 0f;
			}
		}

		private static void AvailablePrefix(PieceTable __instance, ref bool __3)
		{
			if ((Object)(object)__instance == (Object)(object)NativeShovelMenu.Table)
			{
				__3 = true;
			}
		}

		private static bool IsShovel(Player player)
		{
			if (Object.op_Implicit((Object)(object)player) && (Object)(object)player == (Object)(object)Player.m_localPlayer && (Object)(object)player.GetBuildTool() == (Object)(object)NativeShovelMenu.Table)
			{
				return Object.op_Implicit((Object)(object)NativeShovelMenu.Table);
			}
			return false;
		}

		private static bool PlacementPrefix(Player __instance, bool __0)
		{
			if (!IsShovel(__instance))
			{
				return true;
			}
			if (__0 && Object.op_Implicit((Object)(object)Hud.instance))
			{
				BuildInput.Invoke(__instance, null);
			}
			return false;
		}

		private static bool GhostPrefix(Player __instance)
		{
			if (!IsShovel(__instance))
			{
				return true;
			}
			object? value = PlacementGhost.GetValue(__instance);
			GameObject val = (GameObject)((value is GameObject) ? value : null);
			if (Object.op_Implicit((Object)(object)val))
			{
				Object.Destroy((Object)(object)val);
			}
			PlacementGhost.SetValue(__instance, null);
			return false;
		}

		private static void SelectionPostfix(Player __instance)
		{
			if (IsShovel(__instance) && Object.op_Implicit((Object)(object)active))
			{
				active.SyncSelection();
			}
		}

		private void SyncSelection()
		{
			Piece val = (Object.op_Implicit((Object)(object)NativeShovelMenu.Table) ? NativeShovelMenu.Table.GetSelectedPiece() : null);
			if (Object.op_Implicit((Object)(object)val) && NativeShovelMenu.TryGetMode(val, out var mode))
			{
				State.SelectMode(mode);
			}
		}

		private void LateUpdate()
		{
			//IL_0127: Unknown result type (might be due to invalid IL or missing references)
			//IL_015c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0193: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a5: 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_00b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
			Hud instance = Hud.instance;
			if (!IsControllable || config == null || !config.ShowToolHud.Value || !Object.op_Implicit((Object)(object)instance) || !instance.m_buildHud.activeInHierarchy)
			{
				if (Object.op_Implicit((Object)(object)controlHints))
				{
					((Component)controlHints).gameObject.SetActive(false);
				}
				return;
			}
			if (!Object.op_Implicit((Object)(object)controlHints))
			{
				TMP_Text pieceDescription = instance.m_pieceDescription;
				controlHints = Object.Instantiate<TMP_Text>(pieceDescription, pieceDescription.transform.parent, false);
				((Object)controlHints).name = "WispShovelControlHints";
				RectTransform rectTransform = controlHints.rectTransform;
				rectTransform.anchoredPosition += new Vector2(535f, -120f);
				rectTransform.sizeDelta = new Vector2(300f, 160f);
				controlHints.alignment = (TextAlignmentOptions)257;
			}
			((Component)controlHints).gameObject.SetActive(true);
			controlHints.text = TranslationFiles.Text("wispshovel_shape") + ": <color=yellow>" + Shortcut(config.ShapeKey.Value) + "</color>\n" + TranslationFiles.Text("wispshovel_side") + ": <color=yellow>" + Shortcut(config.RectangleSideKey.Value) + "</color>\n" + TranslationFiles.Text("wispshovel_size") + ": <color=yellow>" + KeyName(config.SizeModifier.Value) + " + " + TranslationFiles.Text("wispshovel_wheel") + "</color>";
			if (State.Mode == ToolMode.Level)
			{
				TMP_Text? obj = controlHints;