Decompiled source of EarthWright v0.3.0

plugins\EarthWright.dll

Decompiled a day ago
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Serialization;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using Charter;
using ConfigReload;
using EarthWright.Actions;
using EarthWright.Brush;
using EarthWright.Clearing;
using EarthWright.Core;
using EarthWright.Costs;
using EarthWright.Extras;
using EarthWright.Gear;
using EarthWright.History;
using EarthWright.Menu;
using EarthWright.Paths;
using EarthWright.Preview;
using EarthWright.Protection;
using EarthWright.Terrain;
using HarmonyLib;
using ItemCopies;
using Microsoft.CodeAnalysis;
using PatchGuard;
using PlateColumn;
using SyncedConfig;
using TMPro;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Events;
using UnityEngine.Rendering;
using UnityEngine.UI;
using YamlConfig;
using YamlDotNet.Core;
using YamlDotNet.Core.Events;
using YamlDotNet.Core.Tokens;
using YamlDotNet.Helpers;
using YamlDotNet.RepresentationModel;
using YamlDotNet.Serialization;
using YamlDotNet.Serialization.BufferedDeserialization;
using YamlDotNet.Serialization.BufferedDeserialization.TypeDiscriminators;
using YamlDotNet.Serialization.Callbacks;
using YamlDotNet.Serialization.Converters;
using YamlDotNet.Serialization.EventEmitters;
using YamlDotNet.Serialization.NamingConventions;
using YamlDotNet.Serialization.NodeDeserializers;
using YamlDotNet.Serialization.NodeTypeResolvers;
using YamlDotNet.Serialization.ObjectFactories;
using YamlDotNet.Serialization.ObjectGraphTraversalStrategies;
using YamlDotNet.Serialization.ObjectGraphVisitors;
using YamlDotNet.Serialization.Schemas;
using YamlDotNet.Serialization.TypeInspectors;
using YamlDotNet.Serialization.TypeResolvers;
using YamlDotNet.Serialization.Utilities;
using YamlDotNet.Serialization.ValueDeserializers;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: IgnoresAccessChecksTo("assembly_guiutils")]
[assembly: IgnoresAccessChecksTo("assembly_utils")]
[assembly: IgnoresAccessChecksTo("assembly_valheim")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyCompany("EarthWright")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("EarthWright: terraforming for Valheim's hoe and cultivator")]
[assembly: AssemblyFileVersion("0.3.0.0")]
[assembly: AssemblyInformationalVersion("0.3.0+510b1f4e6784454cfcb857453c43a16d18a641aa")]
[assembly: AssemblyProduct("EarthWright")]
[assembly: AssemblyTitle("EarthWright")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.3.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

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

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

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[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 EarthWright
{
	[BepInPlugin("milkyteam.earthwright", "EarthWright", "0.3.0")]
	public class Plugin : BaseUnityPlugin
	{
		public const string PluginGuid = "milkyteam.earthwright";

		public const string PluginName = "EarthWright";

		public const string PluginVersion = "0.3.0";

		public static ManualLogSource Log { get; private set; }

		public static SyncedConfiguration Synced { get; private set; }

		public static Plugin Instance { get; private set; }

		private void Awake()
		{
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Expected O, but got Unknown
			Instance = this;
			Log = ((BaseUnityPlugin)this).Logger;
			Synced = new SyncedConfiguration((BaseUnityPlugin)(object)this, ((BaseUnityPlugin)this).Logger, "EarthWright", "0.3.0");
			InitializeModules();
			WireModules();
			Harmony harmony = new Harmony("milkyteam.earthwright");
			int num = PatchEverything(harmony);
			Synced.Finish(harmony);
			Log.LogInfo((object)("Loading [EarthWright 0.3.0]" + ((num > 0) ? $" with {num} failed patches, see above" : "")));
			Guard.Install(harmony, ((BaseUnityPlugin)this).Logger, Assembly.GetExecutingAssembly());
		}

		private static void InitializeModules()
		{
			CoreModule.Initialize(Synced);
			TerrainModule.Initialize(Synced);
			ActionsModule.Initialize(Synced);
			BrushModule.Initialize(Synced);
			PreviewModule.Initialize(Synced);
			PathsModule.Initialize(Synced);
			HistoryModule.Initialize(Synced);
			ProtectionModule.Initialize(Synced);
			CostsModule.Initialize(Synced);
			ClearingModule.Initialize(Synced);
			MenuModule.Initialize(Synced);
			GearModule.Initialize(Synced);
			ExtrasModule.Initialize(Synced);
		}

		private static void WireModules()
		{
			MenuHooks.ClearingEnabled = () => ClearingSettings.Enabled;
		}

		private void Update()
		{
			Synced.YamlEditor.Update();
			EarthWright.Core.Ticker.Tick();
		}

		private void OnGUI()
		{
			Synced.YamlEditor.OnGUI();
			EarthWright.Core.Ticker.Gui();
		}

		private static int PatchEverything(Harmony harmony)
		{
			int num = 0;
			Type[] typesFromAssembly = AccessTools.GetTypesFromAssembly(Assembly.GetExecutingAssembly());
			foreach (Type type in typesFromAssembly)
			{
				if (type.GetCustomAttributes(typeof(HarmonyPatch), inherit: true).Length != 0)
				{
					try
					{
						harmony.CreateClassProcessor(type).Patch();
					}
					catch (Exception ex)
					{
						num++;
						Log.LogError((object)("Patch " + type.FullName + " failed to apply: " + ex.Message));
					}
				}
			}
			return num;
		}
	}
}
namespace EarthWright.Terrain
{
	public sealed class BrushStroke
	{
		public Vector3 Center;

		public BrushShape Shape;

		public float Radius = 2f;

		public float Radius2;

		public float Rotation;

		public float Hardness = 0.5f;

		public HeightOp Height;

		public LevelStyle Style;

		public float Target;

		public float Amount = 1f;

		public float MaxStep = 1f;

		public float Strength = 0.5f;

		public PaintOp Paint;

		public float PaintRadius;

		public float PaintStrength = 1f;

		public float Density = 1f;

		public float BandMin;

		public float BandMax;

		public float RandomShare = 1f;

		public int Seed;

		public float EffectivePaintRadius
		{
			get
			{
				if (!(PaintRadius > 0f))
				{
					return Radius;
				}
				return PaintRadius;
			}
		}

		public float Reach
		{
			get
			{
				float num = Mathf.Max(Mathf.Max(Radius, Radius2), EffectivePaintRadius);
				return ((Shape == BrushShape.Square || Shape == BrushShape.Rectangle || Shape == BrushShape.Frame) ? (num * 1.415f) : num) + 1.5f;
			}
		}
	}
	public static class Dispatcher
	{
		public static bool Submit(TerrainEdit edit, bool quiet = false)
		{
			if (edit == null)
			{
				return false;
			}
			EditEvents.RaiseBuilding(edit);
			string text = EditGuards.CheckSender(edit);
			if (!string.IsNullOrEmpty(text))
			{
				if (!quiet)
				{
					Messages.Center(text);
				}
				return false;
			}
			SendChecked(edit);
			return true;
		}

		public static string Check(TerrainEdit edit)
		{
			if (edit == null)
			{
				return "";
			}
			EditEvents.RaiseBuilding(edit);
			return EditGuards.CheckSender(edit);
		}

		public static void SendChecked(TerrainEdit edit)
		{
			EditEvents.RaiseBeforeSend(edit);
			Route(edit);
			EditEvents.RaiseSent(edit);
		}

		private static void Route(TerrainEdit edit)
		{
			List<TerrainComp> list = Compilers(edit);
			if (GeneralSettings.DebugLog.Value)
			{
				Plugin.Log.LogInfo((object)$"Edit {edit.Source} ({edit.Kind}, {edit.Flags}) to {list.Count} terrain compilers");
			}
			if (edit.Has(EditFlags.Privileged))
			{
				ServerRelay.Send(edit, list);
				return;
			}
			foreach (TerrainComp item in list)
			{
				TerrainEdit terrainEdit = PartFor(item, edit);
				if (terrainEdit != null)
				{
					item.m_nview.InvokeRPC("EW_TerrainEdit", new object[1] { EditWire.Write(terrainEdit) });
				}
			}
		}

		public static List<TerrainComp> Compilers(TerrainEdit edit)
		{
			List<TerrainComp> list = new List<TerrainComp>();
			foreach (Heightmap item in Maps(edit))
			{
				TerrainComp andCreateTerrainCompiler = item.GetAndCreateTerrainCompiler();
				if ((Object)(object)andCreateTerrainCompiler != (Object)null && (Object)(object)andCreateTerrainCompiler.m_nview != (Object)null && andCreateTerrainCompiler.m_nview.IsValid())
				{
					list.Add(andCreateTerrainCompiler);
				}
			}
			return list;
		}

		private static List<Heightmap> Maps(TerrainEdit edit)
		{
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			List<Heightmap> list = new List<Heightmap>();
			if (edit.Kind == EditKind.Vertices && edit.Vertices.Mode == VertexMode.Restore)
			{
				Heightmap val = Heightmap.FindHeightmap(edit.Vertices.CompPosition);
				if ((Object)(object)val != (Object)null)
				{
					list.Add(val);
				}
			}
			else if (edit.Kind == EditKind.Vertices)
			{
				TargetMaps(edit.Vertices, list);
			}
			else
			{
				edit.GetArea(out var center, out var radius);
				Heightmap.FindHeightmap(center, radius, list);
			}
			return list;
		}

		private static void TargetMaps(VertexSet set, List<Heightmap> maps)
		{
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			List<Heightmap> list = new List<Heightmap>();
			foreach (TargetVertex target in set.Targets)
			{
				list.Clear();
				Heightmap.FindHeightmap(new Vector3((float)target.X, 0f, (float)target.Z), 0f, list);
				foreach (Heightmap item in list)
				{
					if (!maps.Contains(item))
					{
						maps.Add(item);
					}
				}
			}
		}

		public static TerrainEdit PartFor(TerrainComp comp, TerrainEdit edit)
		{
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			if (edit.Kind != EditKind.Vertices || edit.Vertices.Mode != VertexMode.Targets)
			{
				return edit;
			}
			VertexSet vertexSet = new VertexSet
			{
				Mode = VertexMode.Targets
			};
			foreach (TargetVertex target in edit.Vertices.Targets)
			{
				if (comp.m_hmap.IsPointInside(new Vector3((float)target.X, 0f, (float)target.Z), 0f))
				{
					vertexSet.Targets.Add(target);
				}
			}
			if (vertexSet.Targets.Count == 0)
			{
				return null;
			}
			return new TerrainEdit
			{
				Kind = EditKind.Vertices,
				Flags = edit.Flags,
				SenderPlayer = edit.SenderPlayer,
				SenderPeer = edit.SenderPeer,
				Source = edit.Source,
				Vertices = vertexSet
			};
		}
	}
	public sealed class GuardContext
	{
		public TerrainEdit Edit;

		public long SenderPeer;

		public TerrainComp Comp;

		public bool ServerApproved;

		public bool OnOwner => (Object)(object)Comp != (Object)null;
	}
	public static class EditGuards
	{
		private static readonly List<(string Name, Func<GuardContext, string> Check)> sender = new List<(string, Func<GuardContext, string>)>();

		private static readonly List<(string Name, Func<GuardContext, string> Check)> owner = new List<(string, Func<GuardContext, string>)>();

		public static void AddSender(string name, Func<GuardContext, string> check)
		{
			sender.Add((name, check));
		}

		public static void AddOwner(string name, Func<GuardContext, string> check)
		{
			owner.Add((name, check));
		}

		public static string CheckSender(TerrainEdit edit)
		{
			return FirstRefusal(sender, new GuardContext
			{
				Edit = edit
			});
		}

		public static string CheckOwner(GuardContext context)
		{
			return FirstRefusal(owner, context);
		}

		private static string FirstRefusal(List<(string Name, Func<GuardContext, string> Check)> guards, GuardContext context)
		{
			foreach (var guard in guards)
			{
				string text = Safe.Call(guard.Name, () => guard.Check(context), null);
				if (!string.IsNullOrEmpty(text))
				{
					return text;
				}
			}
			return null;
		}
	}
	public static class EditEvents
	{
		public static event Action<TerrainEdit> Building;

		public static event Action<TerrainEdit> BeforeSend;

		public static event Action<TerrainEdit> Sent;

		public static event Action<TerrainComp, TerrainEdit> Applied;

		internal static void RaiseBuilding(TerrainEdit edit)
		{
			Safe.Run("EditEvents.Building", delegate
			{
				EditEvents.Building?.Invoke(edit);
			});
		}

		internal static void RaiseBeforeSend(TerrainEdit edit)
		{
			Safe.Run("EditEvents.BeforeSend", delegate
			{
				EditEvents.BeforeSend?.Invoke(edit);
			});
		}

		internal static void RaiseSent(TerrainEdit edit)
		{
			Safe.Run("EditEvents.Sent", delegate
			{
				EditEvents.Sent?.Invoke(edit);
			});
		}

		internal static void RaiseApplied(TerrainComp comp, TerrainEdit edit)
		{
			Safe.Run("EditEvents.Applied", delegate
			{
				EditEvents.Applied?.Invoke(comp, edit);
			});
		}
	}
	public static class EditWire
	{
		public const byte Version = 1;

		public static ZPackage Write(TerrainEdit edit)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Expected O, but got Unknown
			ZPackage val = new ZPackage();
			val.Write((byte)1);
			val.Write((byte)edit.Kind);
			val.Write((int)edit.Flags);
			val.Write(edit.SenderPlayer);
			val.Write(edit.SenderPeer);
			val.Write(edit.Source ?? "");
			if (edit.Kind == EditKind.Stroke)
			{
				StrokeWire.Write(val, edit.Stroke);
			}
			else
			{
				VertexWire.Write(val, edit.Vertices);
			}
			return val;
		}

		public static TerrainEdit Read(ZPackage pkg)
		{
			try
			{
				byte b = pkg.ReadByte();
				if (b != 1)
				{
					Plugin.Log.LogWarning((object)$"Terrain edit of wire version {b} ignored (this is {(byte)1}); is every player on the same EarthWright?");
					return null;
				}
				return ReadBody(pkg);
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Unreadable terrain edit ignored: " + ex.Message));
				return null;
			}
		}

		private static TerrainEdit ReadBody(ZPackage pkg)
		{
			TerrainEdit terrainEdit = new TerrainEdit
			{
				Kind = (EditKind)pkg.ReadByte(),
				Flags = (EditFlags)pkg.ReadInt(),
				SenderPlayer = pkg.ReadLong(),
				SenderPeer = pkg.ReadLong(),
				Source = pkg.ReadString()
			};
			if (terrainEdit.Kind == EditKind.Stroke)
			{
				terrainEdit.Stroke = StrokeWire.Read(pkg);
			}
			else
			{
				terrainEdit.Vertices = VertexWire.Read(pkg);
			}
			return terrainEdit;
		}

		public static TerrainEdit Clone(TerrainEdit edit)
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Expected O, but got Unknown
			ZPackage obj = Write(edit);
			obj.SetPos(0);
			return Read(new ZPackage(obj.GetArray()));
		}
	}
	public static class StrokeWire
	{
		public static void Write(ZPackage pkg, BrushStroke s)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			pkg.Write(s.Center);
			pkg.Write((byte)s.Shape);
			pkg.Write(s.Radius);
			pkg.Write(s.Radius2);
			pkg.Write(s.Rotation);
			pkg.Write(s.Hardness);
			pkg.Write((byte)s.Height);
			pkg.Write((byte)s.Style);
			pkg.Write(s.Target);
			pkg.Write(s.Amount);
			pkg.Write(s.MaxStep);
			pkg.Write(s.Strength);
			WritePaint(pkg, s);
		}

		private static void WritePaint(ZPackage pkg, BrushStroke s)
		{
			pkg.Write((byte)s.Paint);
			pkg.Write(s.PaintRadius);
			pkg.Write(s.PaintStrength);
			pkg.Write(s.Density);
			pkg.Write(s.BandMin);
			pkg.Write(s.BandMax);
			pkg.Write(s.RandomShare);
			pkg.Write(s.Seed);
		}

		public static BrushStroke Read(ZPackage pkg)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			BrushStroke brushStroke = new BrushStroke
			{
				Center = pkg.ReadVector3(),
				Shape = (BrushShape)pkg.ReadByte(),
				Radius = pkg.ReadSingle(),
				Radius2 = pkg.ReadSingle(),
				Rotation = pkg.ReadSingle(),
				Hardness = pkg.ReadSingle(),
				Height = (HeightOp)pkg.ReadByte(),
				Style = (LevelStyle)pkg.ReadByte(),
				Target = pkg.ReadSingle(),
				Amount = pkg.ReadSingle(),
				MaxStep = pkg.ReadSingle(),
				Strength = pkg.ReadSingle()
			};
			ReadPaint(pkg, brushStroke);
			return brushStroke;
		}

		private static void ReadPaint(ZPackage pkg, BrushStroke s)
		{
			s.Paint = (PaintOp)pkg.ReadByte();
			s.PaintRadius = pkg.ReadSingle();
			s.PaintStrength = pkg.ReadSingle();
			s.Density = pkg.ReadSingle();
			s.BandMin = pkg.ReadSingle();
			s.BandMax = pkg.ReadSingle();
			s.RandomShare = pkg.ReadSingle();
			s.Seed = pkg.ReadInt();
		}
	}
	public static class Engine
	{
		public static EditResult Apply(TerrainComp comp, TerrainEdit edit)
		{
			return Safe.Call("EarthWright engine (apply)", () => EngineApply.Run(comp, edit), default(EditResult));
		}

		public static EditEstimate Estimate(TerrainEdit edit, bool withChanges = false)
		{
			return Safe.Call("EarthWright engine (estimate)", () => EngineEstimate.Run(edit, withChanges), new EditEstimate());
		}

		public static float ExtraReach(TerrainEdit edit)
		{
			if (!SlopeRelax.Applies(edit))
			{
				return 0f;
			}
			return SlopeSettings.RadiusValue;
		}
	}
	public struct EditResult
	{
		public bool HeightChanged;

		public bool PaintChanged;

		public bool HitLimit;

		public bool Changed
		{
			get
			{
				if (!HeightChanged)
				{
					return PaintChanged;
				}
				return true;
			}
		}
	}
	public sealed class EditEstimate
	{
		public int Vertices;

		public int PaintCells;

		public int PavedCells;

		public float Raised;

		public float Lowered;

		public bool HitLimit;

		public readonly List<VertexChange> Changes = new List<VertexChange>();
	}
	public struct VertexChange
	{
		public float X;

		public float Z;

		public float Before;

		public float After;

		public bool Limited;
	}
	public static class EngineApply
	{
		private static readonly HeightView view = new HeightView();

		private static readonly ChangeBuffer changes = new ChangeBuffer();

		private static readonly ChangeBuffer relaxed = new ChangeBuffer();

		private static readonly LimitContext limits = new LimitContext();

		public static EditResult Run(TerrainComp comp, TerrainEdit edit)
		{
			if (edit == null || !EngineViews.ForComp(comp, view))
			{
				return default(EditResult);
			}
			view.UnderPiece = EngineViews.PieceProbeFor(edit);
			limits.Reset(edit.Has(EditFlags.IgnoreLimits), edit.Has(EditFlags.Privileged));
			EnginePlanner.Plan(view, edit, limits, changes);
			EnginePlanner.Write(view, changes);
			EditResult result = new EditResult
			{
				HeightChanged = (changes.HeightCount > 0),
				PaintChanged = (changes.PaintCount > 0),
				HitLimit = changes.HitLimit
			};
			if (result.HeightChanged && SlopeRelax.Applies(edit))
			{
				SlopeRelax.Run(view, changes, limits, relaxed);
				EnginePlanner.Write(view, relaxed);
			}
			return result;
		}
	}
	public static class EngineBaseHeights
	{
		private static readonly ConditionalWeakTable<Heightmap, float[]> cache = new ConditionalWeakTable<Heightmap, float[]>();

		internal static void Capture(Heightmap map, List<float> heights)
		{
			if (!cache.TryGetValue(map, out var value) || value.Length != heights.Count)
			{
				if (value != null)
				{
					cache.Remove(map);
				}
				value = new float[heights.Count];
				cache.Add(map, value);
			}
			heights.CopyTo(value);
		}

		internal static float[] For(Heightmap map, TerrainComp comp, ref float[] scratch)
		{
			if (Captured(map, out var stored))
			{
				return stored;
			}
			int count = map.m_heights.Count;
			Ensure(ref scratch, count);
			for (int i = 0; i < count; i++)
			{
				scratch[i] = map.m_heights[i] - comp.m_levelDelta[i] - comp.m_smoothDelta[i];
			}
			return scratch;
		}

		internal static float[] CopyShown(Heightmap map, ref float[] scratch)
		{
			Ensure(ref scratch, map.m_heights.Count);
			map.m_heights.CopyTo(scratch);
			return scratch;
		}

		public static bool TryWorld(Heightmap map, int index, out float height)
		{
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			height = 0f;
			if ((Object)(object)map == (Object)null || index < 0 || index >= map.m_heights.Count)
			{
				return false;
			}
			height = (Captured(map, out var stored) ? stored[index] : Uncaptured(map, index)) + ((Component)map).transform.position.y;
			return true;
		}

		private static bool Captured(Heightmap map, out float[] stored)
		{
			if (cache.TryGetValue(map, out stored))
			{
				return stored.Length == map.m_heights.Count;
			}
			return false;
		}

		private static float Uncaptured(Heightmap map, int index)
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			float num = map.m_heights[index];
			TerrainComp val = TerrainComp.FindTerrainCompiler(((Component)map).transform.position);
			if ((Object)(object)val != (Object)null && val.m_initialized && index < val.m_levelDelta.Length)
			{
				num -= val.m_levelDelta[index] + val.m_smoothDelta[index];
			}
			return num;
		}

		public static float At(Vector3 world, float fallback)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			Heightmap val = Heightmap.FindHeightmap(world);
			if ((Object)(object)val == (Object)null)
			{
				return fallback;
			}
			int num = default(int);
			int num2 = default(int);
			val.WorldToVertex(world, ref num, ref num2);
			int num3 = val.m_width + 1;
			if (num < 0 || num2 < 0 || num >= num3 || num2 >= num3)
			{
				return fallback;
			}
			if (!TryWorld(val, num2 * num3 + num, out var height))
			{
				return fallback;
			}
			return height;
		}

		private static void Ensure(ref float[] scratch, int count)
		{
			if (scratch == null || scratch.Length != count)
			{
				scratch = new float[count];
			}
		}
	}
	public static class EngineCommands
	{
		public static void Register()
		{
			Command.Add("limits", "limits             shows the height limits where you stand", Show);
		}

		private static void Show(ConsoleEventArgs args)
		{
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: 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_0041: 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_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null)
			{
				args.Context.AddString("EarthWright: no player in the world.");
				return;
			}
			Vector3 position = ((Component)localPlayer).transform.position;
			HeightLimits.At(position.x, position.z, out var raise, out var dig);
			string text = (HeightLimits.DigLifted(position) ? " (lifted here)" : "");
			args.Context.AddString($"EarthWright height limits at {position.x:0}, {position.z:0} ({LimitBiomes.At(position)}): raise {raise:0.##} m, dig {dig:0.##} m{text}.");
			args.Context.AddString(string.Format("  Admin edits up to {0:0.##} m; ground is drawn up to {1:0.##} m from its original height; per-biome rules: {2}.", HeightLimits.Admin, HeightLimits.Absolute, HeightLimits.HasBiomeRules ? "yes" : "none"));
			ShowGround(args, position);
		}

		private static void ShowGround(ConsoleEventArgs args, Vector3 pos)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			float num = EngineBaseHeights.At(pos, float.NaN);
			float num2 = TerrainRead.GroundHeight(pos, float.NaN);
			if (!float.IsNaN(num) && !float.IsNaN(num2))
			{
				args.Context.AddString($"  Ground here: {num2:0.##} m, originally {num:0.##} m ({num2 - num:+0.##;-0.##;0} m).");
			}
		}
	}
	public static class EngineEstimate
	{
		private static List<Heightmap> maps;

		private static readonly HeightView view = new HeightView();

		private static readonly ChangeBuffer changes = new ChangeBuffer();

		private static readonly LimitContext limits = new LimitContext();

		private static readonly EngineEstimateSum sum = new EngineEstimateSum();

		public static EditEstimate Run(TerrainEdit edit, bool withChanges)
		{
			EditEstimate editEstimate = new EditEstimate();
			if (edit == null || !FindMaps(edit))
			{
				return editEstimate;
			}
			bool flag = edit.Has(EditFlags.Privileged) && EarthWright.Core.Side.LocalIsAdmin;
			limits.Reset(flag && edit.Has(EditFlags.IgnoreLimits), flag);
			Func<float, float, float, bool> underPiece = EngineViews.PieceProbeFor(edit);
			sum.Begin();
			foreach (Heightmap map in maps)
			{
				if (EngineViews.ForMap(map, view))
				{
					view.UnderPiece = underPiece;
					EnginePlanner.Plan(view, edit, limits, changes);
					sum.Add(view, changes, editEstimate, withChanges);
				}
			}
			return editEstimate;
		}

		private static bool FindMaps(TerrainEdit edit)
		{
			//IL_0042: 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)
			maps = maps ?? new List<Heightmap>();
			maps.Clear();
			if (edit.Kind == EditKind.Vertices && edit.Vertices != null && edit.Vertices.Mode == VertexMode.Restore)
			{
				Heightmap val = Heightmap.FindHeightmap(edit.Vertices.CompPosition);
				if ((Object)(object)val != (Object)null)
				{
					maps.Add(val);
				}
			}
			else if ((edit.Kind == EditKind.Stroke) ? (edit.Stroke != null) : (edit.Vertices != null))
			{
				edit.GetArea(out var center, out var radius);
				Heightmap.FindHeightmap(center, radius, maps);
			}
			return maps.Count > 0;
		}
	}
	public sealed class EngineEstimateSum
	{
		private readonly HashSet<long> edgeVertices = new HashSet<long>();

		private readonly HashSet<long> edgeCells = new HashSet<long>();

		public void Begin()
		{
			edgeVertices.Clear();
			edgeCells.Clear();
		}

		public void Add(HeightView view, ChangeBuffer changes, EditEstimate estimate, bool withChanges)
		{
			estimate.HitLimit |= changes.HitLimit;
			for (int i = 0; i < changes.HeightCount; i++)
			{
				HeightChange c = changes.Heights[i];
				if (Mathf.Abs(c.After - c.Before) >= 0.001f && FirstTime(view, c.Index, edgeVertices))
				{
					AddVertex(view, c, estimate, withChanges);
				}
			}
			for (int j = 0; j < changes.PaintCount; j++)
			{
				if (FirstTime(view, changes.Paints[j].Index, edgeCells))
				{
					AddCell(changes.Paints[j], estimate);
				}
			}
		}

		private static void AddCell(PaintChange c, EditEstimate estimate)
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			estimate.PaintCells++;
			if (c.NewColor.b > 0.5f && c.Before.b <= 0.5f)
			{
				estimate.PavedCells++;
			}
		}

		private static void AddVertex(HeightView view, HeightChange c, EditEstimate estimate, bool withChanges)
		{
			float num = view.Scale * view.Scale;
			estimate.Vertices++;
			estimate.Raised += Mathf.Max(0f, c.After - c.Before) * num;
			estimate.Lowered += Mathf.Max(0f, c.Before - c.After) * num;
			estimate.HitLimit |= c.Limited;
			if (withChanges)
			{
				int x = c.Index % view.Pitch;
				int y = c.Index / view.Pitch;
				estimate.Changes.Add(new VertexChange
				{
					X = view.VertexX(x),
					Z = view.VertexZ(y),
					Before = c.Before + view.OriginY,
					After = c.After + view.OriginY,
					Limited = c.Limited
				});
			}
		}

		private static bool FirstTime(HeightView view, int index, HashSet<long> seen)
		{
			int x = index % view.Pitch;
			int y = index / view.Pitch;
			if (!view.OnEdge(x, y))
			{
				return true;
			}
			long item = ((long)Mathf.RoundToInt(view.VertexX(x)) << 32) ^ (uint)Mathf.RoundToInt(view.VertexZ(y));
			return seen.Add(item);
		}
	}
	public sealed class EngineFilters
	{
		private bool band;

		private float bandMin;

		private float bandMax;

		private bool share;

		private float shareValue;

		private int seed;

		private Func<float, float, float, bool> underPiece;

		public void Configure(TerrainEdit edit, HeightView view)
		{
			BrushStroke stroke = edit.Stroke;
			band = stroke != null && edit.Has(EditFlags.HeightBand);
			if (band)
			{
				bandMin = Mathf.Min(stroke.BandMin, stroke.BandMax);
				bandMax = Mathf.Max(stroke.BandMin, stroke.BandMax);
			}
			shareValue = ((stroke != null) ? Mathf.Clamp01(stroke.RandomShare) : 1f);
			share = shareValue < 1f;
			seed = stroke?.Seed ?? 0;
			underPiece = view.UnderPiece;
		}

		public bool PassVertex(HeightView view, int i, float wx, float wz)
		{
			return Pass(view, i, wx, wz, wx, wz);
		}

		public bool PassCell(HeightView view, int i, float cx, float cz)
		{
			return Pass(view, i, cx, cz, cx - 0.5f * view.Scale, cz - 0.5f * view.Scale);
		}

		private bool Pass(HeightView view, int i, float px, float pz, float vx, float vz)
		{
			if (share && VertexHash.Unit(Mathf.RoundToInt(vx), Mathf.RoundToInt(vz), seed) >= shareValue)
			{
				return false;
			}
			if (!band && underPiece == null)
			{
				return true;
			}
			float num = view.Current(i) + view.OriginY;
			if (band && (num < bandMin || num > bandMax))
			{
				return false;
			}
			if (underPiece != null)
			{
				return !underPiece(px, num, pz);
			}
			return true;
		}
	}
	public sealed class HeightPlan
	{
		public HeightView View;

		public LimitContext Limits;

		public HeightOp Op;

		public LevelStyle Style;

		public float Target;

		public float Amount;

		public float MaxStep;

		public float Strength;

		public float EasePower = 1f;

		public void Configure(HeightView view, LimitContext limits, StrokeParams p)
		{
			View = view;
			Limits = limits;
			Op = p.Stroke.Height;
			Style = p.Stroke.Style;
			Target = p.Stroke.Target - view.OriginY;
			Amount = p.Amount;
			MaxStep = p.MaxStep;
			Strength = p.Strength;
			EasePower = EngineSettings.EasePowerValue;
		}

		public float After(int x, int y, int i, float current, float weight, float wx, float wz)
		{
			switch (Op)
			{
			case HeightOp.Level:
				return Level(current, weight);
			case HeightOp.Raise:
				return current + Amount * weight;
			case HeightOp.Lower:
				return current - Amount * weight;
			case HeightOp.Smooth:
				return SmoothAt(x, y, current, weight);
			case HeightOp.Reset:
				return Toward(current, View.Base[i], weight);
			case HeightOp.SetMin:
				if (!(current < Target))
				{
					return float.NaN;
				}
				return Toward(current, Target, weight);
			case HeightOp.SetMax:
				if (!(current > Target))
				{
					return float.NaN;
				}
				return Toward(current, Target, weight);
			case HeightOp.Offset:
				return Toward(current, View.Base[i] + Amount, weight);
			case HeightOp.RemoveSurface:
				return Toward(current, View.Base[i] - Limits.DigAt(wx, wz), weight);
			default:
				return float.NaN;
			}
		}

		private static float Toward(float current, float target, float weight)
		{
			return current + (target - current) * weight;
		}

		private float Level(float current, float weight)
		{
			float num = Target - current;
			switch (Style)
			{
			case LevelStyle.Instant:
				return current + num * weight;
			case LevelStyle.Step:
				return current + Mathf.Clamp(num, 0f - MaxStep, MaxStep) * weight;
			default:
			{
				float num2 = ((EasePower == 1f) ? weight : Mathf.Pow(weight, EasePower));
				return current + Mathf.Clamp(num * num2, 0f - MaxStep, MaxStep);
			}
			}
		}

		private float SmoothAt(int x, int y, float current, float weight)
		{
			float num = 0f;
			int num2 = 0;
			for (int i = -1; i <= 1; i++)
			{
				for (int j = -1; j <= 1; j++)
				{
					if (j != 0 || i != 0)
					{
						float num3 = View.HeightAt(x + j, y + i);
						if (!float.IsNaN(num3))
						{
							num += num3;
							num2++;
						}
					}
				}
			}
			if (num2 != 0)
			{
				return Toward(current, num / (float)num2, Strength * weight);
			}
			return float.NaN;
		}
	}
	public static class EnginePieceProbe
	{
		private static int mask;

		private static int Mask
		{
			get
			{
				if (mask == 0)
				{
					return mask = LayerMask.GetMask(new string[1] { "piece" });
				}
				return mask;
			}
		}

		public static bool AnyNear(Vector3 center, float radius)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			Vector3 val = default(Vector3);
			((Vector3)(ref val))..ctor(radius, 1000f, radius);
			return Physics.CheckBox(new Vector3(center.x, 0f, center.z), val, Quaternion.identity, Mask, (QueryTriggerInteraction)1);
		}

		public static bool Covered(float x, float ground, float z)
		{
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			return Physics.CheckBox(new Vector3(x, ground + 1.25f, z), new Vector3(0.3f, 1.75f, 0.3f), Quaternion.identity, Mask, (QueryTriggerInteraction)1);
		}
	}
	public static class EnginePlanner
	{
		private static readonly EngineFilters filters = new EngineFilters();

		private static readonly HeightPlan heights = new HeightPlan();

		public static void Plan(HeightView view, TerrainEdit edit, LimitContext limits, ChangeBuffer buffer)
		{
			buffer.Clear();
			if (view != null && edit != null)
			{
				if (edit.Kind == EditKind.Stroke && edit.Stroke != null)
				{
					EngineStrokePlanner.Plan(view, edit, limits, filters, heights, buffer);
				}
				else if (edit.Kind == EditKind.Vertices && edit.Vertices != null)
				{
					EngineVertexPlanner.Plan(view, edit, limits, buffer);
				}
			}
		}

		public static void Write(HeightView view, ChangeBuffer buffer)
		{
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			if (view.Level != null && view.Paint != null)
			{
				for (int i = 0; i < buffer.HeightCount; i++)
				{
					HeightChange heightChange = buffer.Heights[i];
					view.Level[heightChange.Index] = heightChange.NewLevel;
					view.Smooth[heightChange.Index] = heightChange.NewSmooth;
					view.Modified[heightChange.Index] = heightChange.NewModified;
				}
				for (int j = 0; j < buffer.PaintCount; j++)
				{
					PaintChange paintChange = buffer.Paints[j];
					view.Paint[paintChange.Index] = paintChange.NewColor;
					view.PaintModified[paintChange.Index] = paintChange.NewModified;
				}
			}
		}
	}
	public static class EngineRecord
	{
		public const float MinChange = 0.001f;

		public const float MinPaint = 0.002f;

		public static void Height(HeightView view, LimitContext limits, ChangeBuffer buffer, int i, float wx, float wz, float before, float after, bool reset)
		{
			float num = view.Base[i];
			bool limited = false;
			if (!reset)
			{
				after = limits.Clamp(wx, wz, num, before, after, out limited);
			}
			if (limited)
			{
				buffer.HitLimit = true;
			}
			bool flag = reset && Mathf.Abs(after - num) < 0.001f;
			if (!(Mathf.Abs(after - before) < 0.001f) || (flag && view.ModifiedAt(i)))
			{
				buffer.AddHeight(i, before, flag ? num : after, num, limited, flag);
			}
		}

		public static bool Differs(Color a, Color b)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			return Mathf.Abs(a.r - b.r) + Mathf.Abs(a.g - b.g) + Mathf.Abs(a.b - b.b) + Mathf.Abs(a.a - b.a) >= 0.002f;
		}
	}
	public static class EngineSettings
	{
		public const float AdminMaxRadius = 128f;

		public const float AdminMaxAmount = 512f;

		public static ConfigEntry<float> MaxRadius { get; private set; }

		public static ConfigEntry<float> MaxAmount { get; private set; }

		public static ConfigEntry<float> MaxStep { get; private set; }

		public static ConfigEntry<float> EasePower { get; private set; }

		public static ConfigEntry<float> PaintHardness { get; private set; }

		public static float MaxRadiusValue
		{
			get
			{
				if (MaxRadius == null)
				{
					return 100f;
				}
				return MaxRadius.Value;
			}
		}

		public static float MaxAmountValue
		{
			get
			{
				if (MaxAmount == null)
				{
					return 50f;
				}
				return MaxAmount.Value;
			}
		}

		public static float MaxStepValue
		{
			get
			{
				if (MaxStep == null)
				{
					return 1000f;
				}
				return MaxStep.Value;
			}
		}

		public static float EasePowerValue
		{
			get
			{
				if (EasePower == null)
				{
					return 1f;
				}
				return EasePower.Value;
			}
		}

		public static float PaintHardnessValue
		{
			get
			{
				if (PaintHardness == null)
				{
					return 0.8f;
				}
				return PaintHardness.Value;
			}
		}

		public static void Bind(SyncedConfiguration synced)
		{
			BindOperations(synced);
			LimitSettings.Bind(synced);
			SlopeSettings.Bind(synced);
			EngineCommands.Register();
		}

		private static void BindOperations(SyncedConfiguration synced)
		{
			MaxRadius = synced.Bind("3. Operations", "Max Radius", 100f, "The largest brush radius (also ring, frame and rectangle sizes) the owner of the ground accepts from a player, in metres. Larger strokes are cut down to this whatever the player's own settings say. Admin edits the server approved (admin tools, the admin terrain command) may reach 128 m.", true, (AcceptableValueBase?)(object)new AcceptableValueRange<float>(1f, 100f));
			MaxAmount = synced.Bind("3. Operations", "Max Amount", 50f, "The largest raise, lower or offset one stroke may apply, in metres. Admin edits the server approved may apply up to 512 m (the height limits still apply unless they are lifted too).", true, (AcceptableValueBase?)(object)new AcceptableValueRange<float>(0.1f, 512f));
			MaxStep = synced.Bind("3. Operations", "Max Step", 1000f, "The largest height change per point that one Ease or Step levelling stroke may make, in metres. A stroke that asks for no limit gets this one.", true, (AcceptableValueBase?)(object)new AcceptableValueRange<float>(1f, 1000f));
			EasePower = synced.Bind("3. Operations", "Ease Power", 1f, "How the Ease level style fades toward the soft edge of the brush. 1 follows the edge hardness exactly; higher values ease less near the rim, lower values more.", true, (AcceptableValueBase?)(object)new AcceptableValueRange<float>(0.25f, 4f));
			PaintHardness = synced.Bind("3. Operations", "Paint Edge Hardness", 0.8f, "The least edge hardness used for painting (0 to 1), so painted paths and fields keep a crisp edge like the game's own even when the height brush is soft. 0 paints with the brush's own hardness.", true, (AcceptableValueBase?)(object)new AcceptableValueRange<float>(0f, 1f));
		}
	}
	public sealed class StrokeParams
	{
		public BrushStroke Stroke;

		public bool Valid;

		public float Amount;

		public float MaxStep;

		public float Strength;

		public float PaintStrength;

		public float Density;

		public float Share;

		public Footprint HeightPrint;

		public Footprint PaintPrint;

		public static StrokeParams From(TerrainEdit edit, bool admin = false)
		{
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
			StrokeParams strokeParams = new StrokeParams
			{
				Stroke = edit.Stroke
			};
			BrushStroke stroke = edit.Stroke;
			if (stroke == null || !Finite(stroke))
			{
				return strokeParams;
			}
			float num = (admin ? Mathf.Max(EngineSettings.MaxRadiusValue, 128f) : EngineSettings.MaxRadiusValue);
			float num2 = Mathf.Clamp(stroke.Radius, 0.05f, num);
			float num3 = Mathf.Clamp(stroke.Radius2, 0f, num);
			float num4 = Mathf.Clamp(stroke.EffectivePaintRadius, 0.05f, num);
			bool flat = edit.Has(EditFlags.GridAligned);
			strokeParams.HeightPrint = Footprint.Create(stroke.Shape, stroke.Center, num2, num3, stroke.Rotation, stroke.Hardness, flat);
			float hardness = Mathf.Max(Mathf.Clamp01(stroke.Hardness), EngineSettings.PaintHardnessValue);
			strokeParams.PaintPrint = Footprint.Create(stroke.Shape, stroke.Center, num4, num3 * num4 / num2, stroke.Rotation, hardness, flat);
			strokeParams.CapValues(stroke, admin);
			strokeParams.Valid = true;
			return strokeParams;
		}

		private void CapValues(BrushStroke s, bool admin)
		{
			float num = (admin ? Mathf.Max(EngineSettings.MaxAmountValue, 512f) : EngineSettings.MaxAmountValue);
			Amount = ((s.Height == HeightOp.Offset) ? Mathf.Clamp(s.Amount, 0f - num, num) : Mathf.Min(Mathf.Abs(s.Amount), num));
			float maxStepValue = EngineSettings.MaxStepValue;
			MaxStep = ((s.MaxStep > 0f) ? Mathf.Min(s.MaxStep, maxStepValue) : maxStepValue);
			Strength = Mathf.Clamp01(s.Strength);
			PaintStrength = Mathf.Clamp01(s.PaintStrength);
			Density = Mathf.Clamp01(s.Density);
			Share = Mathf.Clamp01(s.RandomShare);
		}

		private static bool Finite(BrushStroke s)
		{
			if (Ok(s.Center.x) && Ok(s.Center.y) && Ok(s.Center.z) && Ok(s.Radius) && Ok(s.Radius2) && Ok(s.Rotation) && Ok(s.Hardness) && Ok(s.Target) && Ok(s.Amount) && Ok(s.MaxStep) && Ok(s.Strength) && Ok(s.PaintRadius) && Ok(s.PaintStrength) && Ok(s.Density) && Ok(s.BandMin) && Ok(s.BandMax))
			{
				return Ok(s.RandomShare);
			}
			return false;
		}

		public static bool Ok(float value)
		{
			if (!float.IsNaN(value))
			{
				return !float.IsInfinity(value);
			}
			return false;
		}
	}
	public static class EngineStrokePlanner
	{
		public static void Plan(HeightView view, TerrainEdit edit, LimitContext limits, EngineFilters filters, HeightPlan heights, ChangeBuffer buffer)
		{
			StrokeParams strokeParams = StrokeParams.From(edit, limits.AdminEdit);
			if (strokeParams.Valid)
			{
				filters.Configure(edit, view);
				if (strokeParams.Stroke.Height != HeightOp.None)
				{
					heights.Configure(view, limits, strokeParams);
					PlanHeights(view, strokeParams, heights, filters, buffer);
				}
				if (strokeParams.Stroke.Paint != PaintOp.None)
				{
					PaintPlanner.Plan(view, edit, strokeParams, filters, buffer);
				}
			}
		}

		private static void PlanHeights(HeightView view, StrokeParams p, HeightPlan plan, EngineFilters filters, ChangeBuffer buffer)
		{
			p.HeightPrint.Extent(out var halfX, out var halfZ);
			view.RangeX(p.HeightPrint.CenterX - halfX, p.HeightPrint.CenterX + halfX, cells: false, out var from, out var to);
			view.RangeZ(p.HeightPrint.CenterZ - halfZ, p.HeightPrint.CenterZ + halfZ, cells: false, out var from2, out var to2);
			for (int i = from2; i <= to2; i++)
			{
				for (int j = from; j <= to; j++)
				{
					PlanVertex(view, p, plan, filters, buffer, j, i);
				}
			}
		}

		private static void PlanVertex(HeightView view, StrokeParams p, HeightPlan plan, EngineFilters filters, ChangeBuffer buffer, int x, int y)
		{
			float num = view.VertexX(x);
			float num2 = view.VertexZ(y);
			float num3 = p.HeightPrint.Weight(num, num2);
			if (num3 <= 0f)
			{
				return;
			}
			int i = y * view.Pitch + x;
			if (filters.PassVertex(view, i, num, num2))
			{
				float num4 = view.Current(i);
				float num5 = plan.After(x, y, i, num4, num3, num, num2);
				if (!float.IsNaN(num5))
				{
					EngineRecord.Height(view, plan.Limits, buffer, i, num, num2, num4, num5, plan.Op == HeightOp.Reset);
				}
			}
		}
	}
	public static class EngineVertexPlanner
	{
		public const int MaxEntries = 16384;

		private static int[] visited = new int[0];

		private static int visit;

		public static void Plan(HeightView view, TerrainEdit edit, LimitContext limits, ChangeBuffer buffer)
		{
			BeginVisits(view.Count);
			if (edit.Vertices.Mode == VertexMode.Restore)
			{
				PlanRestore(view, edit.Vertices, buffer);
			}
			else
			{
				PlanTargets(view, edit.Vertices, limits, buffer);
			}
		}

		private static void PlanTargets(HeightView view, VertexSet set, LimitContext limits, ChangeBuffer buffer)
		{
			int num = Mathf.Min(set.Targets.Count, 16384);
			for (int i = 0; i < num; i++)
			{
				TargetVertex t = set.Targets[i];
				if (view.TryVertex(t.X, t.Z, out var x, out var y))
				{
					int i2 = y * view.Pitch + x;
					if (FirstVisit(i2))
					{
						PlanTarget(view, t, limits, buffer, x, y, i2);
					}
				}
			}
		}

		private static void PlanTarget(HeightView view, TargetVertex t, LimitContext limits, ChangeBuffer buffer, int x, int y, int i)
		{
			float num = view.VertexX(x);
			float num2 = view.VertexZ(y);
			float num3 = view.Current(i);
			if (view.UnderPiece == null || !view.UnderPiece(num, num3 + view.OriginY, num2))
			{
				float num4 = (StrokeParams.Ok(t.Weight) ? Mathf.Clamp01(t.Weight) : 0f);
				if (num4 > 0f && StrokeParams.Ok(t.Height))
				{
					float after = num3 + (t.Height - view.OriginY - num3) * num4;
					EngineRecord.Height(view, limits, buffer, i, num, num2, num3, after, reset: false);
				}
				float num5 = (StrokeParams.Ok(t.PaintStrength) ? Mathf.Clamp01(t.PaintStrength) : 0f);
				if (t.Paint != PaintOp.None && num5 > 0f)
				{
					PaintPlanner.AddCell(view, i, t.Paint, num5, 1f, snow: false, buffer);
				}
			}
		}

		private static void PlanRestore(HeightView view, VertexSet set, ChangeBuffer buffer)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			if (!view.Holds(set.CompPosition))
			{
				return;
			}
			int num = Mathf.Min(set.Raw.Count, 16384);
			for (int i = 0; i < num; i++)
			{
				RawVertex r = set.Raw[i];
				if (r.Index >= 0 && r.Index < view.Count && FirstVisit(r.Index) && Finite(r))
				{
					RestoreHeight(view, r, buffer);
					RestorePaint(view, r, buffer);
				}
			}
		}

		private static void RestoreHeight(HeightView view, RawVertex r, ChangeBuffer buffer)
		{
			int index = r.Index;
			if (r.HeightModified != view.ModifiedAt(index) || r.LevelDelta != view.LevelAt(index) || r.SmoothDelta != view.SmoothAt(index))
			{
				float num = view.Base[index];
				float after = Mathf.Clamp(num + r.LevelDelta + r.SmoothDelta, num - view.Absolute, num + view.Absolute);
				buffer.AddRawHeight(index, view.Current(index), after, r.LevelDelta, r.SmoothDelta, r.HeightModified);
			}
		}

		private static void RestorePaint(HeightView view, RawVertex r, ChangeBuffer buffer)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			Color val = view.PaintAt(r.Index);
			if (r.PaintModified != view.PaintModifiedAt(r.Index) || !(val == r.Paint))
			{
				buffer.AddPaint(r.Index, val, r.Paint, r.PaintModified);
			}
		}

		private static bool Finite(RawVertex r)
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			if (StrokeParams.Ok(r.LevelDelta) && StrokeParams.Ok(r.SmoothDelta) && StrokeParams.Ok(r.Paint.r) && StrokeParams.Ok(r.Paint.g) && StrokeParams.Ok(r.Paint.b))
			{
				return StrokeParams.Ok(r.Paint.a);
			}
			return false;
		}

		private static void BeginVisits(int count)
		{
			if (visited.Length < count)
			{
				visited = new int[count];
			}
			visit++;
			if (visit == int.MaxValue)
			{
				Array.Clear(visited, 0, visited.Length);
				visit = 1;
			}
		}

		private static bool FirstVisit(int i)
		{
			if (visited[i] == visit)
			{
				return false;
			}
			visited[i] = visit;
			return true;
		}
	}
	public static class EngineViews
	{
		private static readonly Func<float, float, float> outside = OutsideHeight;

		private static readonly Func<float, float, float, bool> covered = EnginePieceProbe.Covered;

		public static bool ForComp(TerrainComp comp, HeightView view)
		{
			if ((Object)(object)comp == (Object)null || !comp.m_initialized || !Common(comp.m_hmap, view))
			{
				return false;
			}
			int count = view.Count;
			if (comp.m_levelDelta.Length != count || comp.m_smoothDelta.Length != count || comp.m_modifiedHeight.Length != count || comp.m_paintMask.Length != count || comp.m_modifiedPaint.Length != count)
			{
				return false;
			}
			view.Base = EngineBaseHeights.For(comp.m_hmap, comp, ref view.OwnBase);
			view.Level = comp.m_levelDelta;
			view.Smooth = comp.m_smoothDelta;
			view.Modified = comp.m_modifiedHeight;
			view.Paint = comp.m_paintMask;
			view.PaintModified = comp.m_modifiedPaint;
			return true;
		}

		public static bool ForMap(Heightmap map, HeightView view)
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)map == (Object)null)
			{
				return false;
			}
			TerrainComp val = TerrainComp.FindTerrainCompiler(((Component)map).transform.position);
			if ((Object)(object)val != (Object)null && val.m_initialized && (Object)(object)val.m_hmap == (Object)(object)map)
			{
				return ForComp(val, view);
			}
			if (!Common(map, view))
			{
				return false;
			}
			view.Base = EngineBaseHeights.CopyShown(map, ref view.OwnBase);
			view.Level = null;
			view.Smooth = null;
			view.Modified = null;
			view.Paint = null;
			view.PaintModified = null;
			return true;
		}

		private static bool Common(Heightmap map, HeightView view)
		{
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)map == (Object)null || map.m_buildData == null || map.m_heights.Count != (map.m_width + 1) * (map.m_width + 1))
			{
				return false;
			}
			Vector3 position = ((Component)map).transform.position;
			view.Width = map.m_width;
			view.Scale = map.m_scale;
			view.OriginX = position.x;
			view.OriginY = position.y;
			view.OriginZ = position.z;
			view.BaseMask = map.m_buildData.m_baseMask;
			view.PaintTexture = map.m_paintMask;
			view.Absolute = HeightLimits.Absolute;
			view.OutsideHeight = outside;
			view.UnderPiece = null;
			return true;
		}

		public static Func<float, float, float, bool> PieceProbeFor(TerrainEdit edit)
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			if (edit == null || !edit.Has(EditFlags.SkipUnderPieces))
			{
				return null;
			}
			edit.GetArea(out var center, out var radius);
			if (!EnginePieceProbe.AnyNear(center, radius))
			{
				return null;
			}
			return covered;
		}

		private static float OutsideHeight(float x, float z)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			float result = default(float);
			if (!Heightmap.GetHeight(new Vector3(x, 0f, z), ref result))
			{
				return float.NaN;
			}
			return result;
		}
	}
	public enum BrushShape : byte
	{
		Circle,
		Square,
		Rectangle,
		Ring,
		Frame
	}
	public enum HeightOp : byte
	{
		None,
		Level,
		Raise,
		Lower,
		Smooth,
		Reset,
		SetMin,
		SetMax,
		Offset,
		RemoveSurface
	}
	public enum LevelStyle : byte
	{
		Ease,
		Step,
		Instant
	}
	public enum PaintOp : byte
	{
		None,
		Dirt,
		Cultivated,
		Paved,
		Grass,
		ClearVegetation,
		Vegetation,
		DeepSnow,
		Original
	}
	[Flags]
	public enum EditFlags : ushort
	{
		None = 0,
		Privileged = 1,
		IgnoreLimits = 2,
		GridAligned = 4,
		SkipUnderPieces = 8,
		HeightBand = 0x10,
		PaintHeightCheck = 0x20,
		IsRestore = 0x40,
		FromPlacement = 0x80
	}
	public enum EditKind : byte
	{
		Stroke = 1,
		Vertices
	}
	public enum VertexMode : byte
	{
		Targets = 1,
		Restore
	}
	public static class HeightLimits
	{
		private static readonly List<Func<Vector3, bool>> digExceptions = new List<Func<Vector3, bool>>();

		private static readonly float[] biomeRaise = NaNs();

		private static readonly float[] biomeDig = NaNs();

		private static bool hasBiomeRules;

		private static float biomeMax;

		public static float Absolute => Mathf.Max(Mathf.Max(Mathf.Max(LimitSettings.RaiseValue, LimitSettings.DigValue), LimitSettings.AdminValue), biomeMax);

		public static float Admin => LimitSettings.AdminValue;

		public static bool HasDigExceptions => digExceptions.Count > 0;

		public static bool HasBiomeRules => hasBiomeRules;

		public static float Raise(Vector3 world)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			At(world.x, world.z, out var raise, out var _);
			return raise;
		}

		public static float Dig(Vector3 world)
		{
			//IL_0000: 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_0014: Unknown result type (might be due to invalid IL or missing references)
			if (DigLifted(world))
			{
				return Absolute;
			}
			At(world.x, world.z, out var _, out var dig);
			return dig;
		}

		public static void AddDigException(Func<Vector3, bool> lifts)
		{
			digExceptions.Add(lifts);
		}

		public static bool DigLifted(Vector3 world)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			foreach (Func<Vector3, bool> digException in digExceptions)
			{
				try
				{
					if (digException(world))
					{
						return true;
					}
				}
				catch (Exception ex)
				{
					ManualLogSource log = Plugin.Log;
					if (log != null)
					{
						log.LogError((object)("EarthWright dig exception rule: " + ex));
					}
				}
			}
			return false;
		}

		public static void At(float x, float z, out float raise, out float dig)
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			raise = LimitSettings.RaiseValue;
			dig = LimitSettings.DigValue;
			if (!hasBiomeRules)
			{
				return;
			}
			int num = LimitBiomes.Slot(LimitBiomes.At(x, z));
			if (num >= 0)
			{
				if (!float.IsNaN(biomeRaise[num]))
				{
					raise = biomeRaise[num];
				}
				if (!float.IsNaN(biomeDig[num]))
				{
					dig = biomeDig[num];
				}
			}
		}

		internal static void SetBiomeRules(BiomeLimit[] rules)
		{
			biomeMax = 0f;
			hasBiomeRules = false;
			for (int i = 0; i < 9; i++)
			{
				BiomeLimit biomeLimit = ((i < rules.Length) ? rules[i] : BiomeLimit.None);
				biomeRaise[i] = biomeLimit.Raise;
				biomeDig[i] = biomeLimit.Dig;
				hasBiomeRules |= !float.IsNaN(biomeLimit.Raise) || !float.IsNaN(biomeLimit.Dig);
				biomeMax = Mathf.Max(biomeMax, Mathf.Max(NaNToZero(biomeLimit.Raise), NaNToZero(biomeLimit.Dig)));
			}
		}

		private static float NaNToZero(float value)
		{
			if (!float.IsNaN(value))
			{
				return value;
			}
			return 0f;
		}

		private static float[] NaNs()
		{
			float[] array = new float[9];
			for (int i = 0; i < array.Length; i++)
			{
				array[i] = float.NaN;
			}
			return array;
		}
	}
	public struct BiomeLimit
	{
		public float Raise;

		public float Dig;

		public static readonly BiomeLimit None = new BiomeLimit
		{
			Raise = float.NaN,
			Dig = float.NaN
		};
	}
	public static class LimitBiomes
	{
		public const int Slots = 9;

		public const string Names = "Meadows, BlackForest, Swamp, Mountain, Plains, Mistlands, AshLands, DeepNorth, Ocean";

		private static readonly string[] slotNames = new string[9] { "Meadows", "Swamp", "Mountain", "BlackForest", "Plains", "AshLands", "DeepNorth", "Ocean", "Mistlands" };

		public static bool TryParse(string name, out int slot)
		{
			string b = ((name != null) ? name.Trim() : "");
			for (slot = 0; slot < 9; slot++)
			{
				if (string.Equals(slotNames[slot], b, StringComparison.OrdinalIgnoreCase))
				{
					return true;
				}
			}
			slot = -1;
			return false;
		}

		public static string NameOf(int slot)
		{
			if (slot < 0 || slot >= 9)
			{
				return "?";
			}
			return slotNames[slot];
		}

		public static int Slot(Biome biome)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0003: Invalid comparison between Unknown and I4
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Invalid comparison between Unknown and I4
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Expected I4, but got Unknown
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Invalid comparison between Unknown and I4
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Invalid comparison between Unknown and I4
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Invalid comparison between Unknown and I4
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Invalid comparison between Unknown and I4
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Invalid comparison between Unknown and I4
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Invalid comparison between Unknown and I4
			if ((int)biome <= 16)
			{
				switch (biome - 1)
				{
				default:
					if ((int)biome != 8)
					{
						if ((int)biome != 16)
						{
							break;
						}
						return 4;
					}
					return 3;
				case 0:
					return 0;
				case 1:
					return 1;
				case 3:
					return 2;
				case 2:
					break;
				}
			}
			else if ((int)biome <= 64)
			{
				if ((int)biome == 32)
				{
					return 5;
				}
				if ((int)biome == 64)
				{
					return 6;
				}
			}
			else
			{
				if ((int)biome == 256)
				{
					return 7;
				}
				if ((int)biome == 512)
				{
					return 8;
				}
			}
			return -1;
		}

		public static Biome At(float x, float z)
		{
			//IL_0040: 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)
			WorldGenerator instance = WorldGenerator.instance;
			if (instance == null || instance.m_world == null)
			{
				return (Biome)0;
			}
			BiomeSector biomeSector = instance.GetBiomeSector(x, z, false);
			if (biomeSector != null && biomeSector != BiomeSector.EmptyMeadows && biomeSector != BiomeSector.EmptyBlackForest)
			{
				return biomeSector.Biome;
			}
			return instance.GetBiome(x, z, 0.02f, false);
		}

		public static Biome At(Vector3 world)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			return At(world.x, world.z);
		}
	}
	public sealed class LimitContext
	{
		private bool ignore;

		private bool exceptions;

		private bool perBiome;

		private float raise;

		private float dig;

		private float admin;

		private float absolute;

		public bool AdminEdit { get; private set; }

		public void Reset(bool ignoreLimits, bool adminEdit)
		{
			ignore = ignoreLimits;
			AdminEdit = adminEdit;
			exceptions = HeightLimits.HasDigExceptions;
			perBiome = HeightLimits.HasBiomeRules;
			raise = LimitSettings.RaiseValue;
			dig = LimitSettings.DigValue;
			admin = HeightLimits.Admin;
			absolute = HeightLimits.Absolute;
		}

		public void Range(float wx, float wz, out float up, out float down)
		{
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			up = raise;
			down = dig;
			if (perBiome)
			{
				HeightLimits.At(wx, wz, out up, out down);
			}
			if (exceptions && down < absolute && HeightLimits.DigLifted(new Vector3(wx, 0f, wz)))
			{
				down = absolute;
			}
			if (ignore)
			{
				up = Mathf.Max(up, admin);
				down = Mathf.Max(down, admin);
			}
		}

		public float DigAt(float wx, float wz)
		{
			Range(wx, wz, out var _, out var down);
			return down;
		}

		public float Clamp(float wx, float wz, float baseHeight, float before, float after, out bool limited)
		{
			Range(wx, wz, out var up, out var down);
			float num = Mathf.Min(baseHeight - down, before);
			float num2 = Mathf.Max(baseHeight + up, before);
			float num3 = Mathf.Clamp(after, num, num2);
			limited = Mathf.Abs(num3 - after) > 0.0005f;
			return num3;
		}
	}
	public static class LimitDisplay
	{
		private static bool warned;

		internal static bool TryApply(TerrainComp comp, Texture2D clearedMask, List<float> heights, Heightmap map)
		{
			if (!comp.m_initialized)
			{
				return true;
			}
			int num = comp.m_width + 1;
			int num2 = num * num;
			if (heights == null || heights.Count != num2 || comp.m_levelDelta.Length != num2 || comp.m_smoothDelta.Length != num2)
			{
				return false;
			}
			if ((Object)(object)map != (Object)null)
			{
				EngineBaseHeights.Capture(map, heights);
			}
			ApplyHeights(comp, heights, HeightLimits.Absolute);
			if ((Object)(object)clearedMask != (Object)null && comp.m_modifiedPaint.Length == num2 && comp.m_paintMask.Length == num2)
			{
				ApplyPaint(comp, clearedMask, num);
			}
			return true;
		}

		private static void ApplyHeights(TerrainComp comp, List<float> heights, float absolute)
		{
			int count = heights.Count;
			for (int i = 0; i < count; i++)
			{
				float num = comp.m_levelDelta[i];
				float num2 = comp.m_smoothDelta[i];
				if (num != 0f || num2 != 0f)
				{
					float num3 = heights[i];
					heights[i] = Mathf.Clamp(num3 + num + num2, num3 - absolute, num3 + absolute);
				}
			}
		}

		private static void ApplyPaint(TerrainComp comp, Texture2D clearedMask, int pitch)
		{
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			for (int i = 0; i < pitch; i++)
			{
				for (int j = 0; j < pitch; j++)
				{
					int num = i * pitch + j;
					if (comp.m_modifiedPaint[num])
					{
						clearedMask.SetPixel(j, i, comp.m_paintMask[num]);
					}
				}
			}
		}

		internal static bool AtMaxDepth(Heightmap map, Vector3 worldPos)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			float num = default(float);
			if (!map.GetWorldHeight(worldPos, ref num))
			{
				return false;
			}
			float num2 = EngineBaseHeights.At(worldPos, float.NaN);
			if (float.IsNaN(num2) && !map.GetWorldBaseHeight(worldPos, ref num2))
			{
				return false;
			}
			return Mathf.Max(num2 - num, 0f) >= HeightLimits.Dig(worldPos) - 0.05f;
		}

		internal static void WarnOnce(string what, Exception e)
		{
			if (!warned)
			{
				warned = true;
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogError((object)$"EarthWright {what} failed, the game's own code runs instead: {e}");
				}
			}
		}
	}
	[HarmonyPatch(typeof(TerrainComp), "ApplyToHeightmap")]
	public static class ApplyToHeightmapPatch
	{
		[HarmonyPrefix]
		public static bool Prefix(TerrainComp __instance, Texture2D clearedMask, List<float> heights, Heightmap hm)
		{
			try
			{
				return !LimitDisplay.TryApply(__instance, clearedMask, heights, hm);
			}
			catch (Exception e)
			{
				LimitDisplay.WarnOnce("height display", e);
				return true;
			}
		}
	}
	[HarmonyPatch(typeof(Heightmap), "AtMaxWorldLevelDepth")]
	public static class AtMaxWorldLevelDepthPatch
	{
		[HarmonyPrefix]
		public static bool Prefix(Heightmap __instance, Vector3 worldPos, ref bool __result)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				__result = LimitDisplay.AtMaxDepth(__instance, worldPos);
				return false;
			}
			catch (Exception e)
			{
				LimitDisplay.WarnOnce("dig depth check", e);
				return true;
			}
		}
	}
	public static class LimitGameOps
	{
		internal static void Level(TerrainComp comp, Vector3 worldPos, float radius, bool square)
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			Heightmap hmap = comp.m_hmap;
			int num = default(int);
			int num2 = default(int);
			hmap.WorldToVertex(worldPos, ref num, ref num2);
			float target = worldPos.y - ((Component)comp).transform.position.y;
			float num3 = radius / hmap.m_scale;
			int num4 = Mathf.CeilToInt(num3);
			int num5 = comp.m_width + 1;
			float up = HeightLimits.Raise(worldPos);
			float down = HeightLimits.Dig(worldPos);
			for (int i = num2 - num4; i <= num2 + num4; i++)
			{
				for (int j = num - num4; j <= num + num4; j++)
				{
					if ((square || !(Distance(num, num2, j, i) > num3)) && j >= 0 && i >= 0 && j < num5 && i < num5)
					{
						LevelPoint(comp, j, i, num5, target, up, down);
					}
				}
			}
		}

		private static void LevelPoint(TerrainComp comp, int j, int i, int pitch, float target, float up, float down)
		{
			float height = comp.m_hmap.GetHeight(j, i);
			int num = i * pitch + j;
			float change = target - height + comp.m_smoothDelta[num];
			comp.m_smoothDelta[num] = 0f;
			SetLevel(comp, num, change, up, down);
		}

		internal static void Raise(TerrainComp comp, Vector3 worldPos, float radius, float delta, bool square, float power)
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			Heightmap hmap = comp.m_hmap;
			int num = default(int);
			int num2 = default(int);
			hmap.WorldToVertex(worldPos, ref num, ref num2);
			float centreHeight = worldPos.y - ((Component)comp).transform.position.y;
			float num3 = radius / hmap.m_scale;
			int num4 = Mathf.CeilToInt(num3);
			int num5 = comp.m_width + 1;
			float up = HeightLimits.Raise(worldPos);
			float down = HeightLimits.Dig(worldPos);
			for (int i = num2 - num4; i <= num2 + num4; i++)
			{
				for (int j = num - num4; j <= num + num4; j++)
				{
					if (j >= 0 && i >= 0 && j < num5 && i < num5)
					{
						float num6 = RaiseWeight(square, Distance(num, num2, j, i), num3, power);
						if (num6 >= 0f)
						{
							RaisePoint(comp, j, i, num5, centreHeight, delta, delta * num6, up, down);
						}
					}
				}
			}
		}

		private static float RaiseWeight(bool square, float distance, float reach, float power)
		{
			if (square)
			{
				return 1f;
			}
			if (distance > reach)
			{
				return -1f;
			}
			if (power <= 0f)
			{
				return 1f;
			}
			float num = 1f - distance / reach;
			if (power == 1f)
			{
				return num;
			}
			return Mathf.Pow(num, power);
		}

		private static void RaisePoint(TerrainComp comp, int j, int i, int pitch, float centreHeight, float delta, float add, float up, float down)
		{
			float height = comp.m_hmap.GetHeight(j, i);
			float num = centreHeight + add;
			if (delta < 0f && num > height)
			{
				return;
			}
			if (delta > 0f)
			{
				if (num < height)
				{
					return;
				}
				if (num > height + add)
				{
					num = height + add;
				}
			}
			int num2 = i * pitch + j;
			float change = num - height + comp.m_smoothDelta[num2];
			comp.m_smoothDelta[num2] = 0f;
			SetLevel(comp, num2, change, up, down);
		}

		private static void SetLevel(TerrainComp comp, int index, float change, float up, float down)
		{
			float num = comp.m_levelDelta[index];
			float num2 = num + change;
			comp.m_levelDelta[index] = Mathf.Clamp(num2, Mathf.Min(0f - down, num), Mathf.Max(up, num));
			comp.m_modifiedHeight[index] = true;
		}

		private static float Distance(int x, int y, int j, int i)
		{
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			return Vector2.Distance(new Vector2((float)x, (float)y), new Vector2((float)j, (float)i));
		}
	}
	[HarmonyPatch(typeof(TerrainComp), "LevelTerrain")]
	public static class LevelTerrainPatch
	{
		[HarmonyPrefix]
		public static bool Prefix(TerrainComp __instance, Vector3 worldPos, float radius, bool square)
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)__instance.m_hmap == (Object)null)
			{
				return true;
			}
			try
			{
				LimitGameOps.Level(__instance, worldPos, radius, square);
				return false;
			}
			catch (Exception e)
			{
				LimitDisplay.WarnOnce("level limits", e);
				return false;
			}
		}
	}
	[HarmonyPatch(typeof(TerrainComp), "RaiseTerrain")]
	public static class RaiseTerrainPatch
	{
		[HarmonyPrefix]
		public static bool Prefix(TerrainComp __instance, Vector3 worldPos, float radius, float delta, bool square, float power)
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)__instance.m_hmap == (Object)null)
			{
				return true;
			}
			try
			{
				LimitGameOps.Raise(__instance, worldPos, radius, delta, square, power);
				return false;
			}
			catch (Exception e)
			{
				LimitDisplay.WarnOnce("raise limits", e);
				return false;
			}
		}
	}
	public static class LimitSettings
	{
		public const string DefaultResource = "EarthWright.config.EarthWright.Limits.yml";

		public static ConfigEntry<float> RaiseLimit { get; private set; }

		public static ConfigEntry<float> DigLimit { get; private set; }

		public static ConfigEntry<float> AdminLimit { get; private set; }

		public static YamlFileSet Files { get; private set; }

		public static float RaiseValue
		{
			get
			{
				if (RaiseLimit == null)
				{
					return 8f;
				}
				return RaiseLimit.Value;
			}
		}

		public static float DigValue
		{
			get
			{
				if (DigLimit == null)
				{
					return 8f;
				}
				return DigLimit.Value;
			}
		}

		public static float AdminValue
		{
			get
			{
				if (AdminLimit == null)
				{
					return 256f;
				}
				return AdminLimit.Value;
			}
		}

		public static void Bind(SyncedConfiguration synced)
		{
			RaiseLimit = synced.Bind("6. Height Limits", "Raise Limit", 8f, "How far the ground may be raised above the world's generated height, in metres (the unmodded game allows 8). Repeated raises add up to this. Applies to new edits; ground already higher stays as it is.", true, (AcceptableValueBase?)(object)new AcceptableValueRange<float>(0.5f, 512f));
			DigLimit = synced.Bind("6. Height Limits", "Dig Limit", 8f, "How deep the ground may be dug below the world's generated height, in metres (the unmodded game allows 8). Also where the pickaxe stops dropping stone. Applies to new edits.", true, (AcceptableValueBase?)(object)new AcceptableValueRange<float>(0.5f, 512f));
			AdminLimit = synced.Bind("6. Height Limits", "Admin Limit", 256f, "The most an admin's edit that ignores the limits (admin tools, the limit override key) may raise or dig, in metres.", true, (AcceptableValueBase?)(object)new AcceptableValueRange<float>(0.5f, 512f));
			Files = synced.AddYaml(new YamlFileSet("EarthWright.Limits*.yml", "earthwright_limits", () => new LimitsModel(), ApplyYaml)
			{
				DefaultContent = SyncedConfiguration.EmbeddedResource(typeof(LimitSettings).Assembly, "EarthWright.config.EarthWright.Limits.yml"),
				EditorLabel = () => "Edit height limits per biome"
			});
		}

		private static void ApplyYaml(YamlModel model)
		{
			LimitsModel limitsModel = (LimitsModel)model;
			HeightLimits.SetBiomeRules(limitsModel.Rules);
			Plugin.Log.LogInfo((object)$"EarthWright: height limits per biome applied, {limitsModel.Count} biomes.");
		}
	}
	public sealed class LimitsModel : YamlModel
	{
		public const float Min = 0.5f;

		public const float Max = 512f;

		public BiomeLimit[] Rules { get; } = NewRules();

		public int Count { get; private set; }

		protected override void Read(YamlConfig.YamlNode root)
		{
			YamlConfig.YamlNode yamlNode = root.Get("biomes");
			if (yamlNode.Kind == YamlNodeKind.Map)
			{
				foreach (KeyValuePair<string, YamlConfig.YamlNode> entry in yamlNode.Entries)
				{
					ReadBiome(entry.Key, entry.Value);
				}
			}
			ReadMisplaced(root);
		}

		private void ReadMisplaced(YamlConfig.YamlNode root)
		{
			foreach (KeyValuePair<string, YamlConfig.YamlNode> entry in root.Entries)
			{
				if (!string.Equals(entry.Key, "biomes", StringComparison.OrdinalIgnoreCase))
				{
					if (!LimitBiomes.TryParse(entry.Key, out var _))
					{
						entry.Value.Warn("unknown key");
						continue;
					}
					entry.Value.Warn("this biome sits at the top of the file; indent it two spaces so it is inside 'biomes:' (applied anyway)");
					ReadBiome(entry.Key, entry.Value);
				}
			}
		}

		private void ReadBiome(string name, YamlConfig.YamlNode node)
		{
			if (!LimitBiomes.TryParse(name, out var slot))
			{
				node.Error("'" + name + "' is not a biome; use one of Meadows, BlackForest, Swamp, Mountain, Plains, Mistlands, AshLands, DeepNorth, Ocean");
				return;
			}
			if (node.Kind != YamlNodeKind.Map)
			{
				node.Error("a biome needs its limits as a map, for example { raise: 16, dig: 4 }");
				return;
			}
			if (float.IsNaN(Rules[slot].Raise) && float.IsNaN(Rules[slot].Dig))
			{
				Count++;
			}
			Rules[slot] = new BiomeLimit
			{
				Raise = ReadMetres(node.Get("raise")),
				Dig = ReadMetres(node.Get("dig"))
			};
		}

		private static BiomeLimit[] NewRules()
		{
			BiomeLimit[] array = new BiomeLimit[9];
			for (int i = 0; i < array.Length; i++)
			{
				array[i] = BiomeLimit.None;
			}
			return array;
		}

		private static float ReadMetres(YamlConfig.YamlNode node)
		{
			if (!node.TryFloat(out var result))
			{
				return float.NaN;
			}
			if (result < 0.5f || result > 512f)
			{
				node.Warn($"{result} is outside {0.5f} to {512f} metres; clamped");
				return Mathf.Clamp(result, 0.5f, 512f);
			}
			return result;
		}
	}
	public struct HeightChange
	{
		public int Index;

		public float Before;

		public float After;

		public bool Limited;

		public float NewLevel;

		public float NewSmooth;

		public bool NewModified;
	}
	public struct PaintChange
	{
		public int Index;

		public Color Before;

		public Color NewColor;

		public bool NewModified;
	}
	public sealed class ChangeBuffer
	{
		public HeightChange[] Heights = new HeightChange[4225];

		public int HeightCount;

		public PaintChange[] Paints = new PaintChange[4225];

		public int PaintCount;

		public bool HitLimit;

		public void Clear()
		{
			HeightCount = 0;
			PaintCount = 0;
			HitLimit = false;
		}

		public void AddHeight(int index, float before, float after, float baseHeight, bool limited, bool forget)
		{
			AddRawHeight(index, before, after, forget ? 0f : (after - baseHeight), 0f, !forget);
			Heights[HeightCount - 1].Limited = limited;
		}

		public void AddRawHeight(int index, float before, float after, float level, float smooth, bool modified)
		{
			if (HeightCount == Heights.Length)
			{
				Array.Resize(ref Heights, Heights.Length * 2);
			}
			Heights[HeightCount++] = new HeightChange
			{
				Index = index,
				Before = before,
				After = after,
				NewLevel = level,
				NewSmooth = smooth,
				NewModified = modified
			};
		}

		public void AddPaint(int index, Color before, Color color, bool modified)
		{
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: 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)
			if (PaintCount == Paints.Length)
			{
				Array.Resize(ref Paints, Paints.Length * 2);
			}
			Paints[PaintCount++] = new PaintChange
			{
				Index = index,
				Before = before,
				NewColor = color,
				NewModified = modified
			};
		}
	}
	public struct Footprint
	{
		private const float Inclusive = 0.001f;

		public float CenterX;

		public float CenterZ;

		public BrushShape Shape;

		public float Outer;

		public float Inner;

		public float Hardness;

		public bool Flat;

		private float cos;

		private float sin;

		private float core;

		public static Footprint Create(BrushShape shape, Vector3 center, float outer, float inner, float rotation, float hardness, bool flat)
		{
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			Footprint result = new Footprint
			{
				CenterX = center.x,
				CenterZ = center.z,
				Shape = shape,
				Outer = Mathf.Max(outer, 0.05f),
				Hardness = Mathf.Clamp01(hardness),
				Flat = flat
			};
			float num = ((shape == BrushShape.Square || shape == BrushShape.Rectangle || shape == BrushShape.Frame) ? (rotation * ((float)Math.PI / 180f)) : 0f);
			result.cos = Mathf.Cos(num);
			result.sin = Mathf.Sin(num);
			result.Normalize(inner);
			return result;
		}

		private void Normalize(float inner)
		{
			switch (Shape)
			{
			case BrushShape.Rectangle:
				Inner = ((inner > 0f) ? inner : Outer);
				core = Mathf.Min(Outer, Inner);
				break;
			case BrushShape.Ring:
				Inner = Mathf.Clamp(inner, 0f, Outer - 0.05f);
				core = (Outer - Inner) * 0.5f;
				break;
			case BrushShape.Frame:
				Inner = ((inner > 0f) ? Mathf.Min(inner, Outer) : Outer);
				core = Inner * 0.5f;
				break;
			default:
				Inner = 0f;
				core = Outer;
				break;
			}
			core = Mathf.Max(core, 0.01f);
		}

		public float Weight(float x, float z)
		{
			float num = x - CenterX;
			float num2 = z - CenterZ;
			float u = num * cos - num2 * sin;
			float v = num * sin + num2 * cos;
			float num3 = Depth(u, v);
			if (num3 < -0.001f)
			{
				return NearestPoint(num, num2);
			}
			if (Flat)
			{
				return 1f;
			}
			return Falloff(1f - Mathf.Max(num3, 0f) / core, Hardness);
		}

		private float Depth(float u, float v)
		{
			float num = Mathf.Abs(u);
			float num2 = Mathf.Abs(v);
			switch (Shape)
			{
			case BrushShape.Square:
				return Outer - Mathf.Max(num, num2);
			case BrushShape.Rectangle:
				return Mathf.Min(Outer - num, Inner - num2);
			case BrushShape.Ring:
			{
				float num4 = Mathf.Sqrt(u * u + v * v);
				return Mathf.Min(Outer - num4, num4 - Inner);
			}
			case BrushShape.Frame:
			{
				float num3 = Mathf.Max(num, num2);
				return Mathf.Min(Outer - num3, num3 - (Outer - Inner));
			}
			default:
				return Outer - Mathf.Sqrt(u * u + v * v);
			}
		}

		private float NearestPoint(float dx, float dz)
		{
			if ((Shape != BrushShape.Circle && Shape != BrushShape.Square && Shape != BrushShape.Rectangle) || !(Mathf.Abs(dx) <= 0.5f) || !(Mathf.Abs(dz) <= 0.5f))
			{
				return 0f;
			}
			return 1f;
		}

		public static float Falloff(float t, float hardness)
		{
			if (t <= hardness)
			{
				return 1f;
			}
			if (t >= 1f)
			{
				return 0f;
			}
			float num = (t - hardness) / (1f - hardness);
			return 1f - num * num * (3f - 2f * num);
		}

		public void Extent(out float halfX, out float halfZ)
		{
			float num = Mathf.Abs(cos);
			float num2 = Mathf.Abs(sin);
			switch (Shape)
			{
			case BrushShape.Square:
			case BrushShape.Frame:
				halfX = (halfZ = Outer * (num + num2));
				break;
			case BrushShape.Rectangle:
				halfX = Outer * num + Inner * num2;
				halfZ = Outer * num2 + Inner * num;
				break;
			default:
				halfX = (halfZ = Outer);
				break;
			}
			halfX = Mathf.Max(halfX, 0.5f) + 0.001f;
			halfZ = Mathf.Max(halfZ, 0.5f) + 0.001f;
		}

		public static Vector3 LocalToWorld(Vector3 center, float rotation, float u, float v)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			float num = rotation * ((float)Math.PI / 180f);
			float num2 = Mathf.Cos(num);
			float num3 = Mathf.Sin(num);
			return new Vector3(center.x + u * num2 + v * num3, center.y, center.z - u * num3 + v * num2);
		}
	}
	public sealed class HeightView
	{
		public int Width = 64;

		public float Scale = 1f;

		public float OriginX;

		public float OriginY;

		public float OriginZ;

		public float[] Base;

		public float[] Level;

		public float[] Smooth;

		public bool[] Modified;

		public Color[] Paint;

		public bool[] PaintModified;

		public Color[] BaseMask;

		public Texture2D PaintTexture;

		public float Absolute = 8f;

		public Func<float, float, float> OutsideHeight;

		public Func<float, float, float, bool> UnderPiece;

		public float[] OwnBase;

		public int Pitch => Width + 1;

		public int Count => Pitch * Pitch;

		public float VertexX(int x)
		{
			return OriginX + (float)(x - Width / 2) * Scale;
		}

		public float VertexZ(int y)
		{
			return OriginZ + (float)(y - Width / 2) * Scale;
		}

		public float CellX(int x)
		{
			return VertexX(x) + 0.5f * Scale;
		}

		public float CellZ(int y)
		{
			return VertexZ(y) + 0.5f * Scale;
		}

		public float Current(int i)
		{
			float num = Base[i];
			if (Level == null)
			{
				return num;
			}
			float num2 = Level[i] + Smooth[i];
			if (num2 == 0f)
			{
				return num;
			}
			return Mathf.Clamp(num + num2, num - Absolute, num + Absolute);
		}

		public float HeightAt(int x, int y)
		{
			if (x >= 0 && y >= 0 && x <= Width && y <= Width)
			{
				return Current(y * Pitch + x);
			}
			if (OutsideHeight == null)
			{
				return float.NaN;
			}
			return OutsideHeight(VertexX(x), VertexZ(y)) - OriginY;
		}

		public Color PaintAt(int i)
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			if (Paint != null)
			{
				return Paint[i];
			}
			if (!((Object)(object)PaintTexture != (Object)null))
			{
				return Color.black;
			}
			return PaintTexture.GetPixel(i % Pitch, i / Pitch);
		}

		public bool PaintModifiedAt(int i)
		{
			if (PaintModified != null)
			{
				return PaintModified[i];
			}
			return false;
		}

		public bool ModifiedAt(int i)
		{
			if (Modified != null)
			{
				return Modified[i];
			}
			return false;
		}

		public float LevelAt(int i)
		{
			if (Level == null)
			{
				return 0f;
			}
			return Level[i];
		}

		public float SmoothAt(int i)
		{
			if (Smooth == null)
			{
				return 0f;
			}
			return Smooth[i];
		}

		public Color OriginalAt(int i)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			if (BaseMask == null || i >= BaseMask.Length)
			{
				return PaintAt(i);
			}
			return BaseMask[i];
		}

		public bool TryVertex(float wx, float wz, out int x, out int y)
		{
			x = Mathf.FloorToInt((wx - OriginX) / Scale + 0.5f) + Width / 2;
			y = Mathf.FloorToInt((wz - OriginZ) / Scale + 0.5f) + Width / 2;
			if (x >= 0 && y >= 0 && x <= Width)
			{
				return y <= Width;
			}
			return false;
		}

		public void RangeX(float min, float max, bool cells, out int from, out int to)
		{
			Range(min - OriginX, max - OriginX, cells, out from, out to);
		}

		public void RangeZ(float min, float max, bool cells, out int from, out int to)
		{
			Range(min - OriginZ, max - OriginZ, cells, out from, out to);
		}

		private void Range(float localMin, float localMax, bool cells, out int from, out int to)
		{
			float num = (cells ? 0.5f : 0f);
			from = Mathf.Max(0, Mathf.CeilToInt(localMin / Scale - num) + Width / 2);
			to = Mathf.Min(Width, Mathf.FloorToInt(localMax / Scale - num) + Width / 2);
		}

		public bool Holds(Vector3 world)
		{
			//IL_0015: 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)
			float num = (float)Width * Scale * 0.5f;
			if (Mathf.Abs(world.x - OriginX) < num)
			{
				return Mathf.Abs(world.z - OriginZ) < num;
			}
			return false;
		}

		public bool OnEdge(int x, int y)
		{
			if (x != 0 && y != 0 && x != Width)
			{
				return y == Width;
			}
			return true;
		}
	}
	public static class VertexHash
	{
		public static float Unit(int x, int z, int seed)
		{
			int num = (x * -1918454973) ^ (z * -669632447) ^ (seed * -887442657);
			int num2 = (num ^ (num >>> 13)) * 1540483477;
			int num3 = (num2 ^ (num2 >>> 15)) * 668265261;
			return (float)(uint)((num3 ^ (num3 >>> 16)) & 0xFFFFFF) / 16777216f;
		}
	}
	public static class OwnerHandler
	{
		public const string RpcName = "EW_TerrainEdit";

		internal static void Register(TerrainComp comp)
		{
			if ((Object)(object)comp.m_nview == (Object)null || !comp.m_nview.IsValid() || (Object)(object)comp.m_hmap == (Object)null)
			{
				return;
			}
			comp.m_nview.Register<ZPackage>("EW_TerrainEdit", (Action<long, ZPackage>)delegate(long sender, ZPackage pkg)
			{
				Safe.Run("EarthWright terrain edit", delegate
				{
					Receive(comp, sender, pkg);
				});
			});
		}

		private static void Receive(TerrainComp comp, long sender, ZPackage pkg)
		{
			if ((Object)(object)comp == (Object)null || !comp.m_nview.IsValid() || !comp.m_nview.IsOwner() || !comp.m_initialized)
			{
				return;
			}
			TerrainEdit terrainEdit = EditWire.Read(pkg);
			if (terrainEdit != null)
			{
				bool flag = terrainEdit.Has(EditFlags.Privileged) && EarthWright.Core.Side.IsServerPeer(sender);
				if (!flag)
				{
					terrainEdit.Flags &= ~(EditFlags.Privileged | EditFlags.IgnoreLimits);
				}
				string text = EditGuards.CheckOwner(new GuardContext
				{
					Edit = terrainEdit,
					SenderPeer = sender,
					Comp = comp,
					ServerApproved = flag
				});
				if (!string.IsNullOrEmpty(text))
				{
					Refusals.Send(terrainEdit.SenderPeer, text);
				}
				else
				{
					Apply(comp, terrainEdit);
				}
			}
		}

		public static void Apply(TerrainComp comp, TerrainEdit edit)
		{
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			EditResult editResult = Engine.Apply(comp, edit);
			if (editResult.Changed)
			{
				edit.GetArea(out var center, out var radius);
				comp.m_operations++;
				comp.m_lastOpPoint = center;
				comp.m_lastOpRadius = radius;
				comp.Save(false);
				comp.m_hmap.Poke(1, !editResult.HeightChanged);
				if ((Object)(object)ClutterSystem.instance != (Object)null)
				{
					ClutterSystem.instance.ResetGrass(center, radius);
				}
				if (GeneralSettings.DebugLog.Value)
				{
					Plugin.Log.LogInfo((object)$"Applied {edit.Source} at {center} r{radius:0.0}: height {editResult.HeightChanged}, paint {editResult.PaintChanged}");
				}
				EditEvents.RaiseApplied(comp, edit);
			}
		}
	}
	[HarmonyPatch(typeof(TerrainComp), "Awake")]
	public static class TerrainCompAwakePatch
	{
		[HarmonyPostfix]
		public static void Postfix(TerrainComp __instance)
		{
			OwnerHandler.Register(__instance);
		}
	}
	public static class PaintOps
	{
		public static readonly Color Dirt = new Color(1f, 0f, 0f, 1f);

		public static readonly Color Cultivated = new Color(0f, 1f, 0f, 1f);

		public static readonly Color Paved = new Color(0f, 0f, 1f, 1f);

		public static readonly Color Grass = new Color(0f, 0f, 0f, 1f);

		public static readonly Color DeepSnow = new Color(1f, 1f, 1f, 1f);

		public static bool Blend(PaintOp op, Color from, Color original, float weight, float density, bool snow, out Color to, out bool forget)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_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)
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_0087: 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_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cb: 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_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: 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_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			forget = false;
			to = from;
			switch (op)
			{
			case PaintOp.Dirt:
				to = Rgb(from, Dirt, weight);
				return true;
			case PaintOp.Cultivated:
				to = (snow ? Snow(from, weight) : Rgb(from, Cultivated, weight));
				return true;
			case PaintOp.Paved:
				to = Rgb(from, Paved, weight);
				return true;
			case PaintOp.Grass:
				to = Rgb(from, Grass, weight);
				return true;
			case PaintOp.DeepSnow:
				to = Rgb(from, DeepSnow, weight);
				return true;
			case PaintOp.ClearVegetation:
				to.a = Mathf.Lerp(from.a, 0f, weight);
				return true;
			case PaintOp.Vegetation:
				to.a = Mathf.Lerp(from.a, density, weight);
				return true;
			case PaintOp.Original:
				to = Color.Lerp(from, original, weight);
				forget = weight >= 0.999f;
				return true;
			default:
				return false;
			}
		}

		private static Color Rgb(Color from, Color target, float weight)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			return new Color(Mathf.Lerp(from.r, target.r, weight), Mathf.Lerp(from.g, target.g, weight), Mathf.Lerp(from.b, target.b, weight), from.a);
		}

		private static Color Snow(Color from, float weight)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			Color result = from;
			result.g = Mathf.Clamp01(from.g + weight);
			return result;
		}
	}
	public static class PaintPlanner
	{
		public static void Plan(HeightView view, TerrainEdit edit, StrokeParams p, EngineFilters filters, ChangeBuffer buffer)
		{
			bool snow = p.Stroke.Paint == PaintOp.Cultivated && WorldGenerator.IsDeepnorth(p.Stroke.Center.x, p.Stroke.Center.z);
			bool heightCheck = edit.Has(EditFlags.PaintHeightCheck);
			p.PaintPrint.Extent(out var halfX, out var halfZ);
			view.RangeX(p.PaintPrint.CenterX - halfX, p.PaintPrint.CenterX + halfX, cells: true, out var from, out var to);
			view.RangeZ(p.PaintPrint.CenterZ - halfZ, p.PaintPrint.CenterZ + halfZ, cells: true, out var from2, out var to2);
			for (int i = from2; i <= to2; i++)
			{
				for (int j = from; j <= to; j++)
				{
					PlanCell(view, p, filters, heightCheck, snow, buffer, j, i);
				}
			}
		}

		private static void PlanCell(HeightView view, StrokeParams p, EngineFilters filters, bool heightCheck, bool snow, ChangeBuffer buffer, int x, int y)
		{
			float num = view.CellX(x);
			float num2 = view.CellZ(y);
			float num3 = p.PaintPrint.Weight(num, num2) * p.PaintStrength;
			if (!(num3 <= 0f))
			{
				int i = y * view.Pitch + x;
				if (filters.PassCell(view, i, num, num2) && (!heightCheck || !(view.Current(i) + view.OriginY > p.Stroke.Center.y)))
				{
					AddCell(view, i, p.Stroke.Paint, num3, p.Density, snow, buffer);
				}
			}
		}

		public static void AddCell(HeightView view, int i, PaintOp op, float weight, float density, bool snow, ChangeBuffer buffer)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: 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)
			Color val = view.PaintAt(i);
			if (PaintOps.Blend(op, val, view.OriginalAt(i), weight, density, snow, out var to, out var forget) && !(forget ? (!view.PaintModifiedAt(i)) : (!EngineRecord.Differs(val, to))))
			{
				buffer.AddPaint(i, val, to, !forget);
			}
		}
	}
	public static class Refusals
	{
		public const string RefusedRpc = "EW_EditRefused";

		private static ZRoutedRpc registeredOn;

		private static float lastShown = -10f;

		public static string NotAdmin { get; private set; } = "$ew_refused_notadmin";

		internal static void Initialize()
		{
			NotAdmin = Language.Add("ew_refused_notadmin", "Only an admin can do that");
		}

		public static void EnsureRegistered()
		{
			ZRoutedRpc instance = ZRoutedRpc.instance;
			if (instance != null && registeredOn != instance)
			{
				registeredOn = instance;
				instance.Register<string>("EW_EditRefused", (Action<long, string>)delegate(long sender, string reason)
				{
					Show(reason);
				});
			}
		}

		public static void Send(long peer, string reason)
		{
			if (peer == 0L || peer == ZNet.GetUID() || ZRoutedRpc.instance == null)
			{
				Show(reason);
				return;
			}
			EnsureRegistered();
			ZRoutedRpc.instance.InvokeRoutedRPC(peer, "EW_EditRefused", new object[1] { reason });
		}

		private static void Show(string reason)
		{
			if (!(Time.time - lastShown < 1f))
			{
				lastShown = Time.time;
				Messages.Center(reason);
			}
		}
	}
	public static class ServerRelay
	{
		public const string RelayRpc = "EW_RelayEdit";

		private static ZRoutedRpc registeredOn;

		public static void EnsureRegistered()
		{
			ZRoutedRpc instance = ZRoutedRpc.instance;
			if (instance == null || registeredOn == instance)
			{
				return;
			}
			registeredOn = instance;
			instance.Register<ZPackage>("EW_RelayEdit", (Action<long, ZPackage>)delegate(long sender, ZPackage pkg)
			{
				Safe.Run("EarthWright relay", delegate
				{
					OnRelay(sender, pkg);
				});
			});
		}

		public static void Send(TerrainEdit edit, List<TerrainComp> comps)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Expected O, but got Unknown
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			EnsureRegistered();
			ZPackage val = new ZPackage();
			val.Write(comps.Count);
			foreach (TerrainComp comp in comps)
			{
				val.Write(comp.m_nview.GetZDO().m_uid);
				val.Write(EditWire.Write(Dispatcher.PartFor(comp, edit) ?? edit));
			}
			ZRoutedRpc.instance.InvokeRoutedRPC("EW_RelayEdit", new object[1] { val });
		}

		private static void OnRelay(long sender, ZPackage pkg)
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			if (!EarthWright.Core.Side.IsServer)
			{
				return;
			}
			bool flag = EarthWright.Core.Side.PeerIsAdmin(sender);
			int num = pkg.ReadInt();
			for (int i = 0; i < num; i++)
			{
				ZDOID id = pkg.ReadZDOID();
				ZPackage editPkg = pkg.ReadPackage();
				if (flag)
				{
					Forward(sender, id, editPkg);
				}
			}
			if (!flag)
			{
				Refusals.Send(sender, Refusals.NotAdmin);
			}
		}

		private static void Forward(long sender, ZDOID id, ZPackage editPkg)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result