Decompiled source of AdvancedTerrainModifiers v1.6.1

TerrainTools.dll

Decompiled 2 weeks ago
using System;
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Drawing;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Resources;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Text.RegularExpressions;
using AdvancedHeightMap.API;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Jotunn;
using Jotunn.Configs;
using Jotunn.Entities;
using Jotunn.Extensions;
using Jotunn.Managers;
using Jotunn.Utils;
using LocalizationLoader;
using Logging;
using Microsoft.CodeAnalysis;
using SimpleJson;
using TerrainTools.Core;
using TerrainTools.Extensions;
using TerrainTools.Tools;
using TerrainTools.Visualization;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("TerrainTools")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("TerrainTools")]
[assembly: AssemblyCopyright("Copyright ©  2023")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("e3243d22-4307-4008-ba36-9f326008cde5")]
[assembly: AssemblyFileVersion("1.6.1")]
[assembly: TargetFramework(".NETFramework,Version=v4.8.1", FrameworkDisplayName = ".NET Framework 4.8.1")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.6.1.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace AdvancedHeightMap.API
{
	public static class AdvanedHeightMapInfo
	{
		public const string PluginGUID = "Searica.Valheim.AdvancedHeightMap";
	}
	public static class AdvancedHeightMapAPI
	{
		private const string ReceiverTypeName = "AdvancedHeightMap.API.APIReceiver";

		private const string ReceiverAssemblyName = "AdvancedHeightMap";

		private static readonly Type APIReceiver;

		private static readonly List<string> Unbound;

		private static readonly Func<int> getApiVersion;

		private static readonly Func<float> getMaxHeightChange;

		private static readonly Func<float> getMinHeightChange;

		public static bool IsAvailable => APIReceiver != null;

		public static int ReceiverApiVersion
		{
			get
			{
				if (getApiVersion == null)
				{
					return 0;
				}
				return getApiVersion();
			}
		}

		static AdvancedHeightMapAPI()
		{
			Unbound = new List<string>();
			APIReceiver = FindReceiver();
			if (!(APIReceiver == null))
			{
				getApiVersion = Bind<Func<int>>("GetApiVersion");
				getMaxHeightChange = Bind<Func<float>>("GetMaxHeightChange");
				getMinHeightChange = Bind<Func<float>>("GetMinHeightChange");
			}
		}

		private static Type FindReceiver()
		{
			Type type = null;
			try
			{
				type = Type.GetType("AdvancedHeightMap.API.APIReceiver, AdvancedHeightMap", throwOnError: false);
			}
			catch (Exception)
			{
			}
			if (type != null)
			{
				return type;
			}
			Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
			foreach (Assembly assembly in assemblies)
			{
				if (!(assembly.GetName().Name != "AdvancedHeightMap"))
				{
					type = assembly.GetType("AdvancedHeightMap.API.APIReceiver", throwOnError: false);
					if (type != null)
					{
						return type;
					}
				}
			}
			return null;
		}

		private static T Bind<T>(string name) where T : class
		{
			MethodInfo method = typeof(T).GetMethod("Invoke");
			ParameterInfo[] parameters = method.GetParameters();
			Type[] array = new Type[parameters.Length];
			for (int i = 0; i < parameters.Length; i++)
			{
				array[i] = parameters[i].ParameterType;
			}
			MethodInfo method2 = APIReceiver.GetMethod(name, BindingFlags.Static | BindingFlags.Public, null, array, null);
			T obj = ((method2 != null && method2.ReturnType == method.ReturnType) ? (Delegate.CreateDelegate(typeof(T), method2, throwOnBindFailure: false) as T) : null);
			if (obj == null)
			{
				Unbound.Add(name);
			}
			return obj;
		}

		public static float GetMaxHeightChange()
		{
			if (getMaxHeightChange == null)
			{
				return 8f;
			}
			return getMaxHeightChange();
		}

		public static float GetMinHeightChange()
		{
			if (getMinHeightChange == null)
			{
				return -8f;
			}
			return getMinHeightChange();
		}
	}
}
namespace Logging
{
	internal static class Log
	{
		internal enum InfoLevel
		{
			Low,
			Medium,
			High
		}

		private static ManualLogSource logSource;

		internal static ConfigEntry<InfoLevel> Verbosity { get; set; }

		internal static InfoLevel VerbosityLevel => Verbosity.Value;

		internal static bool IsVerbosityLow => Verbosity.Value >= InfoLevel.Low;

		internal static bool IsVerbosityMedium => Verbosity.Value >= InfoLevel.Medium;

		internal static bool IsVerbosityHigh => Verbosity.Value >= InfoLevel.High;

		internal static void Init(ManualLogSource logSource)
		{
			Log.logSource = logSource;
		}

		internal static void LogDebug(object data)
		{
			logSource.LogDebug(data);
		}

		internal static void LogError(object data)
		{
			logSource.LogError(data);
		}

		internal static void LogFatal(object data)
		{
			logSource.LogFatal(data);
		}

		internal static void LogMessage(object data)
		{
			logSource.LogMessage(data);
		}

		internal static void LogWarning(object data)
		{
			logSource.LogWarning(data);
		}

		internal static void LogInfo(object data, InfoLevel level = InfoLevel.Low)
		{
			if (Verbosity == null || VerbosityLevel >= level)
			{
				logSource.LogInfo(data);
			}
		}

		internal static void LogGameObject(GameObject prefab, bool includeChildren = false)
		{
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Expected O, but got Unknown
			LogInfo("***** " + ((Object)prefab).name + " *****");
			Component[] components = prefab.GetComponents<Component>();
			for (int i = 0; i < components.Length; i++)
			{
				LogComponent(components[i]);
			}
			if (!includeChildren)
			{
				return;
			}
			LogInfo("***** " + ((Object)prefab).name + " (children) *****");
			foreach (Transform item in prefab.transform)
			{
				Transform val = item;
				if (Object.op_Implicit((Object)(object)val))
				{
					LogInfo(" - " + ((Object)val).name);
					components = ((Component)val).GetComponents<Component>();
					for (int i = 0; i < components.Length; i++)
					{
						LogComponent(components[i]);
					}
				}
			}
		}

		internal static void LogComponent(Component compo)
		{
			if (!Object.op_Implicit((Object)(object)compo))
			{
				return;
			}
			try
			{
				LogInfo("--- " + ((object)compo).GetType().Name + ": " + ((Object)compo).name + " ---");
			}
			catch (Exception ex)
			{
				LogError(ex.ToString());
				LogWarning("Could not get type name for component!");
				return;
			}
			try
			{
				foreach (PropertyInfo declaredProperty in AccessTools.GetDeclaredProperties(((object)compo).GetType()))
				{
					try
					{
						LogInfo($" - {declaredProperty.Name} = {declaredProperty.GetValue(compo)}");
					}
					catch (Exception ex2)
					{
						LogError(ex2.ToString());
						LogWarning("Could not get property: " + declaredProperty.Name + " for component!");
					}
				}
			}
			catch (Exception ex3)
			{
				LogError(ex3.ToString());
				LogWarning("Could not get properties for component!");
			}
			try
			{
				foreach (FieldInfo declaredField in AccessTools.GetDeclaredFields(((object)compo).GetType()))
				{
					try
					{
						LogInfo($" - {declaredField.Name} = {declaredField.GetValue(compo)}");
					}
					catch (Exception ex4)
					{
						LogError(ex4.ToString());
						LogWarning("Could not get field: " + declaredField.Name + " for component!");
					}
				}
			}
			catch (Exception ex5)
			{
				LogError(ex5.ToString());
				LogWarning("Could not get fields for component!");
			}
		}
	}
}
namespace LocalizationLoader
{
	internal static class LocalizationLoader
	{
		internal const string LocalizationFolder = "Localizations";

		private static readonly Regex CommentLine = new Regex("^\\s*//.*$", RegexOptions.Multiline);

		internal static void AddLocalizations(BaseUnityPlugin plugin, string pluginID, CustomLocalization localization)
		{
			string text = Path.Combine(Directory.GetParent(plugin.Config.ConfigFilePath).FullName, pluginID, "Localizations");
			Directory.CreateDirectory(text);
			Log.LogInfo("Loading Localizations.");
			string[] manifestResourceNames = ((object)plugin).GetType().Assembly.GetManifestResourceNames();
			foreach (string text2 in manifestResourceNames)
			{
				if (!text2.Contains("Localizations"))
				{
					continue;
				}
				string text3 = LanguageNameFromResource(text2);
				if (text3 == null)
				{
					Log.LogWarning("Could not determine a language name for '" + text2 + "', skipping it.");
					continue;
				}
				try
				{
					AddLocalization(plugin, localization, text2, text3, Path.Combine(text, text3 + ".json"));
				}
				catch (Exception ex)
				{
					Log.LogError("Failed to load localization '" + text3 + "': " + ex.Message);
				}
			}
		}

		private static void AddLocalization(BaseUnityPlugin plugin, CustomLocalization Localization, string embeddedResource, string language, string translationFile)
		{
			Dictionary<string, string> dictionary = SimpleJson.DeserializeObject<Dictionary<string, string>>(CommentLine.Replace(ReadEmbeddedResourceFile(plugin, embeddedResource), ""));
			List<string> token_order = dictionary.Keys.ToList();
			Dictionary<string, string> dictionary2 = dictionary;
			bool flag = true;
			if (File.Exists(translationFile))
			{
				try
				{
					dictionary2 = SimpleJson.DeserializeObject<Dictionary<string, string>>(File.ReadAllText(translationFile));
					flag = UpdateLocalizationWithMissingKeys(dictionary, dictionary2);
				}
				catch
				{
					Log.LogWarning("'" + translationFile + "' could not be parsed, resetting it to the built in " + language + " localization.");
					dictionary2 = dictionary;
					flag = true;
				}
			}
			string text = SerializePretty(dictionary2, token_order);
			if (flag)
			{
				File.WriteAllText(translationFile, text, new UTF8Encoding(encoderShouldEmitUTF8Identifier: false));
				Log.LogDebug("Wrote " + translationFile);
			}
			Localization.AddJsonFile(language, text);
			Log.LogDebug("Added localization: '" + language + "'");
		}

		private static bool UpdateLocalizationWithMissingKeys(Dictionary<string, string> internal_localization, Dictionary<string, string> cached_localization)
		{
			bool result = false;
			List<string> list = cached_localization.Keys.Where((string key) => !internal_localization.ContainsKey(key)).ToList();
			if (list.Count > 0)
			{
				Log.LogDebug("Removing extra keys " + string.Join(",", list) + ".");
				foreach (string item in list)
				{
					cached_localization.Remove(item);
				}
				result = true;
			}
			foreach (KeyValuePair<string, string> item2 in internal_localization)
			{
				if (!cached_localization.ContainsKey(item2.Key))
				{
					Log.LogDebug("Adding missing localization key " + item2.Key);
					cached_localization.Add(item2.Key, item2.Value);
					result = true;
				}
			}
			return result;
		}

		private static string SerializePretty(Dictionary<string, string> localization, List<string> token_order)
		{
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.Append("{\n");
			for (int i = 0; i < token_order.Count; i++)
			{
				string text = token_order[i];
				stringBuilder.Append("  \"").Append(EscapeJsonString(text)).Append("\": \"")
					.Append(EscapeJsonString(localization[text]))
					.Append('"');
				if (i < token_order.Count - 1)
				{
					stringBuilder.Append(',');
				}
				stringBuilder.Append('\n');
			}
			stringBuilder.Append("}\n");
			return stringBuilder.ToString();
		}

		private static string EscapeJsonString(string value)
		{
			StringBuilder stringBuilder = new StringBuilder(value.Length);
			foreach (char c in value)
			{
				switch (c)
				{
				case '"':
					stringBuilder.Append("\\\"");
					continue;
				case '\\':
					stringBuilder.Append("\\\\");
					continue;
				case '\b':
					stringBuilder.Append("\\b");
					continue;
				case '\f':
					stringBuilder.Append("\\f");
					continue;
				case '\n':
					stringBuilder.Append("\\n");
					continue;
				case '\r':
					stringBuilder.Append("\\r");
					continue;
				case '\t':
					stringBuilder.Append("\\t");
					continue;
				}
				if (c < ' ')
				{
					StringBuilder stringBuilder2 = stringBuilder.Append("\\u");
					int num = c;
					stringBuilder2.Append(num.ToString("x4"));
				}
				else
				{
					stringBuilder.Append(c);
				}
			}
			return stringBuilder.ToString();
		}

		private static string LanguageNameFromResource(string embeddedResource)
		{
			string[] array = embeddedResource.Split(new char[1] { '.' });
			int num = Array.IndexOf(array, "Localizations");
			if (num < 0 || num + 2 > array.Length - 1)
			{
				return null;
			}
			return array[num + 1];
		}

		private static string ReadEmbeddedResourceFile(BaseUnityPlugin plugin, string filename)
		{
			using Stream stream = ((object)plugin).GetType().Assembly.GetManifestResourceStream(filename);
			using StreamReader streamReader = new StreamReader(stream);
			return streamReader.ReadToEnd();
		}
	}
}
namespace TerrainTools
{
	[BepInPlugin("Searica.Valheim.TerrainTools", "AdvancedTerrainModifiers", "1.6.1")]
	[BepInDependency("com.jotunn.jotunn", "2.30.2")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[NetworkCompatibility(/*Could not decode attribute arguments.*/)]
	[SynchronizationMode(/*Could not decode attribute arguments.*/)]
	internal sealed class TerrainTools : BaseUnityPlugin
	{
		internal const string Author = "Searica";

		public const string PluginName = "AdvancedTerrainModifiers";

		public const string PluginGUID = "Searica.Valheim.TerrainTools";

		public const string PluginVersion = "1.6.1";

		public static TerrainTools Instance;

		private static ConfigFileWatcher ConfigFileWatcher;

		private const string MainSection = "Global";

		private const string RadiusSection = "Radius";

		private const string SharpnessSection = "Sharpness";

		private const string ShovelSection = "Shovel";

		private const string HoeSection = "Hoe";

		private const string CultivatorSection = "Cultivator";

		internal bool UpdatePlugin;

		private ConfigEntry<bool> hoverInfoEnabled;

		private readonly Dictionary<string, ConfigEntry<bool>> ToolConfigEntries = new Dictionary<string, ConfigEntry<bool>>();

		private ConfigEntry<bool> enableRadiusModifier;

		private ConfigEntry<KeyCode> radiusModKey;

		private ConfigEntry<float> radiusScrollScale;

		private ConfigEntry<float> maxRadius;

		private ConfigEntry<bool> enableSharpnessModifier;

		private ConfigEntry<KeyCode> hardnessModKey;

		private ConfigEntry<float> hardnessScrollScale;

		private ConfigEntry<bool> enableShovel;

		private static CustomLocalization Localization;

		internal bool IsHoverInforEnabled => hoverInfoEnabled.Value;

		internal bool IsEnableRadiusModifier => enableRadiusModifier.Value;

		internal float MaxRadius => maxRadius.Value;

		internal KeyCode RadiusKey => radiusModKey.Value;

		internal float RadiusScrollScale => radiusScrollScale.Value;

		internal bool IsEnableSharpnessModifier => enableSharpnessModifier.Value;

		internal KeyCode SharpnessKey => hardnessModKey.Value;

		internal float SharpnessScrollScale => hardnessScrollScale.Value;

		internal bool IsShovelEnabled => enableShovel.Value;

		private static string GetSectionName(string pieceTable)
		{
			if (pieceTable == PieceTables.Hoe)
			{
				return "Hoe";
			}
			if (pieceTable == PieceTables.Cultivator)
			{
				return "Cultivator";
			}
			if (pieceTable == "_ShovelPieceTable")
			{
				return "Shovel";
			}
			return "Global";
		}

		internal bool IsToolEnabled(string toolName)
		{
			if (ToolConfigEntries.TryGetValue(toolName, out var value))
			{
				return value?.Value ?? false;
			}
			return false;
		}

		public void Awake()
		{
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cf: Expected O, but got Unknown
			Instance = this;
			Log.Init(((BaseUnityPlugin)this).Logger);
			Localization = LocalizationManager.Instance.GetLocalization();
			global::LocalizationLoader.LocalizationLoader.AddLocalizations((BaseUnityPlugin)(object)Instance, "AdvancedTerrainModifiers", Localization);
			((BaseUnityPlugin)this).Config.DisableSaveOnConfigSet();
			SetUpConfigEntries();
			((BaseUnityPlugin)this).Config.Save();
			((BaseUnityPlugin)this).Config.SaveOnConfigSet = true;
			Harmony.CreateAndPatchAll(Assembly.GetExecutingAssembly(), "Searica.Valheim.TerrainTools");
			Game.isModded = true;
			PrefabManager.OnVanillaPrefabsAvailable += Shovel.CreateShovel;
			PieceManager.OnPiecesRegistered += InitManager.InitToolPieces;
			_ = GUIManager.Instance;
			ConfigFileWatcher = new ConfigFileWatcher(((BaseUnityPlugin)this).Config, 1000L);
			ConfigFileWatcher.OnConfigFileReloaded += delegate
			{
				if (UpdatePlugin)
				{
					InitManager.UpdatePlugin(shouldSave: false);
				}
			};
			SynchronizationManager.OnConfigurationWindowClosed += delegate
			{
				if (UpdatePlugin)
				{
					InitManager.UpdatePlugin();
				}
			};
			SynchronizationManager.OnConfigurationSynchronized += delegate
			{
				if (UpdatePlugin)
				{
					InitManager.UpdatePlugin();
				}
			};
		}

		public void OnDestroy()
		{
			((BaseUnityPlugin)this).Config.Save();
		}

		internal void SetUpConfigEntries()
		{
			Log.Verbosity = ConfigFileExtensions.BindConfigInOrder<Log.InfoLevel>(((BaseUnityPlugin)this).Config, "Global", "Verbosity", Log.InfoLevel.Low, "Low will log basic information about the mod. Medium will log information that is useful for troubleshooting. High will log a lot of information, do not set it to this without good reason as it will slow Down your game.", false, true, true, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			hoverInfoEnabled = ConfigFileExtensions.BindConfigInOrder<bool>(((BaseUnityPlugin)this).Config, "Global", "HoverInfo", true, "Set to true/enabled to show terrain height when using square terrain tools.", true, true, true, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			enableRadiusModifier = ConfigFileExtensions.BindConfigInOrder<bool>(((BaseUnityPlugin)this).Config, "Radius", "Radius Modifier", true, "Set to true/enabled to allow modifying the radius of terrain tools using the scroll wheel. Note: Radius cannot be changed on square terraforming tools.", true, true, true, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			radiusModKey = ConfigFileExtensions.BindConfigInOrder<KeyCode>(((BaseUnityPlugin)this).Config, "Radius", "Adjust Radius Key", (KeyCode)304, "Modifier key that must be held down when using scroll wheel to change the radius of terrain tools.", false, true, true, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			radiusScrollScale = ConfigFileExtensions.BindConfigInOrder<float>(((BaseUnityPlugin)this).Config, "Radius", "Radius Scroll Speed", 0.1f, "How much each tick of movement from the scroll wheel will change radius size. Larger magnitude means the radius will faster. Negative numbers will reverse the scroll direction to adjust the radius.", false, true, true, (AcceptableValueBase)(object)new AcceptableValueRange<float>(-1f, 1f), (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			maxRadius = ConfigFileExtensions.BindConfigInOrder<float>(((BaseUnityPlugin)this).Config, "Radius", "Max Radius", 10f, "Maximum radius of terrain tools.", true, true, true, (AcceptableValueBase)(object)new AcceptableValueRange<float>(4f, 20f), (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			enableSharpnessModifier = ConfigFileExtensions.BindConfigInOrder<bool>(((BaseUnityPlugin)this).Config, "Sharpness", "Sharpness Modifier", true, "Set to true/enabled to allow modifying the hardness of terrain tools using the scroll wheel. Note: Sharpness cannot be changed on square terraforming tools and tools that do not alter ground height do not have a hardness.", true, true, true, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			hardnessModKey = ConfigFileExtensions.BindConfigInOrder<KeyCode>(((BaseUnityPlugin)this).Config, "Sharpness", "Adjust Sharpness Key", (KeyCode)308, "Modifier key that must be held down when using scroll wheel to change the hardness of terrain tools.", false, true, true, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			hardnessScrollScale = ConfigFileExtensions.BindConfigInOrder<float>(((BaseUnityPlugin)this).Config, "Sharpness", "Sharpness Scroll Speed", 0.3f, "How much each tick of movement from the scroll wheel will change sharpness. Larger magnitude means the sharpness will faster. Negative numbers will reverse the scroll direction to adjust the sharpness.", false, true, true, (AcceptableValueBase)(object)new AcceptableValueRange<float>(-1f, 1f), (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			enableShovel = ConfigFileExtensions.BindConfigInOrder<bool>(((BaseUnityPlugin)this).Config, "Shovel", "Shovel", true, "Whether the shovel is craftable in-game. If Enabled then crafting is allowed. If Disabled then crafting new shovels is prevented but existing shovels are untouched.", true, true, true, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			enableShovel.SettingChanged += SetUpdatePlugin;
			foreach (string key in ToolConfigs.ToolConfigsMap.Keys)
			{
				ToolDB toolDB = ToolConfigs.ToolConfigsMap[key];
				ConfigEntry<bool> val = ConfigFileExtensions.BindConfigInOrder<bool>(((BaseUnityPlugin)this).Config, GetSectionName(toolDB.pieceTable), key, true, "Set to true/enabled to add this terrain tool. Set to false/disabled to remove it.", true, true, true, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
				val.SettingChanged += SetUpdatePlugin;
				ToolConfigEntries.Add(key, val);
			}
		}

		private void SetUpdatePlugin(object obj, EventArgs e)
		{
			UpdatePlugin = !UpdatePlugin || UpdatePlugin;
		}
	}
}
namespace TerrainTools.Visualization
{
	public class HoverInfo
	{
		private readonly GameObject _gameObject;

		private readonly Transform _transform;

		private readonly TextMesh _textMesh;

		public string Text
		{
			get
			{
				return _textMesh.text;
			}
			set
			{
				_textMesh.text = value;
			}
		}

		public bool Enabled
		{
			get
			{
				return _gameObject.activeSelf;
			}
			set
			{
				_gameObject.SetActive(value);
			}
		}

		public Color Color
		{
			get
			{
				//IL_0006: Unknown result type (might be due to invalid IL or missing references)
				return _textMesh.color;
			}
			set
			{
				//IL_0006: Unknown result type (might be due to invalid IL or missing references)
				_textMesh.color = value;
			}
		}

		public HoverInfo(Transform parentTransform)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Expected O, but got Unknown
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			_gameObject = new GameObject();
			_gameObject.transform.parent = parentTransform;
			_transform = _gameObject.transform;
			_textMesh = _gameObject.AddComponent<TextMesh>();
			((Component)_textMesh).transform.localPosition = Vector3.zero;
			((Component)_textMesh).transform.localScale = new Vector3(0.1f / parentTransform.localScale.x, 0.1f / parentTransform.localScale.y, 0.1f / parentTransform.localScale.z);
			_textMesh.anchor = (TextAnchor)4;
			_textMesh.alignment = (TextAlignment)1;
			_textMesh.fontSize = 16;
		}

		public void RotateToPlayer()
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			Vector3 val = default(Vector3);
			((Vector3)(ref val))..ctor(((Component)GameCamera.m_instance).transform.position.x, _transform.position.y, ((Component)GameCamera.m_instance).transform.position.z);
			_transform.LookAt(val, Vector3.up);
			_transform.Rotate(90f, 180f, 0f);
		}
	}
	internal static class IconCache
	{
		private static Texture2D _remove;

		private static Texture2D _cross;

		private static Texture2D _undo;

		private static Texture2D _redo;

		private static Texture2D _box;

		private static Texture2D _pavedRoadSquare;

		private static Texture2D _pavedRoadPath;

		private static Texture2D _pavedRoadPathSquare;

		private static Texture2D _mudRoadSquare;

		private static Texture2D _mudRoadPathSquare;

		private static Texture2D _replantSquare;

		private static Texture2D _cultivateSquare;

		private static Texture2D _cultivatePath;

		private static Texture2D _cultivatePathSquare;

		private static Texture2D _raiseSquare;

		private static Texture2D _lower;

		private static Texture2D _shovel;

		private static readonly MethodInfo LoadImageMethod = AccessTools.Method(typeof(ImageConversion), "LoadImage", new Type[2]
		{
			typeof(Texture2D),
			typeof(byte[])
		}, (Type[])null);

		internal static Texture2D Shovel
		{
			get
			{
				if ((Object)(object)_shovel == (Object)null)
				{
					_shovel = LoadTextureFromResources("ShovelIcon.png");
				}
				return _shovel;
			}
		}

		internal static Texture2D Lower
		{
			get
			{
				if ((Object)(object)_lower == (Object)null)
				{
					_lower = LoadTextureFromResources("lower_v2.png");
				}
				return _lower;
			}
		}

		internal static Texture2D CultivateSquare
		{
			get
			{
				if ((Object)(object)_cultivateSquare == (Object)null)
				{
					_cultivateSquare = LoadTextureFromResources("cultivate_v2_square.png");
				}
				return _cultivateSquare;
			}
		}

		internal static Texture2D CultivatePathSquare
		{
			get
			{
				if ((Object)(object)_cultivatePathSquare == (Object)null)
				{
					_cultivatePathSquare = LoadTextureFromResources("cultivate_v2_path_square.png");
				}
				return _cultivatePathSquare;
			}
		}

		internal static Texture2D CultivatePath
		{
			get
			{
				if ((Object)(object)_cultivatePath == (Object)null)
				{
					_cultivatePath = LoadTextureFromResources("cultivate_v2_path.png");
				}
				return _cultivatePath;
			}
		}

		internal static Texture2D ReplantSquare
		{
			get
			{
				if ((Object)(object)_replantSquare == (Object)null)
				{
					_replantSquare = LoadTextureFromResources("replant_v2_square.png");
				}
				return _replantSquare;
			}
		}

		internal static Texture2D RaiseSquare
		{
			get
			{
				if ((Object)(object)_raiseSquare == (Object)null)
				{
					_raiseSquare = LoadTextureFromResources("raise_v2_square.png");
				}
				return _raiseSquare;
			}
		}

		internal static Texture2D MudRoadPathSquare
		{
			get
			{
				if ((Object)(object)_mudRoadPathSquare == (Object)null)
				{
					_mudRoadPathSquare = LoadTextureFromResources("path_v2_square.png");
				}
				return _mudRoadPathSquare;
			}
		}

		internal static Texture2D MudRoadSquare
		{
			get
			{
				if ((Object)(object)_mudRoadSquare == (Object)null)
				{
					_mudRoadSquare = LoadTextureFromResources("mud_road_v2_square.png");
				}
				return _mudRoadSquare;
			}
		}

		internal static Texture2D PavedRoadPath
		{
			get
			{
				if ((Object)(object)_pavedRoadPath == (Object)null)
				{
					_pavedRoadPath = LoadTextureFromResources("paved_road_v2_path.png");
				}
				return _pavedRoadPath;
			}
		}

		internal static Texture2D PavedRoadPathSquare
		{
			get
			{
				if ((Object)(object)_pavedRoadPathSquare == (Object)null)
				{
					_pavedRoadPathSquare = LoadTextureFromResources("paved_road_v2_path_square.png");
				}
				return _pavedRoadPathSquare;
			}
		}

		internal static Texture2D PavedRoadSquare
		{
			get
			{
				if ((Object)(object)_pavedRoadSquare == (Object)null)
				{
					_pavedRoadSquare = LoadTextureFromResources("paved_road_v2_square.png");
				}
				return _pavedRoadSquare;
			}
		}

		internal static Texture2D Remove
		{
			get
			{
				if ((Object)(object)_remove == (Object)null)
				{
					_remove = LoadTextureFromResources("remove.png");
				}
				return _remove;
			}
		}

		internal static Texture2D Cross
		{
			get
			{
				if ((Object)(object)_cross == (Object)null)
				{
					_cross = LoadTextureFromResources("cross.png");
				}
				return _cross;
			}
		}

		internal static Texture2D Undo
		{
			get
			{
				if ((Object)(object)_undo == (Object)null)
				{
					_undo = LoadTextureFromResources("undo.png");
				}
				return _undo;
			}
		}

		internal static Texture2D Redo
		{
			get
			{
				if ((Object)(object)_redo == (Object)null)
				{
					_redo = LoadTextureFromResources("redo.png");
				}
				return _redo;
			}
		}

		internal static Texture2D Box
		{
			get
			{
				if ((Object)(object)_box == (Object)null)
				{
					_box = LoadTextureFromResources("box.png");
				}
				return _box;
			}
		}

		internal static Sprite LoadEmbeddedTextureAsSprite(string fileName)
		{
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			Texture2D val = LoadTextureFromResources(fileName);
			if ((Object)(object)val == (Object)null)
			{
				return null;
			}
			Vector2 val2 = default(Vector2);
			((Vector2)(ref val2))..ctor(0.5f, 0.5f);
			float num = 100f;
			return Sprite.Create(val, new Rect(0f, 0f, (float)((Texture)val).width, (float)((Texture)val).height), val2, num);
		}

		internal static Texture2D LoadTextureFromResources(string fileName)
		{
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a2: Expected O, but got Unknown
			string text = Path.GetExtension(fileName).ToLower();
			if (text != ".png" && text != ".jpg")
			{
				Log.LogWarning("LoadTextureFromResources can only load png or jpg textures");
				return null;
			}
			fileName = Path.GetFileNameWithoutExtension(fileName);
			string name = (from text2 in Assembly.GetExecutingAssembly().GetManifestResourceNames()
				where text2.Contains(fileName)
				select text2).FirstOrDefault();
			Stream? manifestResourceStream = Assembly.GetExecutingAssembly().GetManifestResourceStream(name);
			byte[] array = new byte[manifestResourceStream.Length];
			manifestResourceStream.Read(array, 0, array.Length);
			Texture2D val = new Texture2D(0, 0);
			LoadImageMethod.Invoke(null, new object[2] { val, array });
			return val;
		}
	}
	public class Overlay
	{
		private GameObject GameObject { get; }

		private Particle[] Particles => (Particle[])(object)new Particle[2];

		private Transform Transform { get; }

		public ParticleSystem ps { get; }

		public ParticleSystemRenderer psr { get; }

		public MainModule psm { get; }

		public bool Enabled
		{
			get
			{
				return GameObject.activeSelf;
			}
			set
			{
				GameObject.SetActive(value);
			}
		}

		public Vector3 Position
		{
			get
			{
				//IL_0006: Unknown result type (might be due to invalid IL or missing references)
				return Transform.position;
			}
			set
			{
				//IL_0006: Unknown result type (might be due to invalid IL or missing references)
				Transform.position = value;
			}
		}

		public Vector3 LocalPosition
		{
			get
			{
				//IL_0006: Unknown result type (might be due to invalid IL or missing references)
				return Transform.localPosition;
			}
			set
			{
				//IL_0006: Unknown result type (might be due to invalid IL or missing references)
				Transform.localPosition = value;
			}
		}

		public Quaternion Rotation
		{
			get
			{
				//IL_0006: Unknown result type (might be due to invalid IL or missing references)
				return Transform.rotation;
			}
			set
			{
				//IL_0006: Unknown result type (might be due to invalid IL or missing references)
				Transform.rotation = value;
			}
		}

		public Color Color
		{
			get
			{
				//IL_0025: Unknown result type (might be due to invalid IL or missing references)
				//IL_002a: Unknown result type (might be due to invalid IL or missing references)
				ps.GetParticles(Particles, 2);
				return Color32.op_Implicit(((Particle)(ref Particles[1])).GetCurrentColor(ps));
			}
		}

		public Color StartColor
		{
			get
			{
				//IL_0001: Unknown result type (might be due to invalid IL or missing references)
				//IL_0006: Unknown result type (might be due to invalid IL or missing references)
				//IL_0009: Unknown result type (might be due to invalid IL or missing references)
				//IL_000e: Unknown result type (might be due to invalid IL or missing references)
				//IL_0011: Unknown result type (might be due to invalid IL or missing references)
				MainModule val = psm;
				MinMaxGradient startColor = ((MainModule)(ref val)).startColor;
				return ((MinMaxGradient)(ref startColor)).color;
			}
			set
			{
				//IL_0001: Unknown result type (might be due to invalid IL or missing references)
				//IL_0006: Unknown result type (might be due to invalid IL or missing references)
				//IL_0009: Unknown result type (might be due to invalid IL or missing references)
				//IL_000e: Unknown result type (might be due to invalid IL or missing references)
				//IL_0011: Unknown result type (might be due to invalid IL or missing references)
				MainModule val = psm;
				MinMaxGradient startColor = ((MainModule)(ref val)).startColor;
				((MinMaxGradient)(ref startColor)).color = value;
			}
		}

		public float StartSize
		{
			get
			{
				//IL_0001: Unknown result type (might be due to invalid IL or missing references)
				//IL_0006: Unknown result type (might be due to invalid IL or missing references)
				//IL_0009: Unknown result type (might be due to invalid IL or missing references)
				//IL_000e: Unknown result type (might be due to invalid IL or missing references)
				MainModule val = psm;
				MinMaxCurve startSize = ((MainModule)(ref val)).startSize;
				return ((MinMaxCurve)(ref startSize)).constant;
			}
			set
			{
				//IL_0006: Unknown result type (might be due to invalid IL or missing references)
				//IL_000b: Unknown result type (might be due to invalid IL or missing references)
				//IL_000f: Unknown result type (might be due to invalid IL or missing references)
				MainModule main = ps.main;
				((MainModule)(ref main)).startSize = MinMaxCurve.op_Implicit(value);
			}
		}

		public float StartSpeed
		{
			get
			{
				//IL_0001: Unknown result type (might be due to invalid IL or missing references)
				//IL_0006: Unknown result type (might be due to invalid IL or missing references)
				//IL_0009: Unknown result type (might be due to invalid IL or missing references)
				//IL_000e: Unknown result type (might be due to invalid IL or missing references)
				MainModule val = psm;
				MinMaxCurve startSpeed = ((MainModule)(ref val)).startSpeed;
				return ((MinMaxCurve)(ref startSpeed)).constant;
			}
			set
			{
				//IL_0006: Unknown result type (might be due to invalid IL or missing references)
				//IL_000b: Unknown result type (might be due to invalid IL or missing references)
				//IL_000f: Unknown result type (might be due to invalid IL or missing references)
				MainModule main = ps.main;
				((MainModule)(ref main)).startSpeed = MinMaxCurve.op_Implicit(value);
			}
		}

		public float StartLifetime
		{
			get
			{
				//IL_0001: Unknown result type (might be due to invalid IL or missing references)
				//IL_0006: Unknown result type (might be due to invalid IL or missing references)
				//IL_0009: Unknown result type (might be due to invalid IL or missing references)
				//IL_000e: Unknown result type (might be due to invalid IL or missing references)
				MainModule val = psm;
				MinMaxCurve startLifetime = ((MainModule)(ref val)).startLifetime;
				return ((MinMaxCurve)(ref startLifetime)).constant;
			}
			set
			{
				//IL_0006: Unknown result type (might be due to invalid IL or missing references)
				//IL_000b: Unknown result type (might be due to invalid IL or missing references)
				//IL_000f: Unknown result type (might be due to invalid IL or missing references)
				MainModule main = ps.main;
				((MainModule)(ref main)).startLifetime = MinMaxCurve.op_Implicit(value);
			}
		}

		public bool SizeOverLifetimeEnabled
		{
			get
			{
				//IL_0006: Unknown result type (might be due to invalid IL or missing references)
				//IL_000b: Unknown result type (might be due to invalid IL or missing references)
				SizeOverLifetimeModule sizeOverLifetime = ps.sizeOverLifetime;
				return ((SizeOverLifetimeModule)(ref sizeOverLifetime)).enabled;
			}
			set
			{
				//IL_0006: Unknown result type (might be due to invalid IL or missing references)
				//IL_000b: Unknown result type (might be due to invalid IL or missing references)
				SizeOverLifetimeModule sizeOverLifetime = ps.sizeOverLifetime;
				((SizeOverLifetimeModule)(ref sizeOverLifetime)).enabled = value;
			}
		}

		public MinMaxCurve SizeOverLifetime
		{
			get
			{
				//IL_0006: Unknown result type (might be due to invalid IL or missing references)
				//IL_000b: Unknown result type (might be due to invalid IL or missing references)
				//IL_000e: Unknown result type (might be due to invalid IL or missing references)
				SizeOverLifetimeModule sizeOverLifetime = ps.sizeOverLifetime;
				return ((SizeOverLifetimeModule)(ref sizeOverLifetime)).size;
			}
			set
			{
				//IL_0006: Unknown result type (might be due to invalid IL or missing references)
				//IL_000b: Unknown result type (might be due to invalid IL or missing references)
				//IL_000e: Unknown result type (might be due to invalid IL or missing references)
				SizeOverLifetimeModule sizeOverLifetime = ps.sizeOverLifetime;
				((SizeOverLifetimeModule)(ref sizeOverLifetime)).size = value;
			}
		}

		public Overlay(Transform transform)
		{
			//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)
			Transform = transform;
			GameObject = ((Component)transform).gameObject;
			ps = ((Component)transform).GetComponentInChildren<ParticleSystem>();
			psr = ((Component)transform).GetComponentInChildren<ParticleSystemRenderer>();
			psm = ps.main;
		}
	}
	public abstract class OverlayVisualizer : MonoBehaviour
	{
		protected Overlay primary;

		protected Overlay secondary;

		protected Overlay tertiary;

		protected HoverInfo hoverInfo;

		internal static readonly Vector3 VerticalOffset = new Vector3(0f, 0.075f, 0f);

		private void Update()
		{
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			if (primary == null)
			{
				Transform val = ((Component)this).transform.Find("_GhostOnly");
				Transform val2 = Object.Instantiate<Transform>(val, ((Component)this).transform);
				Transform transform = Object.Instantiate<Transform>(val2, ((Component)this).transform);
				primary = new Overlay(val);
				secondary = new Overlay(val2);
				tertiary = new Overlay(transform);
				hoverInfo = new HoverInfo(val2);
				tertiary.StartColor = new Color(255f, 255f, 255f);
				primary.Enabled = false;
				secondary.Enabled = false;
				tertiary.Enabled = false;
				Initialize();
			}
			OnRefresh();
		}

		protected abstract void Initialize();

		protected abstract void OnRefresh();

		protected void SpeedUp(Overlay overlay)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Expected O, but got Unknown
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			AnimationCurve val = new AnimationCurve();
			val.AddKey(0f, 0f);
			val.AddKey(0.5f, 1f);
			MinMaxCurve sizeOverLifetime = default(MinMaxCurve);
			((MinMaxCurve)(ref sizeOverLifetime))..ctor(1f, val);
			overlay.StartLifetime = 2f;
			overlay.SizeOverLifetime = sizeOverLifetime;
		}

		protected void Freeze(Overlay overlay)
		{
			overlay.StartSpeed = 0f;
			overlay.SizeOverLifetimeEnabled = false;
		}

		protected void VisualizeTerraformingBounds(Overlay overlay)
		{
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			overlay.StartSize = 3f;
			((Renderer)overlay.psr).material.mainTexture = (Texture)(object)IconCache.Box;
			overlay.LocalPosition = VerticalOffset;
		}

		protected void VisualizeIconInsideTerraformingBounds(Overlay overlay, Texture iconTexture)
		{
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			overlay.StartSize = 2.5f;
			((Renderer)overlay.psr).material.mainTexture = iconTexture;
			overlay.LocalPosition = VerticalOffset;
		}

		protected void VisualizeRecoloringBounds(Overlay overlay)
		{
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			overlay.StartSize = 3f;
			((Renderer)overlay.psr).material.mainTexture = (Texture)(object)IconCache.Box;
			overlay.LocalPosition = VerticalOffset;
		}

		protected void VisualizeIconInsideRecoloringBounds(Overlay overlay, Texture iconTexture)
		{
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			overlay.StartSize = 2.5f;
			((Renderer)overlay.psr).material.mainTexture = iconTexture;
			overlay.Position = ((Component)this).transform.position + VerticalOffset;
		}
	}
	public abstract class HoverInfoEnabled : OverlayVisualizer
	{
		protected override void Initialize()
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			hoverInfo.Color = secondary.StartColor;
		}

		protected override void OnRefresh()
		{
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			hoverInfo.Enabled = TerrainTools.Instance.IsHoverInforEnabled;
			if (hoverInfo.Enabled)
			{
				hoverInfo.RotateToPlayer();
				Vector3 val = secondary.Position - OverlayVisualizer.VerticalOffset;
				hoverInfo.Text = $"x: {val.x:0}, y: {val.y:0.000}, z: {val.z:0}";
			}
		}
	}
	[HarmonyPatch(typeof(Piece), "SetInvalidPlacementHeightlight")]
	public static class OverlayVisualizationRedshiftHeigthlightBlocker
	{
		private static bool Prefix(Piece __instance)
		{
			return !Object.op_Implicit((Object)(object)((Component)__instance).GetComponentInChildren<OverlayVisualizer>());
		}
	}
	public class LevelGroundOverlayVisualizer : HoverInfoEnabled
	{
		protected override void Initialize()
		{
			base.Initialize();
			SpeedUp(secondary);
			VisualizeTerraformingBounds(secondary);
		}

		protected override void OnRefresh()
		{
			base.OnRefresh();
			primary.Enabled = false;
			secondary.Enabled = true;
		}
	}
	public class RaiseGroundOverlayVisualizer : HoverInfoEnabled
	{
		protected override void Initialize()
		{
			base.Initialize();
			Freeze(secondary);
			Freeze(tertiary);
			VisualizeTerraformingBounds(secondary);
			VisualizeTerraformingBounds(tertiary);
		}

		protected override void OnRefresh()
		{
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			base.OnRefresh();
			primary.Enabled = true;
			secondary.Enabled = true;
			GroundLevelSpinner.Refresh();
			secondary.LocalPosition = new Vector3(0f, GroundLevelSpinner.Value, 0f);
			Vector3 val = secondary.Position - OverlayVisualizer.VerticalOffset;
			if (GroundLevelSpinner.Value > 0f)
			{
				float y = secondary.LocalPosition.y;
				hoverInfo.Text = $"x: {val.x:0}, y: {val.y - y:0.000}, z: {val.z:0}\n\nh: +{y:0.000}";
			}
			else
			{
				hoverInfo.Text = $"x: {val.x:0}, y: {val.y:0.000}, z: {val.z:0}";
			}
			tertiary.Enabled = true;
		}
	}
	public class SquarePathOverlayVisualizer : HoverInfoEnabled
	{
		protected override void Initialize()
		{
			base.Initialize();
			SpeedUp(secondary);
			VisualizeRecoloringBounds(secondary);
		}

		protected override void OnRefresh()
		{
			base.OnRefresh();
			primary.Enabled = false;
			secondary.Enabled = true;
		}
	}
	public class CultivateOverlayVisualizer : HoverInfoEnabled
	{
		protected override void Initialize()
		{
			base.Initialize();
			SpeedUp(secondary);
			VisualizeRecoloringBounds(secondary);
		}

		protected override void OnRefresh()
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			base.OnRefresh();
			primary.Enabled = false;
			secondary.Enabled = true;
			hoverInfo.Color = secondary.Color;
		}
	}
	public class SeedGrassOverlayVisualizer : HoverInfoEnabled
	{
		protected override void Initialize()
		{
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			base.Initialize();
			Freeze(secondary);
			VisualizeRecoloringBounds(secondary);
			primary.StartSize = 3f;
			primary.LocalPosition = new Vector3(0f, 2.5f, 0f);
		}

		protected override void OnRefresh()
		{
			base.OnRefresh();
			primary.Enabled = true;
			secondary.Enabled = true;
		}
	}
	public class RemoveModificationsOverlayVisualizer : OverlayVisualizer
	{
		protected override void Initialize()
		{
			Freeze(primary);
			SpeedUp(secondary);
			VisualizeTerraformingBounds(primary);
			VisualizeIconInsideTerraformingBounds(secondary, (Texture)(object)IconCache.Cross);
		}

		protected override void OnRefresh()
		{
			primary.Enabled = true;
			secondary.Enabled = true;
		}
	}
	public abstract class UndoRedoModificationsOverlayVisualizer : OverlayVisualizer
	{
		protected override void Initialize()
		{
			Freeze(primary);
			Freeze(secondary);
			VisualizeRecoloringBounds(primary);
			VisualizeIconInsideRecoloringBounds(secondary, (Texture)(object)Icon());
		}

		protected override void OnRefresh()
		{
			primary.Enabled = true;
			secondary.Enabled = true;
		}

		protected abstract Texture2D Icon();
	}
	public class UndoModificationsOverlayVisualizer : UndoRedoModificationsOverlayVisualizer
	{
		protected override Texture2D Icon()
		{
			return IconCache.Undo;
		}
	}
	public class RedoModificationsOverlayVisualizer : UndoRedoModificationsOverlayVisualizer
	{
		protected override Texture2D Icon()
		{
			return IconCache.Redo;
		}
	}
}
namespace TerrainTools.Tools
{
	internal static class ToolConfigs
	{
		internal static Dictionary<string, ToolDB> ToolConfigsMap;

		static ToolConfigs()
		{
			Dictionary<string, ToolDB> obj = new Dictionary<string, ToolDB>
			{
				{
					"mud_road_v2_sq",
					new ToolDB("mud_road_v2_sq", "mud_road_v2", "$mud_road_v2_sq_name", "$mud_road_v2_sq_desc", IconCache.MudRoadSquare, PieceTables.Hoe, 1, typeof(LevelGroundOverlayVisualizer), null, null, null, null, null, null, null, null, null, null, null, null, null, invertGhost: false, (UsageTagFlags)1)
				},
				{
					"raise_v2_precise",
					new ToolDB("raise_v2_precise", "raise_v2", "$raise_v2_precise_name", "$raise_v2_precise_desc", IconCache.RaiseSquare, PieceTables.Hoe, 2, typeof(RaiseGroundOverlayVisualizer), null, null, null, null, null, null, null, null, null, null, null, null, null, invertGhost: false, (UsageTagFlags)1)
				}
			};
			Texture2D lower = IconCache.Lower;
			float? raiseRadius = 1.5f;
			float? raisePower = 0.5f;
			float? raiseDelta = -0.5f;
			Requirement[] requirements = Array.Empty<Requirement>();
			obj.Add("lower_v2", new ToolDB("lower_v2", "raise_v2", "$lower_v2_name", "$lower_v2_desc", lower, "_ShovelPieceTable", -1, null, null, null, null, raiseRadius, raisePower, raiseDelta, null, null, null, null, null, null, requirements, invertGhost: true, (UsageTagFlags)1));
			obj.Add("path_v2_square", new ToolDB("path_v2_square", "path_v2", "$path_v2_square_name", "$path_v2_square_desc", IconCache.MudRoadPathSquare, PieceTables.Hoe, 3, typeof(SquarePathOverlayVisualizer), null, null, null, null, null, null, null, null, null, null, null, null, null, invertGhost: false, (UsageTagFlags)1));
			obj.Add("paved_road_v2_square", new ToolDB("paved_road_v2_square", "paved_road_v2", "$paved_road_v2_square_name", "$paved_road_v2_square_desc", IconCache.PavedRoadSquare, PieceTables.Hoe, 4, typeof(SquarePathOverlayVisualizer), null, null, null, null, null, null, null, null, null, null, null, null, null, invertGhost: false, (UsageTagFlags)1));
			Texture2D pavedRoadPath = IconCache.PavedRoadPath;
			string hoe = PieceTables.Hoe;
			bool? smooth = false;
			obj.Add("paved_road_v2_path", new ToolDB("paved_road_v2_path", "paved_road_v2", "$paved_road_v2_path_name", "$paved_road_v2_path_desc", pavedRoadPath, hoe, 4, null, null, null, null, null, null, null, smooth, null, null, null, null, null, null, invertGhost: false, (UsageTagFlags)1));
			Texture2D pavedRoadPathSquare = IconCache.PavedRoadPathSquare;
			string hoe2 = PieceTables.Hoe;
			Type? typeFromHandle = typeof(SquarePathOverlayVisualizer);
			smooth = false;
			obj.Add("paved_road_v2_path_square", new ToolDB("paved_road_v2_path_square", "paved_road_v2", "$paved_road_v2_path_square_name", "$paved_road_v2_path_square_desc", pavedRoadPathSquare, hoe2, 4, typeFromHandle, null, null, null, null, null, null, smooth, null, null, null, null, null, null, invertGhost: false, (UsageTagFlags)1));
			Texture2D remove = IconCache.Remove;
			string hoe3 = PieceTables.Hoe;
			Type? typeFromHandle2 = typeof(RemoveModificationsOverlayVisualizer);
			smooth = false;
			bool? level = false;
			bool? raise = false;
			bool? clearPaint = false;
			bool? allowedInDeepSnow = true;
			obj.Add("remove_terrain_mods", new ToolDB("remove_terrain_mods", "mud_road_v2", "$remove_terrain_mods_name", "$remove_terrain_mods_desc", remove, hoe3, 4, typeFromHandle2, level, null, raise, null, null, null, smooth, null, null, clearPaint, null, allowedInDeepSnow, null, invertGhost: false, (UsageTagFlags)1));
			obj.Add("cultivate_v2_square", new ToolDB("cultivate_v2_square", "cultivate_v2", "$cultivate_v2_square_name", "$cultivate_v2_square_desc", IconCache.CultivateSquare, PieceTables.Cultivator, 1, typeof(CultivateOverlayVisualizer), null, null, null, null, null, null, null, null, null, null, null, null, null, invertGhost: false, (UsageTagFlags)1));
			Texture2D cultivatePath = IconCache.CultivatePath;
			string cultivator = PieceTables.Cultivator;
			allowedInDeepSnow = false;
			obj.Add("cultivate_v2_path", new ToolDB("cultivate_v2_path", "cultivate_v2", "$cultivate_v2_path_name", "$cultivate_v2_path_desc", cultivatePath, cultivator, 1, null, null, null, null, null, null, null, allowedInDeepSnow, null, null, null, null, null, null, invertGhost: false, (UsageTagFlags)1));
			Texture2D cultivatePathSquare = IconCache.CultivatePathSquare;
			string cultivator2 = PieceTables.Cultivator;
			Type? typeFromHandle3 = typeof(CultivateOverlayVisualizer);
			allowedInDeepSnow = false;
			obj.Add("cultivate_v2_path_square", new ToolDB("cultivate_v2_path_square", "cultivate_v2", "$cultivate_v2_path_square_name", "$cultivate_v2_path_square_desc", cultivatePathSquare, cultivator2, 1, typeFromHandle3, null, null, null, null, null, null, allowedInDeepSnow, null, null, null, null, null, null, invertGhost: false, (UsageTagFlags)1));
			obj.Add("replant_v2_square", new ToolDB("replant_v2_square", "replant_v2", "$replant_v2_square_name", "$replant_v2_square_desc", IconCache.ReplantSquare, PieceTables.Cultivator, 2, typeof(SeedGrassOverlayVisualizer), null, null, null, null, null, null, null, null, null, null, null, null, null, invertGhost: false, (UsageTagFlags)1));
			ToolConfigsMap = obj;
		}
	}
	internal class ToolDB
	{
		public string name;

		public string basePrefab;

		public string pieceName;

		public string pieceDesc;

		public Texture2D icon;

		public string pieceTable;

		public int insertIndex;

		public Type overlayType;

		public bool? level;

		public float? levelRadius;

		public bool? raise;

		public float? raiseRadius;

		public float? raisePower;

		public float? raiseDelta;

		public bool? smooth;

		public float? smoothRadius;

		public float? smoothPower;

		public bool? clearPaint;

		public float? paintRadius;

		public bool? allowedInDeepSnow;

		public Requirement[] requirements;

		public bool invertGhost;

		public UsageTagFlags usageTagFlags;

		public GameObject prefab;

		public ToolDB(string name, string basePrefab, string pieceName, string pieceDesc, Texture2D icon, string pieceTable, int insertIndex = -1, Type overlayType = null, bool? level = null, float? levelRadius = null, bool? raise = null, float? raiseRadius = null, float? raisePower = null, float? raiseDelta = null, bool? smooth = null, float? smoothRadius = null, float? smoothPower = null, bool? clearPaint = null, float? paintRadius = null, bool? allowedInDeepSnow = null, Requirement[] requirements = null, bool invertGhost = false, UsageTagFlags usageTagFlags = (UsageTagFlags)1)
		{
			//IL_00be: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
			this.name = name;
			this.basePrefab = basePrefab;
			this.pieceName = pieceName;
			this.pieceDesc = pieceDesc;
			this.icon = icon;
			this.pieceTable = pieceTable;
			this.insertIndex = insertIndex;
			if (overlayType != null)
			{
				this.overlayType = overlayType;
			}
			this.level = level;
			this.levelRadius = levelRadius;
			this.raise = raise;
			this.raiseRadius = raiseRadius;
			this.raisePower = raisePower;
			this.raiseDelta = raiseDelta;
			this.smooth = smooth;
			this.smoothRadius = smoothRadius;
			this.smoothPower = smoothPower;
			this.clearPaint = clearPaint;
			this.paintRadius = paintRadius;
			this.allowedInDeepSnow = allowedInDeepSnow;
			this.requirements = requirements;
			this.invertGhost = invertGhost;
			this.usageTagFlags = usageTagFlags;
		}
	}
}
namespace TerrainTools.Properties
{
	[GeneratedCode("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
	[DebuggerNonUserCode]
	[CompilerGenerated]
	internal class Resources
	{
		private static ResourceManager resourceMan;

		private static CultureInfo resourceCulture;

		[EditorBrowsable(EditorBrowsableState.Advanced)]
		internal static ResourceManager ResourceManager
		{
			get
			{
				if (resourceMan == null)
				{
					resourceMan = new ResourceManager("TerrainTools.Properties.Resources", typeof(Resources).Assembly);
				}
				return resourceMan;
			}
		}

		[EditorBrowsable(EditorBrowsableState.Advanced)]
		internal static CultureInfo Culture
		{
			get
			{
				return resourceCulture;
			}
			set
			{
				resourceCulture = value;
			}
		}

		internal static Bitmap box => (Bitmap)ResourceManager.GetObject("box", resourceCulture);

		internal static byte[] Bulgarian => (byte[])ResourceManager.GetObject("Bulgarian", resourceCulture);

		internal static byte[] Chinese => (byte[])ResourceManager.GetObject("Chinese", resourceCulture);

		internal static Bitmap cross => (Bitmap)ResourceManager.GetObject("cross", resourceCulture);

		internal static Bitmap cultivate_v2_path => (Bitmap)ResourceManager.GetObject("cultivate_v2_path", resourceCulture);

		internal static Bitmap cultivate_v2_path_square => (Bitmap)ResourceManager.GetObject("cultivate_v2_path_square", resourceCulture);

		internal static Bitmap cultivate_v2_square => (Bitmap)ResourceManager.GetObject("cultivate_v2_square", resourceCulture);

		internal static byte[] English => (byte[])ResourceManager.GetObject("English", resourceCulture);

		internal static Bitmap lower_v2 => (Bitmap)ResourceManager.GetObject("lower_v2", resourceCulture);

		internal static Bitmap mud_road_v2_square => (Bitmap)ResourceManager.GetObject("mud_road_v2_square", resourceCulture);

		internal static Bitmap path_v2_square => (Bitmap)ResourceManager.GetObject("path_v2_square", resourceCulture);

		internal static Bitmap paved_road_v2_path => (Bitmap)ResourceManager.GetObject("paved_road_v2_path", resourceCulture);

		internal static Bitmap paved_road_v2_path_square => (Bitmap)ResourceManager.GetObject("paved_road_v2_path_square", resourceCulture);

		internal static Bitmap paved_road_v2_square => (Bitmap)ResourceManager.GetObject("paved_road_v2_square", resourceCulture);

		internal static Bitmap raise => (Bitmap)ResourceManager.GetObject("raise", resourceCulture);

		internal static Bitmap raise_v2_square => (Bitmap)ResourceManager.GetObject("raise_v2_square", resourceCulture);

		internal static Bitmap redo => (Bitmap)ResourceManager.GetObject("redo", resourceCulture);

		internal static Bitmap remove => (Bitmap)ResourceManager.GetObject("remove", resourceCulture);

		internal static Bitmap replant_v2_square => (Bitmap)ResourceManager.GetObject("replant_v2_square", resourceCulture);

		internal static Bitmap ShovelIcon => (Bitmap)ResourceManager.GetObject("ShovelIcon", resourceCulture);

		internal static Bitmap smooth => (Bitmap)ResourceManager.GetObject("smooth", resourceCulture);

		internal static Bitmap undo => (Bitmap)ResourceManager.GetObject("undo", resourceCulture);

		internal Resources()
		{
		}
	}
}
namespace TerrainTools.Patches
{
	[HarmonyPatch(typeof(GameCamera))]
	internal static class GameCameraPatch
	{
		[HarmonyTranspiler]
		[HarmonyPatch("UpdateCamera")]
		private static IEnumerable<CodeInstruction> UpdateCameraTranspiler(IEnumerable<CodeInstruction> instructions)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Expected O, but got Unknown
			return new CodeMatcher(instructions, (ILGenerator)null).MatchForward(false, (CodeMatch[])(object)new CodeMatch[1]
			{
				new CodeMatch((OpCode?)OpCodes.Callvirt, (object)AccessTools.Method(typeof(Player), "CanRotatePiece", (Type[])null, (Type[])null), (string)null)
			}).SetInstructionAndAdvance(Transpilers.EmitDelegate<Func<Player, bool>>((Func<Player, bool>)UpdateCamera_BlockScroll_Delegate)).InstructionEnumeration();
		}

		private static bool UpdateCamera_BlockScroll_Delegate(Player localPlayer)
		{
			Piece val = ((localPlayer != null) ? localPlayer.GetSelectedPiece() : null);
			if ((Object)(object)((val != null) ? ((Component)val).gameObject : null) != (Object)null && (Object.op_Implicit((Object)(object)((Component)val).gameObject.GetComponentInChildren<RaiseGroundOverlayVisualizer>()) || RadiusModifier.ShouldModifyRadius() || SharpnessModifier.ShouldModifySharpness()))
			{
				return true;
			}
			return localPlayer.CanRotatePiece();
		}
	}
	[HarmonyPatch]
	internal static class HeightmapPatches
	{
		private static void CounterOffset(ref Vector3 worldPos)
		{
			worldPos.x += 0.5f;
			worldPos.z += 0.5f;
		}

		private static void UndoCounterOffset(ref Vector3 worldPos)
		{
			worldPos.x -= 0.5f;
			worldPos.z -= 0.5f;
		}

		public static float GetPaintMaskScale(this Heightmap hm)
		{
			float num = hm.m_width;
			return num / (num + 1f);
		}

		public static int GetPaintMaskWidth(this Heightmap hm)
		{
			return hm.m_width + 1;
		}

		public static Vector3 PaintMaskVertexToWorldPos(this Heightmap hm, int i, int j)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			Vector3 position = ((Component)hm).transform.position;
			int num = hm.GetPaintMaskWidth() / 2;
			float paintMaskScale = hm.GetPaintMaskScale();
			position.x += paintMaskScale * (float)(i - num);
			position.z += paintMaskScale * (float)(j - num);
			return position;
		}
	}
	[HarmonyPatch]
	internal class PlayerPatch
	{
		private static int m_width = 64;

		private static float m_scale = 1f;

		private static float PaintStepSize = 64f / 65f;

		[HarmonyFinalizer]
		[HarmonyPatch(typeof(Player), "UpdatePlacementGhost")]
		private static void UpdatePlacementGhostPostfix(Player __instance)
		{
			//IL_0044: 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_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			if (Object.op_Implicit((Object)(object)__instance) && ((Character)__instance).InPlaceMode() && !((Character)__instance).IsDead() && Object.op_Implicit((Object)(object)__instance.m_placementGhost) && Object.op_Implicit((Object)(object)__instance.m_placementGhost.GetComponent<OverlayVisualizer>()))
			{
				Vector3 position = __instance.m_placementGhost.transform.position;
				position.x = position.x.RoundToNearest(1f);
				position.z = position.z.RoundToNearest(1f);
				__instance.m_placementGhost.transform.position = position;
			}
		}
	}
}
namespace TerrainTools.Extensions
{
	public static class ConfigFileExtensions
	{
		public static bool DisableSaveOnConfigSet(this ConfigFile configFile)
		{
			bool saveOnConfigSet = configFile.SaveOnConfigSet;
			configFile.SaveOnConfigSet = false;
			return saveOnConfigSet;
		}
	}
	internal static class EventExtensions
	{
		public static void SafeInvoke(this Action events)
		{
			if (events == null)
			{
				return;
			}
			Delegate[] invocationList = events.GetInvocationList();
			for (int i = 0; i < invocationList.Length; i++)
			{
				Action action = (Action)invocationList[i];
				try
				{
					action();
				}
				catch (Exception ex)
				{
					Log.LogWarning($"Exception thrown at event {new StackFrame(1).GetMethod().Name} in {action.Method.DeclaringType.Name}.{action.Method.Name}:\n{ex}");
				}
			}
		}

		public static void SafeInvoke<TArg1>(this Action<TArg1> events, TArg1 arg1)
		{
			if (events == null)
			{
				return;
			}
			Delegate[] invocationList = events.GetInvocationList();
			for (int i = 0; i < invocationList.Length; i++)
			{
				Action<TArg1> action = (Action<TArg1>)invocationList[i];
				try
				{
					action(arg1);
				}
				catch (Exception ex)
				{
					Log.LogWarning($"Exception thrown at event {new StackFrame(1).GetMethod().Name} in {action.Method.DeclaringType.Name}.{action.Method.Name}:\n{ex}");
				}
			}
		}

		public static void SafeInvoke<TArg1, TArg2>(this Action<TArg1, TArg2> events, TArg1 arg1, TArg2 arg2)
		{
			if (events == null)
			{
				return;
			}
			Delegate[] invocationList = events.GetInvocationList();
			for (int i = 0; i < invocationList.Length; i++)
			{
				Action<TArg1, TArg2> action = (Action<TArg1, TArg2>)invocationList[i];
				try
				{
					action(arg1, arg2);
				}
				catch (Exception ex)
				{
					Log.LogWarning($"Exception thrown at event {new StackFrame(1).GetMethod().Name} in {action.Method.DeclaringType.Name}.{action.Method.Name}:\n{ex}");
				}
			}
		}

		public static void SafeInvoke<TEventArg>(this EventHandler<TEventArg> events, object sender, TEventArg arg1)
		{
			if (events == null)
			{
				return;
			}
			Delegate[] invocationList = events.GetInvocationList();
			for (int i = 0; i < invocationList.Length; i++)
			{
				EventHandler<TEventArg> eventHandler = (EventHandler<TEventArg>)invocationList[i];
				try
				{
					eventHandler(sender, arg1);
				}
				catch (Exception ex)
				{
					Log.LogWarning($"Exception thrown at event {new StackFrame(1).GetMethod().Name} in {eventHandler.Method.DeclaringType.Name}.{eventHandler.Method.Name}:\n{ex}");
				}
			}
		}
	}
	internal static class FloatExtensions
	{
		private const float Tolerance = 1E-06f;

		internal static bool Equals(this float x, float y, float eps = 1E-06f)
		{
			float num = Mathf.Abs(x - y);
			if (!(num <= eps))
			{
				return num <= Mathf.Max(Mathf.Abs(x), Mathf.Abs(y)) * eps;
			}
			return true;
		}

		internal static float RoundToNearest(this float x, float precision)
		{
			if ((double)precision != 0.0)
			{
				return Mathf.Sign(x) * Mathf.Floor(Mathf.Abs(x) / precision + 0.5f) * precision;
			}
			return x;
		}
	}
}
namespace TerrainTools.Core
{
	public static class GroundLevelSpinner
	{
		public const string MouseScrollWheel = "Mouse ScrollWheel";

		public const float ScrollPrecision = 0.05f;

		public const float SuperiorScrollPrecisionMultiplier = 0.2f;

		public const float MaxSpinner = 1f;

		public const float MinSpinner = 0f;

		public static float Value { get; private set; } = 1f;

		public static void Refresh()
		{
			float num = ScrollDelta();
			if (num > 0f)
			{
				Up(num);
			}
			if (num < 0f)
			{
				Down(num);
			}
		}

		private static float ScrollDelta()
		{
			float num = Input.GetAxis("Mouse ScrollWheel");
			if (num != 0f)
			{
				num = ((num > 0f) ? 0.05f : (-0.05f));
			}
			return num;
		}

		private static void Up(float scrollDelta)
		{
			if (Value + scrollDelta > 1f)
			{
				Value = 1f;
			}
			else
			{
				Value = Mathf.Round((Value + scrollDelta) * 100f) / 100f;
			}
		}

		private static void Down(float scrollDelta)
		{
			if (Value + scrollDelta < 0f)
			{
				Value = 0f;
			}
			else
			{
				Value = Mathf.Round((Value + scrollDelta) * 100f) / 100f;
			}
		}
	}
	[HarmonyPatch]
	internal static class InitManager
	{
		private static bool HasInitialized = false;

		internal static readonly Dictionary<string, GameObject> ToolRefs = new Dictionary<string, GameObject>();

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

		internal static void InitToolPieces()
		{
			if (HasInitialized)
			{
				return;
			}
			FixVanillaToolDescriptions();
			foreach (string key in ToolConfigs.ToolConfigsMap.Keys)
			{
				try
				{
					ToolDB toolDB = ToolConfigs.ToolConfigsMap[key];
					toolDB.prefab = MakeToolPiece(toolDB);
					Log.LogInfo("Created " + toolDB.name, Log.InfoLevel.Medium);
				}
				catch
				{
					Log.LogWarning("Failed to create: " + key);
				}
			}
			HasInitialized = true;
			UpdateTools();
		}

		internal static void FixVanillaToolDescriptions()
		{
			SetDescription("mud_road_v2", "Levels ground based on player position. Use shift + click to level ground based on where you are pointing (this will smooth the terrain).");
			SetDescription("raise_v2", "Raise grounds up to a maximum of 8 m above it's original height.");
			SetDescription("path_v2", "Creates a dirt path without affecting ground height.");
			SetDescription("paved_road_v2", "Creates a paved path and levels ground based on player position. Use shift + click to level ground based on where you are pointing (this will smooth the terrain).");
			SetDescription("cultivate_v2", "Cultivates ground and levels ground based on player position. Use shift + click to level ground based on where you are pointing (this will smooth the terrain).");
			SetDescription("replant_v2", "Replants terrain without affecting ground height.");
		}

		private static void SetDescription(string prefabName, string description)
		{
			Piece component = PrefabManager.Instance.GetPrefab(prefabName).GetComponent<Piece>();
			if (Object.op_Implicit((Object)(object)component))
			{
				component.m_description = description;
			}
			else
			{
				Log.LogWarning("Could not set description for: " + prefabName);
			}
		}

		internal static void UpdatePlugin(bool shouldSave = true)
		{
			UpdateTools();
			UpdateShovelRecipe();
			if (shouldSave)
			{
				((BaseUnityPlugin)TerrainTools.Instance).Config.Save();
			}
			TerrainTools.Instance.UpdatePlugin = false;
		}

		private static void UpdateTools()
		{
			if (!HasInitialized)
			{
				return;
			}
			ForceUnequipTerrainTools();
			foreach (ToolDB value in ToolConfigs.ToolConfigsMap.Values)
			{
				RemovePieceInPieceTable(value);
			}
			foreach (string key in ToolConfigs.ToolConfigsMap.Keys)
			{
				if (TerrainTools.Instance.IsToolEnabled(key))
				{
					ToolDB toolDB = ToolConfigs.ToolConfigsMap[key];
					RegisterPieceInPieceTable(toolDB.prefab, toolDB.pieceTable, null, toolDB.insertIndex);
				}
			}
		}

		private static void UpdateShovelRecipe()
		{
			if (Shovel.TryGetShovel(out var shovel))
			{
				shovel.Recipe.Recipe.m_enabled = TerrainTools.Instance.IsShovelEnabled;
			}
		}

		private static void ForceUnequipTerrainTools()
		{
			if (!Object.op_Implicit((Object)(object)Player.m_localPlayer))
			{
				return;
			}
			ItemData rightItem = ((Humanoid)Player.m_localPlayer).GetRightItem();
			if (rightItem != null)
			{
				if (rightItem.m_shared.m_name == "$item_cultivator")
				{
					Log.LogWarning("AdvancedTerrainModifiers updated through config change, unequipping cultivator");
					((Humanoid)Player.m_localPlayer).HideHandItems(false, true);
				}
				if (rightItem.m_shared.m_name == "$item_hoe")
				{
					Log.LogWarning("AdvancedTerrainModifiers updated through config change, unequipping hoe");
					((Humanoid)Player.m_localPlayer).HideHandItems(false, true);
				}
			}
		}

		internal static GameObject MakeToolPiece(ToolDB toolDB)
		{
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0211: Unknown result type (might be due to invalid IL or missing references)
			//IL_021d: 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_0236: Unknown result type (might be due to invalid IL or missing references)
			if (PieceManager.Instance.GetPiece(toolDB.pieceName) != null)
			{
				return null;
			}
			GameObject val = PrefabManager.Instance.CreateClonedPrefab(toolDB.name, toolDB.basePrefab);
			if ((Object)(object)val == (Object)null)
			{
				return null;
			}
			Piece component = val.GetComponent<Piece>();
			component.m_icon = Sprite.Create(toolDB.icon, new Rect(0f, 0f, (float)((Texture)toolDB.icon).width, (float)((Texture)toolDB.icon).height), Vector2.zero);
			component.m_name = toolDB.pieceName;
			component.m_description = toolDB.pieceDesc;
			if (toolDB.requirements != null)
			{
				component.m_resources = toolDB.requirements;
			}
			component.m_allowedInDeepSnow = UpdateValueIfNeeded(component.m_allowedInDeepSnow, toolDB.allowedInDeepSnow);
			Settings settings = val.GetComponent<TerrainOp>().m_settings;
			settings.m_level = UpdateValueIfNeeded(settings.m_level, toolDB.level);
			settings.m_levelRadius = UpdateValueIfNeeded(settings.m_levelRadius, toolDB.levelRadius);
			settings.m_raise = UpdateValueIfNeeded(settings.m_raise, toolDB.raise);
			settings.m_raiseRadius = UpdateValueIfNeeded(settings.m_raiseRadius, toolDB.raiseRadius);
			settings.m_raisePower = UpdateValueIfNeeded(settings.m_raisePower, toolDB.raisePower);
			settings.m_raiseDelta = UpdateValueIfNeeded(settings.m_raiseDelta, toolDB.raiseDelta);
			settings.m_smooth = UpdateValueIfNeeded(settings.m_smooth, toolDB.smooth);
			settings.m_smoothRadius = UpdateValueIfNeeded(settings.m_smoothRadius, toolDB.smoothRadius);
			settings.m_smoothPower = UpdateValueIfNeeded(settings.m_smoothPower, toolDB.smoothPower);
			settings.m_paintCleared = UpdateValueIfNeeded(settings.m_paintCleared, toolDB.clearPaint);
			settings.m_paintRadius = UpdateValueIfNeeded(settings.m_paintRadius, toolDB.paintRadius);
			if (toolDB.overlayType != null)
			{
				val.AddComponent(toolDB.overlayType);
			}
			if (toolDB.invertGhost)
			{
				Transform val2 = val.transform.Find("_GhostOnly");
				if ((Object)(object)val2 != (Object)null)
				{
					val2.localRotation = Quaternion.Euler(270f, 0f, 0f);
					val2.localPosition += new Vector3(0f, 2f, 0f);
				}
			}
			return val;
		}

		private static T UpdateValueIfNeeded<T>(T source, T? newValue) where T : struct
		{
			if (!newValue.HasValue)
			{
				return source;
			}
			return newValue.Value;
		}

		private static T UpdateValueIfNeeded<T>(T source, T newValue)
		{
			if (newValue == null)
			{
				return source;
			}
			return newValue;
		}

		private static void RegisterPieceInPieceTable(GameObject prefab, string pieceTable, string category, int position = -1)
		{
			//IL_009b: 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)
			Piece component = prefab.GetComponent<Piece>();
			if (!Object.op_Implicit((Object)(object)component))
			{
				throw new Exception("Prefab " + ((Object)prefab).name + " has no Piece component attached");
			}
			PieceTable pieceTable2 = PieceManager.Instance.GetPieceTable(pieceTable);
			if ((Object)(object)pieceTable2 == (Object)null)
			{
				throw new Exception("Could not find PieceTable " + pieceTable);
			}
			PrefabManager.Instance.RegisterToZNetScene(prefab);
			if (pieceTable2.m_pieces.Contains(prefab))
			{
				Log.LogDebug("Already added piece " + ((Object)prefab).name);
				return;
			}
			if (!string.IsNullOrEmpty(category))
			{
				component.m_category = PieceManager.Instance.AddPieceCategory(category);
			}
			if (!InsertionIndexes.ContainsKey(pieceTable))
			{
				InsertionIndexes[pieceTable] = new List<int>();
			}
			int num = 0;
			if (position >= 0)
			{
				num = position + InsertionIndexes[pieceTable].Where((int x) => x <= position).Count();
			}
			else if (num >= pieceTable2.m_pieces.Count && pieceTable2.m_pieces.Count != 0)
			{
				Log.LogWarning("Piece insertion index is out of bounds");
			}
			if (position >= 0 && num < pieceTable2.m_pieces.Count)
			{
				pieceTable2.m_pieces.Insert(num, prefab);
				InsertionIndexes[pieceTable].Add(position);
			}
			else
			{
				pieceTable2.m_pieces.Add(prefab);
				InsertionIndexes[pieceTable].Add(pieceTable2.m_pieces.Count - 1);
			}
			Log.LogDebug("Added piece " + ((Object)prefab).name + " | Token: " + PieceExtension.TokenName(component));
		}

		private static void RemovePieceInPieceTable(ToolDB toolDB)
		{
			GameObject prefab = toolDB.prefab;
			if (!Object.op_Implicit((Object)(object)prefab))
			{
				return;
			}
			PieceTable pieceTable = PieceManager.Instance.GetPieceTable(toolDB.pieceTable);
			if ((Object)(object)pieceTable == (Object)null)
			{
				throw new Exception("Could not find PieceTable " + toolDB.pieceTable);
			}
			int num = pieceTable.m_pieces.IndexOf(prefab);
			if (num < 0)
			{
				return;
			}
			pieceTable.m_pieces.Remove(prefab);
			Log.LogDebug("Removed piece " + ((Object)prefab).name);
			List<int> list = InsertionIndexes[toolDB.pieceTable];
			if (!list.Contains(num))
			{
				return;
			}
			list.Remove(num);
			for (int i = 0; i < list.Count; i++)
			{
				if (list[i] > num)
				{
					list[i]--;
				}
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(ObjectDB), "UpdateRegisters")]
		private static void UpdateRegistersPostfix()
		{
			Log.LogInfo("Updating ObjectDB registers to ensure custom terrain operations are retained.", Log.InfoLevel.Medium);
			foreach (string key in ToolConfigs.ToolConfigsMap.Keys)
			{
				if (TerrainTools.Instance.IsToolEnabled(key))
				{
					RegisterTerrainOpInObjectDB(ToolConfigs.ToolConfigsMap[key].prefab);
				}
			}
		}

		internal static void RegisterTerrainOpInObjectDB(GameObject prefab)
		{
			TerrainOp val = default(TerrainOp);
			if (!HasInitialized || !Object.op_Implicit((Object)(object)ObjectDB.instance) || ObjectDB.instance.m_terrainOpsByHash == null || ObjectDB.instance.m_terrainOps == null || !Object.op_Implicit((Object)(object)prefab) || !prefab.TryGetComponent<TerrainOp>(ref val) || !Object.op_Implicit((Object)(object)val))
			{
				return;
			}
			int stableHashCode = StringExtensionMethods.GetStableHashCode(((Object)prefab).name);
			if (ObjectDB.instance.m_terrainOpsByHash.TryGetValue(stableHashCode, out var value))
			{
				if ((Object)(object)value != (Object)(object)val)
				{
					Log.LogWarning($"TerrainOp prefab hash collision for {((Object)prefab).name} ({stableHashCode}); keeping the registered prefab");
				}
				return;
			}
			if (!ObjectDB.instance.m_terrainOps.Contains(val))
			{
				ObjectDB.instance.m_terrainOps.Add(val);
			}
			if (!ObjectDB.instance.m_terrainOpsByHash.ContainsKey(stableHashCode))
			{
				ObjectDB.instance.m_terrainOpsByHash.Add(stableHashCode, val);
			}
			Log.LogInfo("Registered TerrainOp " + ((Object)prefab).name + " in ObjectDB", Log.InfoLevel.Medium);
		}
	}
	[HarmonyPatch(typeof(PreciseTerrainModifier))]
	public static class PreciseTerrainModifier
	{
		public class HeightIndex
		{
			public int Index;

			public Vector3 Position;

			public float DistanceWidth;

			public float DistanceDepth;

			public float Distance;
		}

		public class PaintIndex
		{
			public int Index;

			public Vector3 Position;
		}

		public class Indices
		{
			public HeightIndex[] HeightIndices = new HeightIndex[0];

			public PaintIndex[] PaintIndices = new PaintIndex[0];
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(ClutterSystem), "ResetGrass")]
		private static void ResetGrassPrefix(ClutterSystem __instance, Vector3 center, ref float radius)
		{
			if (TerrainOperations.IsPrecisionModifier(radius))
			{
				radius = 1f;
			}
		}

		[HarmonyPrefix]
		[HarmonyPriority(100)]
		[HarmonyPatch(typeof(TerrainComp), "ApplyOperation")]
		private static void ApplyOperationPrefix(TerrainComp __instance, TerrainOp modifier)
		{
			if (Object.op_Implicit((Object)(object)__instance) && Object.op_Implicit((Object)(object)__instance.m_nview) && Object.op_Implicit((Object)(object)modifier) && Object.op_Implicit((Object)(object)((Component)modifier).gameObject) && !__instance.m_nview.IsOwner())
			{
				__instance.m_nview.ClaimOwnership();
			}
		}

		[HarmonyPostfix]
		[HarmonyPriority(700)]
		[HarmonyPatch(typeof(Settings), "Deserialize")]
		private static void TerrainOpDeserializePostfix(ZPackage pkg, ref Settings __result)
		{
			if (__result == null || pkg == null)
			{
				return;
			}
			pkg.SetPos(pkg.GetPos() - 4);
			int num = pkg.ReadInt();
			TerrainOp val = default(TerrainOp);
			if (Object.op_Implicit((Object)(object)ObjectDB.instance) && ObjectDB.instance.TryGetTerrainOp(num, ref val) && Object.op_Implicit((Object)(object)val) && Object.op_Implicit((Object)(object)((Component)val).GetComponentInChildren<OverlayVisualizer>()))
			{
				Log.LogInfo("Deserializing precision tool: " + ((Object)val).name, Log.InfoLevel.Medium);
				if (__result.m_smooth)
				{
					__result.m_smoothRadius = float.NegativeInfinity;
				}
				if (__result.m_raise && __result.m_raiseDelta >= 0f)
				{
					__result.m_raiseRadius = float.NegativeInfinity;
					__result.m_raiseDelta = GroundLevelSpinner.Value;
				}
				if (__result.m_paintCleared)
				{
					__result.m_paintRadius = float.NegativeInfinity;
				}
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(TerrainOp), "GetRadius")]
		private static void GetRadiusPostfix(TerrainOp __instance, ref float __result)
		{
			if (Object.op_Implicit((Object)(object)__instance) && __instance.m_settings.IsPrecisionModifier())
			{
				__result = Mathf.Max(__result, (float)__instance.GetFixedRadius());
			}
		}

		[HarmonyPrefix]
		[HarmonyPriority(700)]
		[HarmonyPatch(typeof(TerrainComp), "InternalDoOperation")]
		private static void InternalDoOperationPrefix(TerrainComp __instance, Vector3 pos, Settings modifier)
		{
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			if (!modifier.m_level && !modifier.m_raise && !modifier.m_smooth && !modifier.m_paintCleared)
			{
				__instance.RemoveTerrainModifications(pos);
				__instance.PreciseRecolorTerrain(pos, (PaintType)3);
			}
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(TerrainComp), "SmoothTerrain")]
		private static bool SmoothTerrainPrefix(TerrainComp __instance, Vector3 worldPos, float radius)
		{
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			if (TerrainOperations.IsPrecisionModifier(radius))
			{
				__instance.PreciseSmoothTerrain(worldPos);
				return false;
			}
			return true;
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(TerrainComp), "RaiseTerrain")]
		private static bool RaiseTerrainPrefix(TerrainComp __instance, Vector3 worldPos, float radius, float delta)
		{
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			if (TerrainOperations.IsPrecisionModifier(radius))
			{
				__instance.PreciseRaiseTerrain(worldPos, delta);
				return false;
			}
			return true;
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(TerrainComp), "PaintCleared")]
		private static bool PaintClearedPrefix(TerrainComp __instance, Vector3 worldPos, Vector3 rot, Settings settings)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			if (TerrainOperations.IsPrecisionModifier(settings.m_paintRadius))
			{
				__instance.PreciseRecolorTerrain(worldPos, settings.m_paintType);
				return false;
			}
			return true;
		}
	}
	[HarmonyPatch]
	internal static class RadiusModifier
	{
		private static bool RadiusToolIsInUse = false;

		private static float lastOriginalRadius;

		private static float lastModdedRadius;

		private static float lastTotalDelta;

		private static float lastRaiseRadius;

		private static float lastSmoothRadius;

		private static float lastPaintRadius;

		private static float lastLevelRadius;

		private static Vector3 lastGhostScale = Vector3.zero;

		private const float MinRadius = 0.5f;

		private const float Tolerance = 0.01f;

		[HarmonyPrefix]
		[HarmonyPatch(typeof(Player), "Update")]
		private static void UpdatePrefix(Player __instance)
		{
			//IL_0050: 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_006e: Unknown result type (might be due to invalid IL or missing references)
			if (!Object.op_Implicit((Object)(object)__instance) || (Object)(object)__instance != (Object)(object)Player.m_localPlayer)
			{
				return;
			}
			TerrainOp terrainOp;
			if (!((Character)__instance).InPlaceMode() || Hud.IsPieceSelectionVisible())
			{
				if (RadiusToolIsInUse)
				{
					RadiusToolIsInUse = false;
					lastOriginalRadius = 0f;
					lastModdedRadius = 0f;
					lastTotalDelta = 0f;
					lastGhostScale = Vector3.zero;
				}
			}
			else if (IsValidSelectedPiece(__instance, out terrainOp))
			{
				if (ShouldModifyRadius())
				{
					SetRadius(terrainOp, Input.mouseScrollDelta.y * TerrainTools.Instance.RadiusScrollScale);
				}
				RefreshGhostScale(__instance);
			}
		}

		internal static bool IsValidSelectedPiece(Player player, out TerrainOp terrainOp)
		{
			Piece selectedPiece = player.GetSelectedPiece();
			if (!Object.op_Implicit((Object)(object)selectedPiece) || !Object.op_Implicit((Object)(object)((Component)selectedPiece).gameObject) || Object.op_Implicit((Object)(object)((Component)selectedPiece).gameObject.GetComponent<OverlayVisualizer>()))
			{
				terrainOp = null;
				return false;
			}
			terrainOp = ((Component)selectedPiece).gameObject.GetComponent<TerrainOp>();
			if (!Object.op_Implicit((Object)(object)terrainOp))
			{
				return false;
			}
			return true;
		}

		internal static bool ShouldModifyRadius()
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			if (TerrainTools.Instance.IsEnableRadiusModifier && Input.GetKey(TerrainTools.Instance.RadiusKey))
			{
				return Input.mouseScrollDelta.y != 0f;
			}
			return false;
		}

		[HarmonyPrefix]
		[HarmonyPriority(100)]
		[HarmonyPatch(typeof(TerrainOp), "Awake")]
		private static void TerrainOpAwakeModifyRadius(TerrainOp __instance)
		{
			if (Object.op_Implicit((Object)(object)__instance) && !TerrainOp.m_forceDisableTerrainOps)
			{
				ApplyModifiedRadius(__instance.m_settings);
			}
		}

		[HarmonyPrefix]
		[HarmonyPriority(100)]
		[HarmonyPatch(typeof(TerrainComp), "InternalDoOperation")]
		private static void InternalDoOperationPrefix(TerrainComp __instance, Settings modifier)
		{
			if (Object.op_Implicit((Object)(object)__instance))
			{
				ApplyModifiedRadius(modifier);
			}
		}

		private static void ApplyModifiedRadius(Settings modifier)
		{
			if (modifier != null && !modifier.IsPrecisionModifier())
			{
				if (modifier.m_level)
				{
					lastLevelRadius = modifier.m_levelRadius;
					modifier.m_levelRadius = ModifyRadius(modifier.m_levelRadius, lastTotalDelta);
					Log.LogInfo($"Applying level radius {modifier.m_levelRadius}", Log.InfoLevel.Medium);
				}
				if (modifier.m_raise)
				{
					lastRaiseRadius = modifier.m_raiseRadius;
					modifier.m_raiseRadius = ModifyRadius(modifier.m_raiseRadius, lastTotalDelta);
					Log.LogInfo($"Applying raise radius {modifier.m_raiseRadius}", Log.InfoLevel.Medium);
				}
				if (modifier.m_smooth)
				{
					lastSmoothRadius = modifier.m_smoothRadius;
					modifier.m_smoothRadius = ModifyRadius(modifier.m_smoothRadius, lastTotalDelta);
					Log.LogInfo($"Applying smooth radius {modifier.m_smoothRadius}", Log.InfoLevel.Medium);
				}
				if (modifier.m_paintCleared)
				{
					lastPaintRadius = modifier.m_paintRadius;
					modifier.m_paintRadius = ModifyRadius(modifier.m_paintRadius, lastTotalDelta);
					Log.LogInfo($"Applying paint radius {modifier.m_paintRadius}", Log.InfoLevel.Medium);
				}
			}
		}

		[HarmonyPostfix]
		[HarmonyPriority(700)]
		[HarmonyPatch(typeof(TerrainComp), "InternalDoOperation")]
		private static void InternalDoOperationPostfix(TerrainComp __instance, Settings modifier)
		{
			if (Object.op_Implicit((Object)(object)__instance) && modifier != null && !modifier.IsPrecisionModifier())
			{
				if (modifier.m_level)
				{
					modifier.m_levelRadius = lastLevelRadius;
					Log.LogInfo($"Restored level radius {modifier.m_levelRadius}", Log.InfoLevel.Medium);
				}
				if (modifier.m_raise)
				{
					modifier.m_raiseRadius = lastRaiseRadius;
					Log.LogInfo($"Restored raise radius {modifier.m_raiseRadius}", Log.InfoLevel.Medium);
				}
				if (modifier.m_smooth)
				{
					modifier.m_smoothRadius = lastSmoothRadius;
					Log.LogInfo($"Restored smooth radius {modifier.m_smoothRadius}", Log.InfoLevel.Medium);
				}
				if (modifier.m_paintCleared)
				{
					modifier.m_paintRadius = lastPaintRadius;
					Log.LogInfo($"Restored paint radius {modifier.m_paintRadius}", Log.InfoLevel.Medium);
				}
			}
		}

		private static float ModifyRadius(float radius, float delta)
		{
			return Mathf.Clamp(radius + delta, 0.5f, TerrainTools.Instance.MaxRadius);
		}

		private static void SetRadius(TerrainOp terrainOp, float delta)
		{
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			Log.LogInfo($"Adjusting radius by {delta}", Log.InfoLevel.High);
			if (!RadiusToolIsInUse && Object.op_Implicit((Object)(object)terrainOp))
			{
				if (TryGetMaximumRadius(terrainOp, out var maxRadius))
				{
					RadiusToolIsInUse = true;
					lastOriginalRadius = maxRadius;
					lastModdedRadius = ModifyRadius(maxRadius, delta);
					lastTotalDelta += delta;
				}
			}
			else
			{
				lastModdedRadius = ModifyRadius(lastModdedRadius, delta);
				lastTotalDelta += delta;
			}
			Log.LogInfo($"Total delta {lastTotalDelta}", Log.InfoLevel.High);
			lastGhostScale = new Vector3(lastModdedRadius / lastOriginalRadius, lastModdedRadius / lastOriginalRadius, lastModdedRadius / lastOriginalRadius);
		}

		private static void RefreshGhostScale(Player player)
		{
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			if (!RadiusToolIsInUse || !Object.op_Implicit((Object)(object)player.m_placementGhost))
			{
				return;
			}
			Transform val = player.m_placementGhost.transform.Find("_GhostOnly");
			if (Object.op_Implicit((Object)(object)val))
			{
				ParticleSystem componentInChildren = ((Component)val).GetComponentInChildren<ParticleSystem>();
				if (Object.op_Implicit((Object)(object)componentInChildren) && !(lastGhostScale == Vector3.zero) && Vector3.Distance(((Component)componentInChildren).transform.localScale, lastGhostScale) > 0.01f)
				{
					Log.LogInfo($"Adjusting ghost scale to {lastGhostScale}x", Log.InfoLevel.High);
					((Component)componentInChildren).transform.localScale = lastGhostScale;
				}
			}
		}

		private static bool TryGetMaximumRadius(TerrainOp terrainOp, out float maxRadius)
		{
			maxRadius = 0f;
			if (terrainOp.m_settings.m_level && maxRadius < terrainOp.m_settings.m_levelRadius)
			{
				maxRadius = terrainOp.m_settings.m_levelRadius;
			}
			if (terrainOp.m_settings.m_raise && maxRadius < terrainOp.m_settings.m_raiseRadius)
			{
				maxRadius = terrainOp.m_settings.m_raiseRadius;
			}
			if (terrainOp.m_settings.m_smooth && maxRadius < terrainOp.m_settings.m_smoothRadius)
			{
				maxRadius = terrainOp.m_settings.m_smoothRadius;
			}
			if (terrainOp.m_settings.m_paintCleared && maxRadius < terrainOp.m_settings.m_paintRadius)
			{
				maxRadius = terrainOp.m_settings.m_paintRadius;
			}
			return maxRadius != 0f;
		}

		[HarmonyPostfix]
		[HarmonyBefore(new string[] { "Searica.Valheim.BuildRestrictionTweaksSync" })]
		[HarmonyPatch(typeof(Player), "UpdatePlacementGhost")]
		private static void CheckValidTerrainOpPlacement(Player __instance)
		{
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: 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_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00be: Unknown result type (might be due to invalid IL or missing references)
			//IL_0129: Unknown result type (might be due to invalid IL or missing references)
			//IL_012f: Invalid comparison between Unknown and I4
			//IL_00d0: 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_00e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fc: Invalid comparison between Unknown and I4
			//IL_0100: Unknown result type (might be due to invalid IL or missing references)
			//IL_0112: Unknown result type (might be due to invalid IL or missing references)
			TerrainOp val = default(TerrainOp);
			Piece val2 = default(Piece);
			if (!Object.op_Implicit((Object)(object)__instance) || (Object)(object)__instance.m_placementGhost == (Object)null || !__instance.m_placementGhost.TryGetComponent<TerrainOp>(ref val) || !TryGetMaximumRadius(val, out var maxRadius) || !__instance.m_placementGhost.TryGetComponent<Piece>(ref val2))
			{
				return;
			}
			if (RadiusToolIsInUse)
			{
				maxRadius *= lastGhostScale.x;
			}
			int num = Mathf.CeilToInt((float)Math.PI * maxRadius);
			Matrix4x4 val3 = Matrix4x4.Rotate(Quaternion.AngleAxis(57.29578f * ((float)Math.PI * 2f / (float)num), Vector3.up));
			Vector3 val4 = default(Vector3);
			((Vector3)(ref val4))..ctor(maxRadius, 0f, 0f);
			for (int i = 0; i <= num; i++)
			{
				val4 = ((Matrix4x4)(ref val3)).MultiplyVector(val4);
				Vector3 val5 = ((Component)val).gameObject.transform.position + val4;
				if (Location.IsInsideNoBuildLocation(val5))
				{
					__instance.m_placementStatus = (PlacementStatus)3;
					break;
				}
				Heightmap val6 = Heightmap.FindHeightmap(val5);
				if ((Object)(object)val6 == (Object)null)
				{
					__instance.m_placementStatus = (PlacementStatus)1;
					break;
				}
				if (!val2.m_allowedInDeepSnow && (int)__instance.m_currentBiome == 64 && val6.GetCultivationMask(val5) > __instance.m_deepSnowBuildHeight)
				{
					__instance.m_placementStatus = (PlacementStatus)13;
					break;
				}
			}
			__instance.SetPlacementGhostValid((int)__instance.m_placementStatus == 0);
		}
	}
	[HarmonyPatch]
	internal static class SharpnessModifier
	{
		private static bool SmoothToolIsInUse;

		private static float lastModdedSmoothPwr;

		private static float lastTotalSmoothDelta;

		private const float MinSmoothPwr = 1f;

		private const float MaxSmoothPwr = 30f;

		private static bool RaiseToolIsInUse;

		private static float lastModdedRaisePwr;

		private static float lastTotalRaiseDelta;

		private const float MinRaisePwr = 0.05f;

		private const float MaxRaisePwr = 1f;

		private static float lastRaisePower;

		private static float lastSmoothPower;

		private const float DisplayThreshold = 0.9f;

		private static float lastDisplayedSmoothSharpness;

		private static float lastDisplayedRaiseSharpness;

		[HarmonyPrefix]
		[HarmonyPriority(300)]
		[HarmonyPatch(typeof(Player), "Update")]
		private static void UpdatePrefix(Player __instance)
		{
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			if (!Object.op_Implicit((Object)(object)__instance) || (Object)(object)__instance != (Object)(object)Player.m_localPlayer)
			{
				return;
			}
			if (!((Character)__instance).InPlaceMode() || Hud.IsPieceSelectionVisible())
			{
				if (SmoothToolIsInUse)
				{
					SmoothToolIsInUse = false;
					lastModdedSmoothPwr = 0f;
					lastTotalSmoothDelta = 0f;
					lastDisplayedSmoothSharpness = -1f;
					SetPower(__instance, 0f);
				}
				if (RaiseToolIsInUse)
				{
					RaiseToolIsInUse = false;
					lastModdedRaisePwr = 0f;
					lastTotalRaiseDelta = 0f;
					lastDisplayedRaiseSharpness = -1f;
					SetPower(__instance, 0f);
				}
			}
			else if (ShouldModifySharpness())
			{
				SetPower(__instance, Input.mouseScrollDelta.y * TerrainTools.Instance.SharpnessScrollScale);
			}
		}

		internal static bool ShouldModifySharpness()
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			if (TerrainTools.Instance.IsEnableSharpnessModifier && Input.GetKey(TerrainTools.Instance.SharpnessKey))
			{
				return Input.mouseScrollDelta.y != 0f;
			}
			return false;
		}

		[HarmonyPrefix]
		[HarmonyPriority(101)]
		[HarmonyPatch(typeof(TerrainComp), "InternalDoOperation")]
		private static void InternalDoOperationPrefix(TerrainComp __instance, Settings modifier)
		{
			if (Object.op_Implicit((Object)(object)__instance) && modifier != null && !modifier.IsPrecisionModifier())
			{
				if (modifier.m_raise)
				{
					lastRaisePower = modifier.m_raisePower;
					modifier.m_raisePower = ModifyRaisePower(modifier.m_raisePower, lastTotalRaiseDelta);
					Log.LogInfo($"Applying raise power {modifier.m_raisePower}", Log.InfoLevel.Medium);
				}
				if (modifier.m_smooth)
				{
					lastSmoothPower = modifier.m_smoothPower;
					modifier.m_smoothPower = ModifySmoothPower(modifier.m_smoothPower, lastTotalSmoothDelta);
					Log.LogInfo($"Applying smooth power {modifier.m_smoothPower}", Log.InfoLevel.Medium);
				}
			}
		}

		[HarmonyPostfix]
		[HarmonyPriority(701)]
		[HarmonyPatch(typeof(TerrainComp), "InternalDoOperation")]
		private static void InternalDoOperationPostfix(TerrainComp __instance, Settings modifier)
		{
			if (Object.op_Implicit((Object)(object)__instance) && modifier != null && !modifier.IsPrecisionModifier())
			{
				if (modifier.m_raise)
				{
					modifier.m_raisePower = lastRaisePower;
					Log.LogInfo($"Restored raise power {modifier.m_raisePower}", Log.InfoLevel.Medium);
				}
				if (modifier.m_smooth)
				{
					modifier.m_smoothPower = lastSmoothPower;
					Log.LogInfo($"Restored smooth power {modifier.m_smoothPower}", Log.InfoLevel.Medium);
				}
			}
		}

		private static void SetPower(Player player, float delta)
		{
			Piece selectedPiece = player.GetSelectedPiece();
			if (!Object.op_Implicit((Object)(object)selectedPiece) || !Object.op_Implicit((Object)(object)((Component)selectedPiece).gameObject) || Object.op_Implicit((Object)(object)((Component)selectedPiece).gameObject.GetComponent<OverlayVisualizer>()))
			{
				return;
			}
			TerrainOp component = ((Component)selectedPiece).gameObject.GetComponent<TerrainOp>();
			if (!Object.op_Implicit((Object)(object)component))
			{
				return;
			}
			SetSmoothPower(component, delta);
			SetRaisePower(component, delta);
			List<string> list = new List<string>();
			if (SmoothToolIsInUse)
			{
				float smoothPowerDisplayValue = GetSmoothPowerDisplayValue(lastModdedSmoothPwr);
				if (Mathf.Abs(smoothPowerDisplayValue - lastDisplayedSmoothSharpness) > 0.9f)
				{
					lastDisplayedSmoothSharpness = Mathf.Round(smoothPowerDisplayValue);
					list.Add($"$terrain_tool_hud_name $smooth_op_name $sharpness_hud_name: {smoothPowerDisplayValue:0}%");
				}
			}
			if (RaiseToolIsInUse)
			{
				float raisePowerDisplayValue = GetRaisePowerDisplayValue(lastModdedRaisePwr);
				if (Mathf.Abs(raisePowerDisplayValue - lastDisplayedRaiseSharpness) > 0.9f)
				{
					lastDisplayedRaiseSharpness = Mathf.Round(raisePowerDisplayValue);
					string arg = ((component.m_settings.m_raiseDelta < 0f) ? "$dig_op_name" : "$raise_op_name");
					list.Add($"$terrain_tool_hud_name {arg} $sharpness_hud_name: {raisePowerDisplayValue:0}%");
				}
			}
			if (SmoothToolIsInUse || RaiseToolIsInUse)
			{
				Sprite icon = player.m_placementGhost.GetComponent<Piece>().m_icon;
				if ((Object)(object)icon != (Object)null && list.Count > 0)
				{
					((Character)player).Message((MessageType)2, string.Join("\n", list.ToArray()), 0, icon, false);
				}
			}
		}

		private static void SetSmoothPower(TerrainOp terrainOp, float delta)
		{
			if (terrainOp.m_settings.m_smooth)
			{
				Log.LogInfo($"Adjusting Smooth Power by {delta}", Log.InfoLevel.High);
				if (!SmoothToolIsInUse)
				{
					SmoothToolIsInUse = true;
					lastModdedSmoothPwr = ModifySmoothPower(terrainOp.m_settings.m_smoothPower, delta);
				}
				else
				{
					lastModdedSmoothPwr = ModifySmoothPower(lastModdedSmoothPwr, delta);
				}
				lastTotalSmoothDelta += delta;
				Log.LogInfo($"Total smooth power delta {lastTotalSmoothDelta}", Log.InfoLevel.High);
			}
		}

		private static void SetRaisePower(TerrainOp terrainOp, float delta)
		{
			if (terrainOp.m_settings.m_raise)
			{
				delta = ConvertSmoothDeltaToRaiseDelta(delta);
				Log.LogInfo($"Adjusting Raise Power by {de