Decompiled source of TerrainRamp v0.3.1

TerrainRamp.dll

Decompiled a day ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Jotunn.Configs;
using Jotunn.Entities;
using Jotunn.Managers;
using Jotunn.Utils;
using Microsoft.CodeAnalysis;
using UnityEngine;
using UnityEngine.Rendering;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyCompany("TerrainRamp")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.3.1.0")]
[assembly: AssemblyInformationalVersion("0.3.1")]
[assembly: AssemblyProduct("TerrainRamp")]
[assembly: AssemblyTitle("TerrainRamp")]
[assembly: AssemblyVersion("0.3.1.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace OstrixMods.TerrainRamp
{
	internal static class RampAssets
	{
		private const string IconResource = "OstrixMods.TerrainRamp.Resources.terrain-ramp-icon.png";

		public static Sprite LoadIcon(out Texture2D texture)
		{
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: 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_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Expected O, but got Unknown
			//IL_0125: Unknown result type (might be due to invalid IL or missing references)
			//IL_0134: Unknown result type (might be due to invalid IL or missing references)
			texture = null;
			try
			{
				using (Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("OstrixMods.TerrainRamp.Resources.terrain-ramp-icon.png"))
				{
					if (stream == null)
					{
						TerrainRampPlugin.Log.LogError((object)"Missing embedded icon resource OstrixMods.TerrainRamp.Resources.terrain-ramp-icon.png.");
						return null;
					}
					using MemoryStream memoryStream = new MemoryStream();
					stream.CopyTo(memoryStream);
					texture = new Texture2D(2, 2, (TextureFormat)4, false)
					{
						name = "TerrainRamp_IconTexture",
						filterMode = (FilterMode)1,
						wrapMode = (TextureWrapMode)1,
						hideFlags = (HideFlags)61
					};
					MethodInfo method = Type.GetType("UnityEngine.ImageConversion, UnityEngine.ImageConversionModule", throwOnError: true).GetMethod("LoadImage", BindingFlags.Static | BindingFlags.Public, null, new Type[3]
					{
						typeof(Texture2D),
						typeof(byte[]),
						typeof(bool)
					}, null);
					if (method == null || !(bool)method.Invoke(null, new object[3]
					{
						texture,
						memoryStream.ToArray(),
						false
					}))
					{
						Object.Destroy((Object)(object)texture);
						texture = null;
						return null;
					}
				}
				Sprite obj = Sprite.Create(texture, new Rect(0f, 0f, (float)((Texture)texture).width, (float)((Texture)texture).height), new Vector2(0.5f, 0.5f), 100f);
				((Object)obj).name = "TerrainRamp_Icon";
				((Object)obj).hideFlags = (HideFlags)61;
				return obj;
			}
			catch (Exception ex)
			{
				TerrainRampPlugin.Log.LogError((object)"Terrain Ramp could not load its embedded icon.");
				TerrainRampPlugin.Log.LogError((object)ex);
				if (Object.op_Implicit((Object)(object)texture))
				{
					Object.Destroy((Object)(object)texture);
					texture = null;
				}
				return null;
			}
		}
	}
	internal sealed class RampBirdMarker
	{
		private static bool sourceLogged;

		private readonly GameObject root;

		private readonly Transform labelTransform;

		private readonly TextMesh label;

		public RampBirdMarker(string name, Transform parent)
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Expected O, but got Unknown
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Expected O, but got Unknown
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			root = new GameObject(name)
			{
				hideFlags = (HideFlags)61
			};
			root.transform.SetParent(parent, false);
			root.SetActive(false);
			CreateBirdVisual();
			GameObject val = new GameObject("Label")
			{
				hideFlags = (HideFlags)61
			};
			val.transform.SetParent(root.transform, false);
			val.transform.localPosition = new Vector3(0f, 1.45f, 0f);
			labelTransform = val.transform;
			label = val.AddComponent<TextMesh>();
			label.anchor = (TextAnchor)4;
			label.alignment = (TextAlignment)1;
			label.fontSize = 64;
			label.characterSize = 0.065f;
			label.fontStyle = (FontStyle)1;
			label.text = string.Empty;
			MeshRenderer component = val.GetComponent<MeshRenderer>();
			((Renderer)component).shadowCastingMode = (ShadowCastingMode)0;
			((Renderer)component).receiveShadows = false;
			SetLayerRecursively(root, LayerMask.NameToLayer("Ignore Raycast"));
		}

		public void Show(Vector3 position, Vector3 forward, string markerText, Color color)
		{
			//IL_0002: 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_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_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)
			Vector3 forward2 = default(Vector3);
			((Vector3)(ref forward2))..ctor(forward.x, 0f, forward.z);
			if (((Vector3)(ref forward2)).sqrMagnitude < 0.001f)
			{
				forward2 = Vector3.forward;
			}
			root.transform.position = position + Vector3.up * 0.08f;
			root.transform.rotation = Quaternion.LookRotation(((Vector3)(ref forward2)).normalized, Vector3.up);
			label.text = markerText;
			label.color = color;
			root.SetActive(true);
			UpdateBillboard();
		}

		public void Hide()
		{
			if (Object.op_Implicit((Object)(object)root))
			{
				root.SetActive(false);
			}
		}

		public void UpdateBillboard()
		{
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			if (Object.op_Implicit((Object)(object)root) && root.activeSelf && Object.op_Implicit((Object)(object)labelTransform))
			{
				Camera main = Camera.main;
				if (Object.op_Implicit((Object)(object)main))
				{
					labelTransform.rotation = ((Component)main).transform.rotation;
				}
			}
		}

		public void Dispose()
		{
			if (Object.op_Implicit((Object)(object)root))
			{
				Object.Destroy((Object)(object)root);
			}
		}

		private void CreateBirdVisual()
		{
			//IL_0142: Unknown result type (might be due to invalid IL or missing references)
			//IL_0152: Unknown result type (might be due to invalid IL or missing references)
			//IL_0162: Unknown result type (might be due to invalid IL or missing references)
			//IL_016c: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = FindWorldBirdPrefab();
			if (!Object.op_Implicit((Object)(object)val))
			{
				TerrainRampPlugin.Log.LogWarning((object)"The original Valheim Crow/Seagal prefab was not found; showing letter markers only.");
				return;
			}
			GameObject val2 = Object.Instantiate<GameObject>(val, root.transform);
			((Object)val2).name = "OriginalValheimWorldBird_VisualOnly";
			val2.SetActive(false);
			RandomFlyingBird componentInChildren = val2.GetComponentInChildren<RandomFlyingBird>(true);
			GameObject val3 = (Object.op_Implicit((Object)(object)componentInChildren) ? componentInChildren.m_landedModel : null);
			GameObject val4 = (Object.op_Implicit((Object)(object)componentInChildren) ? componentInChildren.m_flyingModel : null);
			MonoBehaviour[] componentsInChildren = val2.GetComponentsInChildren<MonoBehaviour>(true);
			for (int i = 0; i < componentsInChildren.Length; i++)
			{
				Object.DestroyImmediate((Object)(object)componentsInChildren[i]);
			}
			Collider[] componentsInChildren2 = val2.GetComponentsInChildren<Collider>(true);
			for (int i = 0; i < componentsInChildren2.Length; i++)
			{
				Object.DestroyImmediate((Object)(object)componentsInChildren2[i]);
			}
			Rigidbody[] componentsInChildren3 = val2.GetComponentsInChildren<Rigidbody>(true);
			for (int i = 0; i < componentsInChildren3.Length; i++)
			{
				Object.DestroyImmediate((Object)(object)componentsInChildren3[i]);
			}
			AudioSource[] componentsInChildren4 = val2.GetComponentsInChildren<AudioSource>(true);
			for (int i = 0; i < componentsInChildren4.Length; i++)
			{
				Object.DestroyImmediate((Object)(object)componentsInChildren4[i]);
			}
			Renderer[] componentsInChildren5 = val2.GetComponentsInChildren<Renderer>(true);
			foreach (Renderer obj in componentsInChildren5)
			{
				obj.shadowCastingMode = (ShadowCastingMode)1;
				obj.receiveShadows = true;
				obj.enabled = true;
			}
			val2.transform.localPosition = Vector3.zero;
			val2.transform.localRotation = Quaternion.identity;
			val2.transform.localScale = Vector3.one * 0.9f;
			val2.SetActive(true);
			if (Object.op_Implicit((Object)(object)val4) && (Object)(object)val4 != (Object)(object)val3)
			{
				val4.SetActive(false);
			}
			if (Object.op_Implicit((Object)(object)val3))
			{
				val3.SetActive(true);
			}
		}

		private static GameObject FindWorldBirdPrefab()
		{
			GameObject val = PrefabManager.Instance.GetPrefab("Crow") ?? PrefabManager.Instance.GetPrefab("Seagal");
			if (Object.op_Implicit((Object)(object)val) && !sourceLogged)
			{
				sourceLogged = true;
				TerrainRampPlugin.Log.LogInfo((object)("Terrain Ramp markers use original Valheim world-bird prefab '" + ((Object)val).name + "'."));
			}
			return val;
		}

		private static void SetLayerRecursively(GameObject target, int layer)
		{
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			if (layer < 0)
			{
				return;
			}
			target.layer = layer;
			foreach (Transform item in target.transform)
			{
				SetLayerRecursively(((Component)item).gameObject, layer);
			}
		}
	}
	internal enum RampProfile
	{
		Linear,
		LinearJoined,
		SoftEnds,
		Smooth
	}
	internal struct RampRoadWidths
	{
		public readonly double NegativeSide;

		public readonly double PositiveSide;

		public RampRoadWidths(double negativeSide, double positiveSide)
		{
			NegativeSide = negativeSide;
			PositiveSide = positiveSide;
		}
	}
	internal struct RampEndpointPlane
	{
		public readonly double AlongSlope;

		public readonly double CrossSlope;

		public RampEndpointPlane(double alongSlope, double crossSlope)
		{
			AlongSlope = alongSlope;
			CrossSlope = crossSlope;
		}
	}
	internal struct RampPlaneSample
	{
		public readonly double Along;

		public readonly double Cross;

		public readonly double Height;

		public RampPlaneSample(double along, double cross, double height)
		{
			Along = along;
			Cross = cross;
			Height = height;
		}
	}
	internal static class RampPlaneFitting
	{
		public static bool TryFit(IList<RampPlaneSample> samples, out RampEndpointPlane plane)
		{
			plane = default(RampEndpointPlane);
			if (samples == null || samples.Count < 3)
			{
				return false;
			}
			double num = 0.0;
			double num2 = 0.0;
			double num3 = 0.0;
			foreach (RampPlaneSample sample in samples)
			{
				num += sample.Along;
				num2 += sample.Cross;
				num3 += sample.Height;
			}
			num /= (double)samples.Count;
			num2 /= (double)samples.Count;
			num3 /= (double)samples.Count;
			double num4 = 0.0;
			double num5 = 0.0;
			double num6 = 0.0;
			double num7 = 0.0;
			double num8 = 0.0;
			foreach (RampPlaneSample sample2 in samples)
			{
				double num9 = sample2.Along - num;
				double num10 = sample2.Cross - num2;
				double num11 = sample2.Height - num3;
				num4 += num9 * num9;
				num5 += num9 * num10;
				num6 += num10 * num10;
				num7 += num9 * num11;
				num8 += num10 * num11;
			}
			double num12 = num4 * num6 - num5 * num5;
			if (Math.Abs(num12) < 1E-06)
			{
				return false;
			}
			plane = new RampEndpointPlane((num7 * num6 - num8 * num5) / num12, (num8 * num4 - num7 * num5) / num12);
			return true;
		}
	}
	internal static class RampWidthSelection
	{
		public static RampRoadWidths Calculate(double signedCursorDistance, double configuredMinimumWidth, double configuredMaximumWidth, bool oneSided)
		{
			double num = Math.Max(0.1, Math.Min(configuredMinimumWidth, configuredMaximumWidth));
			double num2 = Math.Max(num, Math.Max(configuredMinimumWidth, configuredMaximumWidth));
			double num3 = num * 0.5;
			double value = Math.Abs(signedCursorDistance);
			if (!oneSided)
			{
				double num4 = Clamp(value, num3, num2 * 0.5);
				return new RampRoadWidths(num4, num4);
			}
			double num5 = Clamp(value, num3, num2 - num3);
			if (!(signedCursorDistance < 0.0))
			{
				return new RampRoadWidths(num3, num5);
			}
			return new RampRoadWidths(num5, num3);
		}

		private static double Clamp(double value, double minimum, double maximum)
		{
			return Math.Max(minimum, Math.Min(value, maximum));
		}
	}
	internal sealed class RampDefinition
	{
		public readonly double StartX;

		public readonly double StartY;

		public readonly double StartZ;

		public readonly double EndX;

		public readonly double EndY;

		public readonly double EndZ;

		public readonly double NegativeRoadWidth;

		public readonly double PositiveRoadWidth;

		public readonly double ShoulderWidth;

		public readonly double EndBlendLength;

		public readonly RampProfile Profile;

		public readonly bool UsesEndpointPlanes;

		public readonly RampEndpointPlane StartPlane;

		public readonly RampEndpointPlane EndPlane;

		public readonly double Length;

		public readonly double DirectionX;

		public readonly double DirectionZ;

		public readonly double NormalX;

		public readonly double NormalZ;

		private readonly double minimumEndpointPlaneTransitionLength;

		public double FullRoadWidth => NegativeRoadWidth + PositiveRoadWidth;

		public double MaximumRoadSideWidth => Math.Max(NegativeRoadWidth, PositiveRoadWidth);

		public double MaximumSlopeDegrees
		{
			get
			{
				if (!UsesEndpointPlanes)
				{
					return CalculateMaximumSlopeDegrees(Length, EndY - StartY, Profile, EndBlendLength);
				}
				return CalculateEndpointPlaneMaximumSlopeDegrees();
			}
		}

		public double BoundingRadius => Length * 0.5 + EndBlendLength + MaximumRoadSideWidth + ShoulderWidth + 2.0;

		public RampDefinition(double startX, double startY, double startZ, double endX, double endY, double endZ, double halfWidth, double shoulderWidth, double endBlendLength)
			: this(startX, startY, startZ, endX, endY, endZ, halfWidth, halfWidth, shoulderWidth, endBlendLength, RampProfile.Linear, usesEndpointPlanes: false, default(RampEndpointPlane), default(RampEndpointPlane))
		{
		}

		public RampDefinition(double startX, double startY, double startZ, double endX, double endY, double endZ, double negativeRoadWidth, double positiveRoadWidth, double shoulderWidth, double endBlendLength, RampProfile profile)
			: this(startX, startY, startZ, endX, endY, endZ, negativeRoadWidth, positiveRoadWidth, shoulderWidth, endBlendLength, profile, usesEndpointPlanes: false, default(RampEndpointPlane), default(RampEndpointPlane))
		{
		}

		public RampDefinition(double startX, double startY, double startZ, double endX, double endY, double endZ, double negativeRoadWidth, double positiveRoadWidth, double shoulderWidth, double endBlendLength, RampProfile profile, bool usesEndpointPlanes, RampEndpointPlane startPlane, RampEndpointPlane endPlane, double minimumEndpointPlaneTransitionLength = 2.0)
		{
			double num = endX - startX;
			double num2 = endZ - startZ;
			double num3 = Math.Sqrt(num * num + num2 * num2);
			if (num3 < 0.001)
			{
				throw new ArgumentException("Ramp endpoints must be separated horizontally.");
			}
			if (negativeRoadWidth <= 0.0)
			{
				throw new ArgumentOutOfRangeException("negativeRoadWidth");
			}
			if (positiveRoadWidth <= 0.0)
			{
				throw new ArgumentOutOfRangeException("positiveRoadWidth");
			}
			StartX = startX;
			StartY = startY;
			StartZ = startZ;
			EndX = endX;
			EndY = endY;
			EndZ = endZ;
			NegativeRoadWidth = negativeRoadWidth;
			PositiveRoadWidth = positiveRoadWidth;
			ShoulderWidth = Math.Max(0.0, shoulderWidth);
			EndBlendLength = ((!usesEndpointPlanes && profile == RampProfile.Linear) ? 0.0 : Math.Max(0.0, endBlendLength));
			Profile = profile;
			UsesEndpointPlanes = usesEndpointPlanes;
			StartPlane = startPlane;
			EndPlane = endPlane;
			this.minimumEndpointPlaneTransitionLength = Math.Max(0.001, minimumEndpointPlaneTransitionLength);
			Length = num3;
			DirectionX = num / num3;
			DirectionZ = num2 / num3;
			NormalX = 0.0 - DirectionZ;
			NormalZ = DirectionX;
		}

		public static double CalculateMaximumSlopeDegrees(double horizontalLength, double verticalDelta)
		{
			return CalculateMaximumSlopeDegrees(horizontalLength, verticalDelta, RampProfile.Linear, 0.0);
		}

		public static double CalculateMaximumSlopeDegrees(double horizontalLength, double verticalDelta, RampProfile profile, double transitionLength)
		{
			if (horizontalLength <= 0.001)
			{
				if (!(Math.Abs(verticalDelta) <= 0.001))
				{
					return 90.0;
				}
				return 0.0;
			}
			return Math.Atan(profile switch
			{
				RampProfile.Smooth => 1.5, 
				RampProfile.SoftEnds => (transitionLength > 0.001) ? 1.3333333333333333 : 1.0, 
				_ => 1.0, 
			} * Math.Abs(verticalDelta) / horizontalLength) * 180.0 / Math.PI;
		}

		public RampSample Evaluate(double worldX, double worldZ, double existingHeight)
		{
			double num = worldX - StartX;
			double num2 = worldZ - StartZ;
			double num3 = num * DirectionX + num2 * DirectionZ;
			double num4 = num * NormalX + num2 * NormalZ;
			double num5 = Math.Abs(num4);
			double num6 = ((num4 < 0.0) ? NegativeRoadWidth : PositiveRoadWidth);
			double num7 = ((num5 <= num6) ? 1.0 : ((!(ShoulderWidth <= 0.0) && !(num5 >= num6 + ShoulderWidth)) ? Smooth01((num6 + ShoulderWidth - num5) / ShoulderWidth) : 0.0));
			double num8 = ((num3 < 0.0) ? ((EndBlendLength <= 0.0) ? 0.0 : Smooth01((num3 + EndBlendLength) / EndBlendLength)) : ((!(num3 > Length)) ? 1.0 : ((EndBlendLength <= 0.0) ? 0.0 : Smooth01((Length + EndBlendLength - num3) / EndBlendLength))));
			double num9 = num7 * num8;
			if (num9 <= 0.0001)
			{
				return new RampSample(affectsTerrain: false, existingHeight);
			}
			double normalized = Clamp01(num3 / Length);
			double num10 = (UsesEndpointPlanes ? EvaluateEndpointPlaneHeight(num3, normalized, num4) : (StartY + (EndY - StartY) * EvaluateProfile(normalized)));
			double finalHeight = existingHeight + (num10 - existingHeight) * num9;
			return new RampSample(affectsTerrain: true, finalHeight);
		}

		private double EvaluateEndpointPlaneHeight(double along, double normalized, double signedLateral)
		{
			if (along < 0.0)
			{
				return StartY + along * StartPlane.AlongSlope + signedLateral * StartPlane.CrossSlope;
			}
			if (along > Length)
			{
				return EndY + (along - Length) * EndPlane.AlongSlope + signedLateral * EndPlane.CrossSlope;
			}
			double value = Clamp01(normalized);
			double num = (EndY - StartY) / Length;
			double num2 = StartY + num * along;
			double endpointPlaneTransitionLength = GetEndpointPlaneTransitionLength();
			if (endpointPlaneTransitionLength > 0.0001)
			{
				if (along < endpointPlaneTransitionLength)
				{
					double normalized2 = along / endpointPlaneTransitionLength;
					num2 += (StartPlane.AlongSlope - num) * endpointPlaneTransitionLength * EvaluateEndpointCorrection(normalized2);
				}
				else if (along > Length - endpointPlaneTransitionLength)
				{
					double normalized3 = (Length - along) / endpointPlaneTransitionLength;
					num2 -= (EndPlane.AlongSlope - num) * endpointPlaneTransitionLength * EvaluateEndpointCorrection(normalized3);
				}
			}
			double num3 = StartPlane.CrossSlope + (EndPlane.CrossSlope - StartPlane.CrossSlope) * Smooth01(value);
			return num2 + signedLateral * num3;
		}

		private double CalculateEndpointPlaneMaximumSlopeDegrees()
		{
			double num = 0.0;
			for (int i = 0; i <= 128; i++)
			{
				num = Math.Max(num, CalculateEndpointPlaneGrade((double)i / 128.0));
			}
			double num2 = GetEndpointPlaneTransitionLength() / Length;
			if (num2 > 0.0001)
			{
				for (int j = 0; j <= 64; j++)
				{
					double num3 = (double)j / 64.0;
					num = Math.Max(num, CalculateEndpointPlaneGrade(num2 * num3));
					num = Math.Max(num, CalculateEndpointPlaneGrade(1.0 - num2 * num3));
				}
			}
			return Math.Atan(num) * 180.0 / Math.PI;
		}

		private double CalculateEndpointPlaneGrade(double normalized)
		{
			double num = Clamp01(normalized);
			double centerSlope = EvaluateEndpointPlaneCenterSlope(num);
			double crossSlope = StartPlane.CrossSlope + (EndPlane.CrossSlope - StartPlane.CrossSlope) * Smooth01(num);
			double crossSlopeChange = (EndPlane.CrossSlope - StartPlane.CrossSlope) * 6.0 * num * (1.0 - num) / Length;
			return Math.Max(CalculateSurfaceGrade(centerSlope, crossSlope, crossSlopeChange, 0.0 - NegativeRoadWidth), CalculateSurfaceGrade(centerSlope, crossSlope, crossSlopeChange, PositiveRoadWidth));
		}

		private double EvaluateEndpointPlaneCenterSlope(double normalized)
		{
			double num = Clamp01(normalized) * Length;
			double num2 = (EndY - StartY) / Length;
			double endpointPlaneTransitionLength = GetEndpointPlaneTransitionLength();
			if (num < endpointPlaneTransitionLength)
			{
				return num2 + (StartPlane.AlongSlope - num2) * EvaluateEndpointCorrectionDerivative(num / endpointPlaneTransitionLength);
			}
			if (num > Length - endpointPlaneTransitionLength)
			{
				return num2 + (EndPlane.AlongSlope - num2) * EvaluateEndpointCorrectionDerivative((Length - num) / endpointPlaneTransitionLength);
			}
			return num2;
		}

		private double GetEndpointPlaneTransitionLength()
		{
			return Math.Min(Math.Max(EndBlendLength, minimumEndpointPlaneTransitionLength), Length * 0.5);
		}

		private static double EvaluateEndpointCorrection(double normalized)
		{
			double num = Clamp01(normalized);
			double num2 = num * num * num;
			double num3 = num2 * num;
			double num4 = num3 * num;
			return num - 6.0 * num2 + 8.0 * num3 - 3.0 * num4;
		}

		private static double EvaluateEndpointCorrectionDerivative(double normalized)
		{
			double num = Clamp01(normalized);
			double num2 = num * num;
			double num3 = num2 * num;
			double num4 = num3 * num;
			return 1.0 - 18.0 * num2 + 32.0 * num3 - 15.0 * num4;
		}

		private static double CalculateSurfaceGrade(double centerSlope, double crossSlope, double crossSlopeChange, double lateral)
		{
			double num = centerSlope + lateral * crossSlopeChange;
			return Math.Sqrt(num * num + crossSlope * crossSlope);
		}

		private double EvaluateProfile(double normalized)
		{
			return Profile switch
			{
				RampProfile.SoftEnds => EvaluateSoftEnds(normalized), 
				RampProfile.Smooth => Smooth01(normalized), 
				_ => normalized, 
			};
		}

		private double EvaluateSoftEnds(double normalized)
		{
			double num = Math.Min(0.25, EndBlendLength / Length);
			if (num <= 0.0001)
			{
				return normalized;
			}
			if (normalized < num)
			{
				double num2 = normalized / num;
				return num * (2.0 * num2 * num2 - num2 * num2 * num2);
			}
			if (normalized > 1.0 - num)
			{
				double num3 = (1.0 - normalized) / num;
				return 1.0 - num * (2.0 * num3 * num3 - num3 * num3 * num3);
			}
			return normalized;
		}

		internal static double Smooth01(double value)
		{
			double num = Clamp01(value);
			return num * num * (3.0 - 2.0 * num);
		}

		private static double Clamp01(double value)
		{
			if (value <= 0.0)
			{
				return 0.0;
			}
			if (!(value >= 1.0))
			{
				return value;
			}
			return 1.0;
		}
	}
	internal struct RampSample
	{
		public readonly bool AffectsTerrain;

		public readonly double FinalHeight;

		public RampSample(bool affectsTerrain, double finalHeight)
		{
			AffectsTerrain = affectsTerrain;
			FinalHeight = finalHeight;
		}
	}
	internal struct RampPoint2
	{
		public readonly double X;

		public readonly double Y;

		public RampPoint2(double x, double y)
		{
			X = x;
			Y = y;
		}
	}
	internal static class RampIntersection
	{
		public static bool TriangleIntersectsRectangle(RampPoint2 a, RampPoint2 b, RampPoint2 c, double minimumX, double maximumX, double minimumY, double maximumY)
		{
			if (PointInRectangle(a, minimumX, maximumX, minimumY, maximumY) || PointInRectangle(b, minimumX, maximumX, minimumY, maximumY) || PointInRectangle(c, minimumX, maximumX, minimumY, maximumY))
			{
				return true;
			}
			RampPoint2 point = new RampPoint2(minimumX, minimumY);
			RampPoint2 point2 = new RampPoint2(maximumX, minimumY);
			RampPoint2 point3 = new RampPoint2(minimumX, maximumY);
			RampPoint2 point4 = new RampPoint2(maximumX, maximumY);
			if (PointInTriangle(point, a, b, c) || PointInTriangle(point2, a, b, c) || PointInTriangle(point3, a, b, c) || PointInTriangle(point4, a, b, c))
			{
				return true;
			}
			if (!SegmentIntersectsRectangle(a, b, minimumX, maximumX, minimumY, maximumY) && !SegmentIntersectsRectangle(b, c, minimumX, maximumX, minimumY, maximumY))
			{
				return SegmentIntersectsRectangle(c, a, minimumX, maximumX, minimumY, maximumY);
			}
			return true;
		}

		private static bool PointInRectangle(RampPoint2 point, double minimumX, double maximumX, double minimumY, double maximumY)
		{
			if (point.X >= minimumX && point.X <= maximumX && point.Y >= minimumY)
			{
				return point.Y <= maximumY;
			}
			return false;
		}

		private static bool PointInTriangle(RampPoint2 point, RampPoint2 a, RampPoint2 b, RampPoint2 c)
		{
			double num = Cross(point, a, b);
			double num2 = Cross(point, b, c);
			double num3 = Cross(point, c, a);
			bool flag = num < -1E-06 || num2 < -1E-06 || num3 < -1E-06;
			bool flag2 = num > 1E-06 || num2 > 1E-06 || num3 > 1E-06;
			return !(flag && flag2);
		}

		private static double Cross(RampPoint2 point, RampPoint2 a, RampPoint2 b)
		{
			return (point.X - b.X) * (a.Y - b.Y) - (a.X - b.X) * (point.Y - b.Y);
		}

		private static bool SegmentIntersectsRectangle(RampPoint2 start, RampPoint2 end, double minimumX, double maximumX, double minimumY, double maximumY)
		{
			double minimum = 0.0;
			double maximum = 1.0;
			double num = end.X - start.X;
			double num2 = end.Y - start.Y;
			if (ClipSegment(0.0 - num, start.X - minimumX, ref minimum, ref maximum) && ClipSegment(num, maximumX - start.X, ref minimum, ref maximum) && ClipSegment(0.0 - num2, start.Y - minimumY, ref minimum, ref maximum))
			{
				return ClipSegment(num2, maximumY - start.Y, ref minimum, ref maximum);
			}
			return false;
		}

		private static bool ClipSegment(double direction, double distance, ref double minimum, ref double maximum)
		{
			if (Math.Abs(direction) < 1E-06)
			{
				return distance >= 0.0;
			}
			double num = distance / direction;
			if (direction < 0.0)
			{
				if (num > maximum)
				{
					return false;
				}
				minimum = Math.Max(minimum, num);
			}
			else
			{
				if (num < minimum)
				{
					return false;
				}
				maximum = Math.Min(maximum, num);
			}
			return true;
		}
	}
	internal static class RampLocalization
	{
		private const string Prefix = "terrainramp_";

		private static readonly Dictionary<string, string> English = new Dictionary<string, string>
		{
			{ "piece_name", "Earth Ramp" },
			{ "piece_desc", "A - B - width. Q/E: profile; Ctrl: one side; Alt: fit endpoint terrain planes; Esc: reset." },
			{ "reset", "Ramp points reset." },
			{ "applying", "Applying and synchronizing the ramp..." },
			{ "aim_loaded", "Aim the tool at loaded terrain." },
			{ "click_start", "Click 1/3: set the center-line start." },
			{ "click_end", "Click: set B | {0:F1} m | slope {1:F1}/{2:F1} deg | {3}." },
			{ "click_apply_ready", "Click to apply the ramp." },
			{ "wait", "Wait for the current ramp to finish." },
			{ "off_grid", "The point is outside the loaded terrain grid." },
			{ "start_set", "Start set. Choose the ramp end." },
			{ "same_point", "Start and end must be different terrain-grid points." },
			{ "width_prompt", "Move sideways for width. Hold Alt to fit both endpoint terrain planes." },
			{ "profile_selected", "Profile selected: {0}." },
			{ "profile_linear", "Straight slope" },
			{ "profile_linear_joined", "Straight + soft joins" },
			{ "profile_soft_ends", "Soft ends" },
			{ "profile_smooth", "Full S-curve" },
			{ "terrain_fit_name", "Endpoint terrain fit (Alt)" },
			{ "terrain_fit_unavailable", "Alt fit needs loaded terrain samples on both sides of A and B." },
			{ "grid_enabled", "Technical terrain grid enabled." },
			{ "grid_disabled", "Technical terrain grid hidden; road outlines remain visible." },
			{ "slope_analytic", "Profile slope {0:F1} deg exceeds the {1:F1} deg limit." },
			{ "zones_loaded", "The complete ramp, shoulders and boundary ring must remain in loaded terrain zones." },
			{ "delta_limit", "Target height exceeds Valheim's +/-8 m terrain limit." },
			{ "too_many_vertices", "The ramp affects more than {0} terrain-grid vertices." },
			{ "footprint_budget", "The ramp footprint exceeds the safety work budget for {0} vertices. Increase MaximumVertices to allow a larger operation." },
			{ "no_vertices", "No loaded terrain vertices were found inside the ramp." },
			{ "slope_grid", "Actual terrain-grid slope {0:F1} deg exceeds the {1:F1} deg limit." },
			{ "ward", "Part of the ramp is inside another player's ward." },
			{ "no_build", "Part of the ramp crosses a no-build location." },
			{ "ownership_failed", "Could not acquire ownership of a terrain zone." },
			{ "rollback_ok", " Changes were rolled back." },
			{ "rollback_failed", " Rollback failed; inspect the BepInEx log and the terrain." },
			{ "apply_failed_rolled_back", "TerrainComp apply failed; changes were rolled back." },
			{ "apply_and_rollback_failed", "TerrainComp apply and rollback failed; inspect the BepInEx log and the terrain." },
			{ "apply_success", "Ramp applied: {0} terrain vertices." },
			{ "zone_unloaded", "A terrain zone unloaded during apply." },
			{ "compiler_failed", "Could not access the vanilla TerrainComp." },
			{ "compiler_incompatible", "TerrainComp layout is incompatible with this game version." },
			{ "prepare_failed", "TerrainComp preparation failed; details are in the BepInEx log." },
			{ "ownership_lost", "Terrain-zone ownership was lost before save." },
			{ "save_failed", "TerrainComp save failed; details are in the BepInEx log." },
			{ "hud_title", "EARTH RAMP" },
			{ "hud_header", "{0}  ·  {1}  ·  {2}" },
			{ "hud_stage_start", "1 START" },
			{ "hud_stage_end", "2 END" },
			{ "hud_stage_width", "3 WIDTH" },
			{ "hud_metrics", "Length {0:F1} m | Rise {1:+0.0;-0.0;0.0} m | Width {2:F1} m ({4:F1}+{5:F1}) | Slope {3:F1} deg | {6} vertices" },
			{ "hud_controls", "[Q/E] profile  ·  [Ctrl] one side  ·  [Alt] endpoint planes  ·  [{0}] grid  ·  [Esc] reset" },
			{ "hud_controls_basic", "[{0}] grid  ·  [Esc] reset" }
		};

		private static readonly Dictionary<string, string> Russian = new Dictionary<string, string>
		{
			{ "piece_name", "Земляной пандус" },
			{ "piece_desc", "A — B — ширина. Q/E: профиль; Ctrl: одна сторона; Alt: плоскости рельефа у A/B; Esc: сброс." },
			{ "reset", "Точки пандуса сброшены." },
			{ "applying", "Пандус применяется и синхронизируется..." },
			{ "aim_loaded", "Наведи инструмент на загруженную поверхность земли." },
			{ "click_start", "Клик 1/3: начало центральной линии." },
			{ "click_end", "Клик: поставить B | {0:F1} м | уклон {1:F1}/{2:F1}° | {3}." },
			{ "click_apply_ready", "Клик: применить пандус." },
			{ "wait", "Дождись завершения текущего пандуса." },
			{ "off_grid", "Точка не попала на загруженную terrain-сетку." },
			{ "start_set", "Начало задано. Выбери конец пандуса." },
			{ "same_point", "Начало и конец должны быть разными точками terrain-сетки." },
			{ "width_prompt", "Отведи курсор вбок. Удерживай Alt для подгонки к плоскостям рельефа у A и B." },
			{ "profile_selected", "Выбран профиль: {0}." },
			{ "profile_linear", "Прямой склон" },
			{ "profile_linear_joined", "Прямой + мягкие стыки" },
			{ "profile_soft_ends", "Мягкие концы" },
			{ "profile_smooth", "Полная S-кривая" },
			{ "terrain_fit_name", "По плоскостям рельефа (Alt)" },
			{ "terrain_fit_unavailable", "Для Alt-подгонки нужна загруженная земля с обеих сторон точек A и B." },
			{ "grid_enabled", "Техническая terrain-сетка включена." },
			{ "grid_disabled", "Техническая сетка скрыта; контуры дороги оставлены." },
			{ "slope_analytic", "Уклон профиля {0:F1}° превышает лимит {1:F1}°." },
			{ "zones_loaded", "Весь пандус, откосы и граничное кольцо должны оставаться в загруженных terrain-зонах." },
			{ "delta_limit", "Целевая высота выходит за ванильный предел изменения земли ±8 м." },
			{ "too_many_vertices", "Пандус затрагивает больше {0} вершин terrain-сетки." },
			{ "footprint_budget", "Область пандуса превышает безопасный объём расчёта для лимита {0} вершин. Увеличь MaximumVertices для более крупной операции." },
			{ "no_vertices", "В области пандуса не найдено загруженных вершин terrain." },
			{ "slope_grid", "Фактический уклон terrain-сетки {0:F1}° превышает лимит {1:F1}°." },
			{ "ward", "Часть пандуса находится внутри чужой охранной зоны." },
			{ "no_build", "Часть пандуса пересекает область, где строительство запрещено." },
			{ "ownership_failed", "Не удалось получить ownership одной из terrain-зон." },
			{ "rollback_ok", " Изменения отменены." },
			{ "rollback_failed", " Rollback завершился с ошибкой; проверь BepInEx log и участок земли." },
			{ "apply_failed_rolled_back", "Ошибка применения TerrainComp; изменения отменены." },
			{ "apply_and_rollback_failed", "Ошибка применения и rollback TerrainComp; проверь BepInEx log и участок земли." },
			{ "apply_success", "Пандус применён: {0} вершин terrain." },
			{ "zone_unloaded", "Terrain-зона была выгружена во время применения." },
			{ "compiler_failed", "Не удалось получить ванильный TerrainComp." },
			{ "compiler_incompatible", "Структура TerrainComp несовместима с этой версией игры." },
			{ "prepare_failed", "Ошибка подготовки TerrainComp; подробности записаны в BepInEx log." },
			{ "ownership_lost", "Ownership terrain-зоны потерян до сохранения." },
			{ "save_failed", "Ошибка сохранения TerrainComp; подробности записаны в BepInEx log." },
			{ "hud_title", "ЗЕМЛЯНОЙ ПАНДУС" },
			{ "hud_header", "{0}  ·  {1}  ·  {2}" },
			{ "hud_stage_start", "1 НАЧАЛО" },
			{ "hud_stage_end", "2 КОНЕЦ" },
			{ "hud_stage_width", "3 ШИРИНА" },
			{ "hud_metrics", "Длина {0:F1} м | Перепад {1:+0.0;-0.0;0.0} м | Ширина {2:F1} м ({4:F1}+{5:F1}) | Уклон {3:F1}° | Вершин {6}" },
			{ "hud_controls", "[Q/E] профиль  ·  [Ctrl] одна сторона  ·  [Alt] плоскости A/B  ·  [{0}] сетка  ·  [Esc] сброс" },
			{ "hud_controls_basic", "[{0}] сетка  ·  [Esc] сброс" }
		};

		public static void Register()
		{
			CustomLocalization localization = LocalizationManager.Instance.GetLocalization();
			string text = "English";
			string text2 = "Russian";
			localization.AddTranslation(ref text, PrefixTokens(English));
			localization.AddTranslation(ref text2, PrefixTokens(Russian));
		}

		public static string Token(string key)
		{
			return "$terrainramp_" + key;
		}

		public static string Text(string key, params object[] arguments)
		{
			string value;
			string text = (English.TryGetValue(key, out value) ? value : key);
			string text2 = ((Localization.instance != null) ? Localization.instance.Localize(Token(key)) : text);
			if (arguments != null && arguments.Length != 0)
			{
				return string.Format(text2, arguments);
			}
			return text2;
		}

		public static string ProfileName(RampProfile profile)
		{
			return profile switch
			{
				RampProfile.LinearJoined => Text("profile_linear_joined"), 
				RampProfile.SoftEnds => Text("profile_soft_ends"), 
				RampProfile.Smooth => Text("profile_smooth"), 
				_ => Text("profile_linear"), 
			};
		}

		private static Dictionary<string, string> PrefixTokens(Dictionary<string, string> source)
		{
			Dictionary<string, string> dictionary = new Dictionary<string, string>();
			foreach (KeyValuePair<string, string> item in source)
			{
				dictionary.Add("terrainramp_" + item.Key, item.Value);
			}
			return dictionary;
		}
	}
	internal static class RampOperationApplier
	{
		private sealed class CompilerBatch
		{
			public TerrainComp Compiler;

			public Heightmap Heightmap;

			public int Width;

			public bool[] ModifiedHeight;

			public float[] LevelDelta;

			public float[] SmoothDelta;

			public ZNetView NetView;

			public readonly List<RampVertexEdit> Edits = new List<RampVertexEdit>();

			public readonly List<VertexSnapshot> Snapshots = new List<VertexSnapshot>();

			public int OriginalOperations;

			public Vector3 OriginalLastOpPoint;

			public float OriginalLastOpRadius;

			public bool SnapshotCaptured;
		}

		private struct VertexSnapshot
		{
			public readonly int Index;

			public readonly bool ModifiedHeight;

			public readonly float LevelDelta;

			public readonly float SmoothDelta;

			public VertexSnapshot(int index, bool modifiedHeight, float levelDelta, float smoothDelta)
			{
				Index = index;
				ModifiedHeight = modifiedHeight;
				LevelDelta = levelDelta;
				SmoothDelta = smoothDelta;
			}
		}

		private static readonly FieldInfo WidthField = AccessTools.Field(typeof(TerrainComp), "m_width");

		private static readonly FieldInfo ModifiedHeightField = AccessTools.Field(typeof(TerrainComp), "m_modifiedHeight");

		private static readonly FieldInfo LevelDeltaField = AccessTools.Field(typeof(TerrainComp), "m_levelDelta");

		private static readonly FieldInfo SmoothDeltaField = AccessTools.Field(typeof(TerrainComp), "m_smoothDelta");

		private static readonly FieldInfo OperationsField = AccessTools.Field(typeof(TerrainComp), "m_operations");

		private static readonly FieldInfo LastOpPointField = AccessTools.Field(typeof(TerrainComp), "m_lastOpPoint");

		private static readonly FieldInfo LastOpRadiusField = AccessTools.Field(typeof(TerrainComp), "m_lastOpRadius");

		private static readonly FieldInfo NetViewField = AccessTools.Field(typeof(TerrainComp), "m_nview");

		private static readonly MethodInfo SaveMethod = AccessTools.Method(typeof(TerrainComp), "Save", (Type[])null, (Type[])null);

		public static void Apply(RampBuildPlan plan, Action<bool, string> completed)
		{
			//IL_01c6: Unknown result type (might be due to invalid IL or missing references)
			if (!RampTerrain.CheckBuildAccess(plan, out var reason))
			{
				completed(arg1: false, reason);
				return;
			}
			if (!TryPrepareBatches(plan, out var result, out var error))
			{
				completed(arg1: false, error);
				return;
			}
			try
			{
				foreach (CompilerBatch item in result)
				{
					item.NetView.ClaimOwnership();
				}
				foreach (CompilerBatch item2 in result)
				{
					if (!item2.NetView.IsValid() || !item2.NetView.IsOwner())
					{
						completed(arg1: false, RampLocalization.Text("ownership_failed"));
						return;
					}
					CaptureSnapshot(item2);
				}
				foreach (CompilerBatch item3 in result)
				{
					ApplyBatchHeights(item3);
				}
				foreach (CompilerBatch item4 in result)
				{
					if (!TrySaveBatch(item4, plan.Definition, out var error2))
					{
						string text = (TryRollback(result) ? RampLocalization.Text("rollback_ok") : RampLocalization.Text("rollback_failed"));
						completed(arg1: false, error2 + text);
						return;
					}
				}
			}
			catch (Exception ex)
			{
				TerrainRampPlugin.Log.LogError((object)ex);
				bool flag = TryRollback(result);
				completed(arg1: false, flag ? RampLocalization.Text("apply_failed_rolled_back") : RampLocalization.Text("apply_and_rollback_failed"));
				return;
			}
			if (Object.op_Implicit((Object)(object)ClutterSystem.instance))
			{
				ClutterSystem.instance.ResetGrass(GetRampCenter(plan.Definition), (float)plan.Definition.BoundingRadius);
			}
			completed(arg1: true, RampLocalization.Text("apply_success", plan.Edits.Count));
		}

		private static bool TryPrepareBatches(RampBuildPlan plan, out List<CompilerBatch> result, out string error)
		{
			Dictionary<TerrainComp, CompilerBatch> dictionary = new Dictionary<TerrainComp, CompilerBatch>();
			try
			{
				foreach (RampVertexEdit edit in plan.Edits)
				{
					if (!Object.op_Implicit((Object)(object)edit.Heightmap))
					{
						result = null;
						error = RampLocalization.Text("zone_unloaded");
						return false;
					}
					TerrainComp andCreateTerrainCompiler = edit.Heightmap.GetAndCreateTerrainCompiler();
					if (!Object.op_Implicit((Object)(object)andCreateTerrainCompiler))
					{
						result = null;
						error = RampLocalization.Text("compiler_failed");
						return false;
					}
					if (!dictionary.TryGetValue(andCreateTerrainCompiler, out var value))
					{
						value = CreateBatch(andCreateTerrainCompiler, edit.Heightmap);
						if (value == null)
						{
							result = null;
							error = RampLocalization.Text("compiler_incompatible");
							return false;
						}
						dictionary.Add(andCreateTerrainCompiler, value);
					}
					value.Edits.Add(edit);
				}
				result = new List<CompilerBatch>(dictionary.Values);
				error = string.Empty;
				return true;
			}
			catch (Exception ex)
			{
				TerrainRampPlugin.Log.LogError((object)ex);
				result = null;
				error = RampLocalization.Text("prepare_failed");
				return false;
			}
		}

		private static bool TrySaveBatch(CompilerBatch batch, RampDefinition definition, out string error)
		{
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (!batch.NetView.IsValid() || !batch.NetView.IsOwner())
				{
					error = RampLocalization.Text("ownership_lost");
					return false;
				}
				int num = (int)OperationsField.GetValue(batch.Compiler);
				OperationsField.SetValue(batch.Compiler, num + 1);
				LastOpPointField.SetValue(batch.Compiler, GetRampCenter(definition));
				LastOpRadiusField.SetValue(batch.Compiler, (float)definition.BoundingRadius);
				SaveMethod.Invoke(batch.Compiler, null);
				batch.Heightmap.Poke(false);
				error = string.Empty;
				return true;
			}
			catch (Exception ex)
			{
				TerrainRampPlugin.Log.LogError((object)ex);
				error = RampLocalization.Text("save_failed");
				return false;
			}
		}

		private static CompilerBatch CreateBatch(TerrainComp compiler, Heightmap heightmap)
		{
			int num = (int)WidthField.GetValue(compiler);
			bool[] array = ModifiedHeightField.GetValue(compiler) as bool[];
			float[] array2 = LevelDeltaField.GetValue(compiler) as float[];
			float[] array3 = SmoothDeltaField.GetValue(compiler) as float[];
			object? value = NetViewField.GetValue(compiler);
			ZNetView val = (ZNetView)((value is ZNetView) ? value : null);
			int num2 = (num + 1) * (num + 1);
			if (num != heightmap.m_width || array == null || array2 == null || array3 == null || (Object)(object)val == (Object)null || array.Length != num2 || array2.Length != num2 || array3.Length != num2 || SaveMethod == null || OperationsField == null || LastOpPointField == null || LastOpRadiusField == null)
			{
				return null;
			}
			return new CompilerBatch
			{
				Compiler = compiler,
				Heightmap = heightmap,
				Width = num,
				ModifiedHeight = array,
				LevelDelta = array2,
				SmoothDelta = array3,
				NetView = val
			};
		}

		private static void CaptureSnapshot(CompilerBatch batch)
		{
			//IL_002c: 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)
			batch.OriginalOperations = (int)OperationsField.GetValue(batch.Compiler);
			batch.OriginalLastOpPoint = (Vector3)LastOpPointField.GetValue(batch.Compiler);
			batch.OriginalLastOpRadius = (float)LastOpRadiusField.GetValue(batch.Compiler);
			int num = batch.Width + 1;
			HashSet<int> hashSet = new HashSet<int>();
			foreach (RampVertexEdit edit in batch.Edits)
			{
				int num2 = edit.GridZ * num + edit.GridX;
				if (hashSet.Add(num2))
				{
					batch.Snapshots.Add(new VertexSnapshot(num2, batch.ModifiedHeight[num2], batch.LevelDelta[num2], batch.SmoothDelta[num2]));
				}
			}
			batch.SnapshotCaptured = true;
		}

		private static void ApplyBatchHeights(CompilerBatch batch)
		{
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			int num = batch.Width + 1;
			foreach (RampVertexEdit edit in batch.Edits)
			{
				int num2 = edit.GridZ * num + edit.GridX;
				float height = batch.Heightmap.GetHeight(edit.GridX, edit.GridZ);
				float num3 = edit.TargetHeight - ((Component)batch.Compiler).transform.position.y;
				float num4 = batch.LevelDelta[num2] + batch.SmoothDelta[num2] + num3 - height;
				batch.LevelDelta[num2] = Mathf.Clamp(num4, -8f, 8f);
				batch.SmoothDelta[num2] = 0f;
				batch.ModifiedHeight[num2] = true;
			}
		}

		private static bool TryRollback(List<CompilerBatch> batches)
		{
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			bool result = true;
			foreach (CompilerBatch batch in batches)
			{
				if (!batch.SnapshotCaptured)
				{
					continue;
				}
				try
				{
					foreach (VertexSnapshot snapshot in batch.Snapshots)
					{
						batch.ModifiedHeight[snapshot.Index] = snapshot.ModifiedHeight;
						batch.LevelDelta[snapshot.Index] = snapshot.LevelDelta;
						batch.SmoothDelta[snapshot.Index] = snapshot.SmoothDelta;
					}
					OperationsField.SetValue(batch.Compiler, batch.OriginalOperations);
					LastOpPointField.SetValue(batch.Compiler, batch.OriginalLastOpPoint);
					LastOpRadiusField.SetValue(batch.Compiler, batch.OriginalLastOpRadius);
					SaveMethod.Invoke(batch.Compiler, null);
					batch.Heightmap.Poke(false);
				}
				catch (Exception ex)
				{
					result = false;
					TerrainRampPlugin.Log.LogError((object)"Terrain Ramp rollback failed.");
					TerrainRampPlugin.Log.LogError((object)ex);
				}
			}
			return result;
		}

		private static Vector3 GetRampCenter(RampDefinition definition)
		{
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			return new Vector3((float)((definition.StartX + definition.EndX) * 0.5), (float)((definition.StartY + definition.EndY) * 0.5), (float)((definition.StartZ + definition.EndZ) * 0.5));
		}
	}
	internal sealed class RampPreview
	{
		private struct GridVertexKey
		{
			private readonly int heightmapId;

			private readonly int x;

			private readonly int z;

			public GridVertexKey(Heightmap heightmap, int x, int z)
			{
				heightmapId = (Object.op_Implicit((Object)(object)heightmap) ? ((Object)heightmap).GetInstanceID() : 0);
				this.x = x;
				this.z = z;
			}

			public override bool Equals(object obj)
			{
				if (!(obj is GridVertexKey gridVertexKey))
				{
					return false;
				}
				if (heightmapId == gridVertexKey.heightmapId && x == gridVertexKey.x)
				{
					return z == gridVertexKey.z;
				}
				return false;
			}

			public override int GetHashCode()
			{
				return (((heightmapId * 397) ^ x) * 397) ^ z;
			}
		}

		private struct GridCellKey
		{
			private readonly int heightmapId;

			public readonly Heightmap Heightmap;

			public readonly int X;

			public readonly int Z;

			public GridCellKey(Heightmap heightmap, int x, int z)
			{
				Heightmap = heightmap;
				heightmapId = (Object.op_Implicit((Object)(object)heightmap) ? ((Object)heightmap).GetInstanceID() : 0);
				X = x;
				Z = z;
			}

			public override bool Equals(object obj)
			{
				if (!(obj is GridCellKey gridCellKey))
				{
					return false;
				}
				if (heightmapId == gridCellKey.heightmapId && X == gridCellKey.X)
				{
					return Z == gridCellKey.Z;
				}
				return false;
			}

			public override int GetHashCode()
			{
				return (((heightmapId * 397) ^ X) * 397) ^ Z;
			}
		}

		private readonly GameObject root;

		private readonly Mesh mesh;

		private readonly MeshRenderer gridRenderer;

		private readonly Material material;

		private readonly LineRenderer outerOutline;

		private readonly LineRenderer roadOutline;

		private readonly LineRenderer centerLine;

		private readonly LineRenderer arrowHead;

		private readonly RampBirdMarker markerA;

		private readonly RampBirdMarker markerB;

		private readonly RampBirdMarker markerW;

		public RampPreview()
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Expected O, but got Unknown
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Expected O, but got Unknown
			//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c4: Expected O, but got Unknown
			root = new GameObject("TerrainRamp_Preview");
			((Object)root).hideFlags = (HideFlags)61;
			MeshFilter obj = root.AddComponent<MeshFilter>();
			gridRenderer = root.AddComponent<MeshRenderer>();
			mesh = new Mesh
			{
				name = "TerrainRamp_PreviewGrid"
			};
			mesh.MarkDynamic();
			obj.sharedMesh = mesh;
			Shader val = Shader.Find("Sprites/Default") ?? Shader.Find("Unlit/Color") ?? Shader.Find("Standard");
			if (!Object.op_Implicit((Object)(object)val))
			{
				throw new InvalidOperationException("No compatible preview shader is available.");
			}
			material = new Material(val)
			{
				name = "TerrainRamp_PreviewMaterial",
				hideFlags = (HideFlags)61
			};
			((Renderer)gridRenderer).sharedMaterial = material;
			((Renderer)gridRenderer).shadowCastingMode = (ShadowCastingMode)0;
			((Renderer)gridRenderer).receiveShadows = false;
			outerOutline = CreateLine("TerrainRamp_ShoulderOutline", 0.06f, loop: true);
			roadOutline = CreateLine("TerrainRamp_RoadOutline", 0.11f, loop: true);
			centerLine = CreateLine("TerrainRamp_Direction", 0.075f, loop: false);
			arrowHead = CreateLine("TerrainRamp_ArrowHead", 0.075f, loop: false);
			markerA = new RampBirdMarker("TerrainRamp_MarkerA", root.transform);
			markerB = new RampBirdMarker("TerrainRamp_MarkerB", root.transform);
			markerW = new RampBirdMarker("TerrainRamp_MarkerW", root.transform);
			SetGridVisible(TerrainRampPlugin.ShowTechnicalGrid.Value);
			Hide();
		}

		public void RenderCenterLine(Vector3 start, Vector3 end, bool slopeAllowed)
		{
			//IL_003d: 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_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: 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_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: 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_0098: Unknown result type (might be due to invalid IL or missing references)
			mesh.Clear();
			Color color = (slopeAllowed ? new Color(0.15f, 1f, 0.25f, 0.95f) : new Color(1f, 0.12f, 0.08f, 0.95f));
			SetColor(color);
			outerOutline.positionCount = 0;
			roadOutline.positionCount = 0;
			SetDirectionArrow(start, end, color);
			Vector3 val = end - start;
			markerA.Show(start, val, "A", color);
			markerB.Show(end, -val, "B", color);
			markerW.Hide();
			root.SetActive(true);
		}

		public void RenderPlan(RampBuildPlan plan, Vector3 widthPoint)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0101: Unknown result type (might be due to invalid IL or missing references)
			//IL_0124: Unknown result type (might be due to invalid IL or missing references)
			//IL_0147: Unknown result type (might be due to invalid IL or missing references)
			//IL_016c: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_02da: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0311: Unknown result type (might be due to invalid IL or missing references)
			//IL_0313: Unknown result type (might be due to invalid IL or missing references)
			//IL_0315: Unknown result type (might be due to invalid IL or missing references)
			//IL_0321: Unknown result type (might be due to invalid IL or missing references)
			//IL_0323: Unknown result type (might be due to invalid IL or missing references)
			//IL_0339: Unknown result type (might be due to invalid IL or missing references)
			//IL_0349: Unknown result type (might be due to invalid IL or missing references)
			//IL_034b: Unknown result type (might be due to invalid IL or missing references)
			//IL_034d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0366: Unknown result type (might be due to invalid IL or missing references)
			//IL_0370: Unknown result type (might be due to invalid IL or missing references)
			//IL_0371: Unknown result type (might be due to invalid IL or missing references)
			//IL_0373: Unknown result type (might be due to invalid IL or missing references)
			//IL_0378: Unknown result type (might be due to invalid IL or missing references)
			//IL_03b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_03b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_03b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_03be: Unknown result type (might be due to invalid IL or missing references)
			//IL_03c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_03c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_03d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_03d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_03d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_03db: Unknown result type (might be due to invalid IL or missing references)
			//IL_03f4: Unknown result type (might be due to invalid IL or missing references)
			mesh.Clear();
			if (plan == null)
			{
				Hide();
				return;
			}
			Color slopeColor = GetSlopeColor(plan);
			SetColor(slopeColor);
			List<Vector3> list = new List<Vector3>(plan.Edits.Count * 4);
			List<int> list2 = new List<int>(plan.Edits.Count * 10);
			Dictionary<GridVertexKey, float> dictionary = new Dictionary<GridVertexKey, float>();
			HashSet<GridCellKey> hashSet = new HashSet<GridCellKey>();
			foreach (RampVertexEdit edit in plan.Edits)
			{
				dictionary[new GridVertexKey(edit.Heightmap, edit.GridX, edit.GridZ)] = edit.TargetHeight;
				AddAdjacentCells(hashSet, edit.Heightmap, edit.GridX, edit.GridZ);
			}
			foreach (GridCellKey item in hashSet)
			{
				int count = list.Count;
				list.Add(GetPreviewVertex(item.Heightmap, dictionary, item.X, item.Z));
				list.Add(GetPreviewVertex(item.Heightmap, dictionary, item.X + 1, item.Z));
				list.Add(GetPreviewVertex(item.Heightmap, dictionary, item.X, item.Z + 1));
				list.Add(GetPreviewVertex(item.Heightmap, dictionary, item.X + 1, item.Z + 1));
				AddLine(list2, count, count + 1);
				AddLine(list2, count, count + 2);
				AddLine(list2, count + 1, count + 2);
				AddLine(list2, count + 1, count + 3);
				AddLine(list2, count + 2, count + 3);
			}
			mesh.SetVertices(list);
			mesh.SetIndices(list2.ToArray(), (MeshTopology)3, 0);
			mesh.RecalculateBounds();
			RampDefinition definition = plan.Definition;
			Vector3 val = default(Vector3);
			((Vector3)(ref val))..ctor((float)definition.DirectionX, 0f, (float)definition.DirectionZ);
			Vector3 val2 = default(Vector3);
			((Vector3)(ref val2))..ctor((float)definition.NormalX, 0f, (float)definition.NormalZ);
			float negativeWidth = (float)(definition.NegativeRoadWidth + definition.ShoulderWidth);
			float positiveWidth = (float)(definition.PositiveRoadWidth + definition.ShoulderWidth);
			Vector3 val3 = default(Vector3);
			((Vector3)(ref val3))..ctor((float)definition.StartX, (float)definition.StartY, (float)definition.StartZ);
			Vector3 val4 = default(Vector3);
			((Vector3)(ref val4))..ctor((float)definition.EndX, (float)definition.EndY, (float)definition.EndZ);
			Vector3 start = val3 - val * (float)definition.EndBlendLength;
			Vector3 end = val4 + val * (float)definition.EndBlendLength;
			SetCorridor(outerOutline, start, end, val2, negativeWidth, positiveWidth, 0.12f);
			SetCorridor(roadOutline, val3, val4, val2, (float)definition.NegativeRoadWidth, (float)definition.PositiveRoadWidth, 0.16f);
			SetDirectionArrow(val3, val4, slopeColor);
			markerA.Show(val3, val, "A", new Color(0.2f, 0.85f, 1f));
			markerB.Show(val4, -val, "B", new Color(1f, 0.78f, 0.2f));
			float num = ((Vector3.Dot(widthPoint - val3, val2) < 0f) ? (-1f) : 1f);
			float num2 = ((num < 0f) ? ((float)definition.NegativeRoadWidth) : ((float)definition.PositiveRoadWidth));
			Vector3 position = val3 + val2 * num2 * num;
			markerW.Show(position, -val2 * num, "W", new Color(0.8f, 0.45f, 1f));
			root.SetActive(true);
		}

		public void SetGridVisible(bool visible)
		{
			if (Object.op_Implicit((Object)(object)gridRenderer))
			{
				((Renderer)gridRenderer).enabled = visible;
			}
		}

		public void UpdateBillboards()
		{
			markerA.UpdateBillboard();
			markerB.UpdateBillboard();
			markerW.UpdateBillboard();
		}

		public void Hide()
		{
			if (Object.op_Implicit((Object)(object)root))
			{
				root.SetActive(false);
			}
		}

		public void Dispose()
		{
			if (Object.op_Implicit((Object)(object)mesh))
			{
				Object.Destroy((Object)(object)mesh);
			}
			if (Object.op_Implicit((Object)(object)material))
			{
				Object.Destroy((Object)(object)material);
			}
			markerA.Dispose();
			markerB.Dispose();
			markerW.Dispose();
			if (Object.op_Implicit((Object)(object)root))
			{
				Object.Destroy((Object)(object)root);
			}
		}

		private void SetColor(Color color)
		{
			//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_001e: 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_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			material.color = color;
			SetLineColor(roadOutline, color);
			SetLineColor(centerLine, color);
			SetLineColor(arrowHead, color);
			Color color2 = color;
			color2.a *= 0.55f;
			SetLineColor(outerOutline, color2);
		}

		private LineRenderer CreateLine(string name, float width, bool loop)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject(name)
			{
				hideFlags = (HideFlags)61
			};
			val.transform.SetParent(root.transform, false);
			LineRenderer obj = val.AddComponent<LineRenderer>();
			obj.useWorldSpace = true;
			obj.loop = loop;
			obj.widthMultiplier = width;
			obj.numCornerVertices = 2;
			obj.numCapVertices = 2;
			((Renderer)obj).shadowCastingMode = (ShadowCastingMode)0;
			((Renderer)obj).receiveShadows = false;
			((Renderer)obj).sharedMaterial = material;
			obj.positionCount = 0;
			return obj;
		}

		private static void SetLineColor(LineRenderer line, Color color)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			line.startColor = color;
			line.endColor = color;
		}

		private static void SetCorridor(LineRenderer line, Vector3 start, Vector3 end, Vector3 normal, float negativeWidth, float positiveWidth, float lift)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: 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)
			//IL_0031: 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_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: 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_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			line.positionCount = 4;
			Vector3 val = Vector3.up * lift;
			line.SetPosition(0, start + normal * positiveWidth + val);
			line.SetPosition(1, end + normal * positiveWidth + val);
			line.SetPosition(2, end - normal * negativeWidth + val);
			line.SetPosition(3, start - normal * negativeWidth + val);
		}

		private void SetDirectionArrow(Vector3 start, Vector3 end, Color color)
		{
			//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_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_0008: 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)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: 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_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: 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_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00be: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_010f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0111: Unknown result type (might be due to invalid IL or missing references)
			//IL_0114: Unknown result type (might be due to invalid IL or missing references)
			//IL_011e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0123: Unknown result type (might be due to invalid IL or missing references)
			//IL_0134: Unknown result type (might be due to invalid IL or missing references)
			//IL_0142: Unknown result type (might be due to invalid IL or missing references)
			//IL_0144: Unknown result type (might be due to invalid IL or missing references)
			//IL_0147: Unknown result type (might be due to invalid IL or missing references)
			//IL_0151: Unknown result type (might be due to invalid IL or missing references)
			//IL_0156: Unknown result type (might be due to invalid IL or missing references)
			//IL_0166: Unknown result type (might be due to invalid IL or missing references)
			//IL_0172: Unknown result type (might be due to invalid IL or missing references)
			Vector3 val = end - start;
			Vector2 val2 = new Vector2(val.x, val.z);
			float magnitude = ((Vector2)(ref val2)).magnitude;
			if (magnitude < 0.01f)
			{
				centerLine.positionCount = 0;
				arrowHead.positionCount = 0;
				return;
			}
			Vector3 val3 = new Vector3(val.x, 0f, val.z);
			Vector3 normalized = ((Vector3)(ref val3)).normalized;
			Vector3 val4 = default(Vector3);
			((Vector3)(ref val4))..ctor(0f - normalized.z, 0f, normalized.x);
			Vector3 val5 = start + Vector3.up * 0.2f;
			Vector3 val6 = end + Vector3.up * 0.2f;
			centerLine.positionCount = 2;
			centerLine.SetPosition(0, val5);
			centerLine.SetPosition(1, val6);
			float num = Mathf.Clamp(magnitude * 0.12f, 0.45f, 1.2f);
			Vector3 val7 = val6 - normalized * num;
			arrowHead.positionCount = 3;
			arrowHead.SetPosition(0, val7 + val4 * num * 0.5f);
			arrowHead.SetPosition(1, val6);
			arrowHead.SetPosition(2, val7 - val4 * num * 0.5f);
			SetLineColor(centerLine, color);
			SetLineColor(arrowHead, color);
		}

		private static Color GetSlopeColor(RampBuildPlan plan)
		{
			//IL_0059: 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_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			if (!plan.IsValid)
			{
				return new Color(1f, 0.12f, 0.08f, 0.9f);
			}
			float num = Mathf.Max(1f, TerrainRampPlugin.MaximumSlopeDegrees.Value);
			float num2 = Mathf.Clamp01(plan.MaximumDrivenSlopeDegrees / num);
			Color val = new Color(0.15f, 1f, 0.25f, 0.82f);
			Color val2 = default(Color);
			((Color)(ref val2))..ctor(1f, 0.82f, 0.12f, 0.86f);
			return Color.Lerp(val, val2, num2);
		}

		private static void AddAdjacentCells(HashSet<GridCellKey> cells, Heightmap heightmap, int x, int z)
		{
			for (int i = z - 1; i <= z; i++)
			{
				for (int j = x - 1; j <= x; j++)
				{
					if (j >= 0 && i >= 0 && j < heightmap.m_width && i < heightmap.m_width)
					{
						cells.Add(new GridCellKey(heightmap, j, i));
					}
				}
			}
		}

		private static Vector3 GetPreviewVertex(Heightmap heightmap, Dictionary<GridVertexKey, float> targetHeights, int x, int z)
		{
			//IL_0003: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: 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_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			Vector3 worldVertex = RampTerrain.GetWorldVertex(heightmap, x, z);
			if (targetHeights.TryGetValue(new GridVertexKey(heightmap, x, z), out var value))
			{
				worldVertex.y = value;
			}
			return worldVertex + Vector3.up * 0.08f;
		}

		private static void AddLine(List<int> indices, int from, int to)
		{
			indices.Add(from);
			indices.Add(to);
		}
	}
	internal sealed class RampSession
	{
		private readonly RampPreview preview;

		private int stage;

		private Vector3 start;

		private Vector3 end;

		private RampBuildPlan currentPlan;

		private Vector3 lastPlanWidthPoint;

		private bool hasLastPlanWidthPoint;

		private bool lastPlanOneSided;

		private bool lastPlanTerrainFit;

		private bool applying;

		private bool gridVisible;

		private RampProfile selectedProfile;

		private int lastProfileInputFrame = -1;

		private float statusHoldUntil;

		private string status = string.Empty;

		public string Status => status;

		public int Stage => stage;

		public RampBuildPlan CurrentPlan => currentPlan;

		public RampProfile SelectedProfile => selectedProfile;

		public bool TerrainFitActive
		{
			get
			{
				if (stage == 2)
				{
					return lastPlanTerrainFit;
				}
				return false;
			}
		}

		public RampSession()
		{
			preview = new RampPreview();
			gridVisible = TerrainRampPlugin.ShowTechnicalGrid.Value;
			preview.SetGridVisible(gridVisible);
		}

		public void Update()
		{
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0108: Unknown result type (might be due to invalid IL or missing references)
			//IL_0184: Unknown result type (might be due to invalid IL or missing references)
			//IL_0189: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_031b: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_020c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0211: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_02bf: Unknown result type (might be due to invalid IL or missing references)
			Player localPlayer = Player.m_localPlayer;
			if (!Object.op_Implicit((Object)(object)localPlayer) || !TerrainRampPlugin.IsRampPiece(localPlayer.GetSelectedPiece()))
			{
				if (!applying)
				{
					ResetSelection();
				}
				return;
			}
			preview.UpdateBillboards();
			if (Input.GetKeyDown(TerrainRampPlugin.ToggleGridKey.Value))
			{
				gridVisible = !gridVisible;
				TerrainRampPlugin.ShowTechnicalGrid.Value = gridVisible;
				preview.SetGridVisible(gridVisible);
				status = RampLocalization.Text(gridVisible ? "grid_enabled" : "grid_disabled");
				return;
			}
			if (Input.GetKeyDown((KeyCode)27))
			{
				ResetSelection();
				status = RampLocalization.Text("reset");
				statusHoldUntil = Time.time + 1.5f;
				return;
			}
			if (applying)
			{
				status = RampLocalization.Text("applying");
				return;
			}
			if (stage == 2)
			{
				PollProfileSelectionInput();
			}
			if (!TerrainRampPlugin.TryGetPlacementPoint(localPlayer, out var point) || !RampTerrain.TrySnapToVertex(point, out var snapped))
			{
				currentPlan = null;
				hasLastPlanWidthPoint = false;
				preview.Hide();
				status = RampLocalization.Text("aim_loaded");
				return;
			}
			if (stage == 0)
			{
				preview.Hide();
				if (Time.time >= statusHoldUntil)
				{
					status = RampLocalization.Text("click_start");
				}
				return;
			}
			if (stage == 1)
			{
				float num = HorizontalDistance(start, snapped);
				if (num < 0.001f)
				{
					preview.RenderCenterLine(start, snapped, slopeAllowed: false);
					status = RampLocalization.Text("same_point");
					return;
				}
				double num2 = RampDefinition.CalculateMaximumSlopeDegrees(num, snapped.y - start.y, selectedProfile, TerrainRampPlugin.EndBlendLength.Value);
				bool flag = num2 <= (double)TerrainRampPlugin.MaximumSlopeDegrees.Value;
				preview.RenderCenterLine(start, snapped, flag);
				status = (flag ? RampLocalization.Text("click_end", num, num2, TerrainRampPlugin.MaximumSlopeDegrees.Value, RampLocalization.ProfileName(selectedProfile)) : RampLocalization.Text("slope_analytic", num2, TerrainRampPlugin.MaximumSlopeDegrees.Value));
				return;
			}
			bool flag2 = IsOneSidedWidthHeld();
			bool flag3 = IsTerrainFitHeld();
			if (currentPlan != null && hasLastPlanWidthPoint)
			{
				Vector3 val = snapped - lastPlanWidthPoint;
				if (!(((Vector3)(ref val)).sqrMagnitude > 0.0001f) && flag2 == lastPlanOneSided && flag3 == lastPlanTerrainFit)
				{
					goto IL_0321;
				}
			}
			currentPlan = CreatePlan(snapped, flag2, flag3);
			lastPlanWidthPoint = snapped;
			lastPlanOneSided = flag2;
			lastPlanTerrainFit = flag3;
			hasLastPlanWidthPoint = true;
			preview.RenderPlan(currentPlan, snapped);
			goto IL_0321;
			IL_0321:
			if (!(Time.time < statusHoldUntil))
			{
				status = (currentPlan.IsValid ? RampLocalization.Text("click_apply_ready") : currentPlan.InvalidReason);
			}
		}

		public bool HandlePlacement(Player player, Vector3 cursor)
		{
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0186: 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_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_0139: Unknown result type (might be due to invalid IL or missing references)
			//IL_013a: Unknown result type (might be due to invalid IL or missing references)
			//IL_015a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
			if (applying)
			{
				status = RampLocalization.Text("wait");
				return false;
			}
			if (!RampTerrain.TrySnapToVertex(cursor, out var snapped))
			{
				status = RampLocalization.Text("off_grid");
				return false;
			}
			if (stage == 0)
			{
				start = snapped;
				stage = 1;
				RampSound.PlayPoint(player.GetSelectedPiece(), snapped, 1);
				status = RampLocalization.Text("start_set");
				return false;
			}
			if (stage == 1)
			{
				float num = HorizontalDistance(start, snapped);
				if (num < 0.001f)
				{
					status = RampLocalization.Text("same_point");
					return false;
				}
				double num2 = RampDefinition.CalculateMaximumSlopeDegrees(num, snapped.y - start.y, selectedProfile, TerrainRampPlugin.EndBlendLength.Value);
				if (num2 > (double)TerrainRampPlugin.MaximumSlopeDegrees.Value)
				{
					preview.RenderCenterLine(start, snapped, slopeAllowed: false);
					status = RampLocalization.Text("slope_analytic", num2, TerrainRampPlugin.MaximumSlopeDegrees.Value);
					return false;
				}
				end = snapped;
				stage = 2;
				currentPlan = null;
				hasLastPlanWidthPoint = false;
				RampSound.PlayPoint(player.GetSelectedPiece(), snapped, 2);
				status = RampLocalization.Text("width_prompt");
				return false;
			}
			bool oneSided = IsOneSidedWidthHeld();
			bool terrainFit = IsTerrainFitHeld();
			currentPlan = CreatePlan(snapped, oneSided, terrainFit);
			lastPlanOneSided = oneSided;
			lastPlanTerrainFit = terrainFit;
			if (!currentPlan.IsValid)
			{
				status = currentPlan.InvalidReason;
				preview.RenderPlan(currentPlan, snapped);
				return false;
			}
			if (!RampTerrain.CheckBuildAccess(currentPlan, out var reason))
			{
				status = reason;
				return false;
			}
			applying = true;
			preview.Hide();
			RampOperationApplier.Apply(currentPlan, OnApplyCompleted);
			return true;
		}

		public void Dispose()
		{
			preview.Dispose();
		}

		private RampBuildPlan CreatePlan(Vector3 widthPoint, bool oneSided, bool terrainFit)
		{
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d7: Unknown result type (might be due to invalid IL or missing references)
			Vector2 val = new Vector2(end.x - start.x, end.z - start.z);
			Vector2 normalized = ((Vector2)(ref val)).normalized;
			Vector2 val2 = default(Vector2);
			((Vector2)(ref val2))..ctor(0f - normalized.y, normalized.x);
			RampRoadWidths rampRoadWidths = RampWidthSelection.Calculate(Vector2.Dot(new Vector2(widthPoint.x - start.x, widthPoint.z - start.z), val2), TerrainRampPlugin.MinimumRoadWidth.Value, TerrainRampPlugin.MaximumRoadWidth.Value, oneSided);
			RampEndpointPlane plane = default(RampEndpointPlane);
			RampEndpointPlane plane2 = default(RampEndpointPlane);
			float terrainGridScale = 0f;
			float terrainGridScale2 = 0f;
			bool flag = !terrainFit || (RampTerrain.TryFitEndpointPlane(start, normalized, out plane, out terrainGridScale) && RampTerrain.TryFitEndpointPlane(end, normalized, out plane2, out terrainGridScale2));
			RampBuildPlan rampBuildPlan = BuildPlan((flag && terrainFit) ? new RampDefinition(start.x, start.y, start.z, end.x, end.y, end.z, rampRoadWidths.NegativeSide, rampRoadWidths.PositiveSide, TerrainRampPlugin.ShoulderWidth.Value, TerrainRampPlugin.EndBlendLength.Value, selectedProfile, usesEndpointPlanes: true, plane, plane2, (double)Math.Max(terrainGridScale, terrainGridScale2) * 2.0) : new RampDefinition(start.x, start.y, start.z, end.x, end.y, end.z, rampRoadWidths.NegativeSide, rampRoadWidths.PositiveSide, TerrainRampPlugin.ShoulderWidth.Value, TerrainRampPlugin.EndBlendLength.Value, selectedProfile));
			if (terrainFit && !flag)
			{
				rampBuildPlan.IsValid = false;
				rampBuildPlan.InvalidReason = RampLocalization.Text("terrain_fit_unavailable");
			}
			return rampBuildPlan;
		}

		private static RampBuildPlan BuildPlan(RampDefinition definition)
		{
			return RampTerrain.BuildPlan(definition, TerrainRampPlugin.MaximumVertices.Value, TerrainRampPlugin.MaximumSlopeDegrees.Value);
		}

		private void OnApplyCompleted(bool success, string message)
		{
			applying = false;
			status = message;
			if (success)
			{
				stage = 0;
				selectedProfile = RampProfile.Linear;
				currentPlan = null;
				hasLastPlanWidthPoint = false;
			}
			else
			{
				hasLastPlanWidthPoint = false;
			}
			statusHoldUntil = Time.time + 2.5f;
		}

		private void ResetSelection()
		{
			stage = 0;
			selectedProfile = RampProfile.Linear;
			currentPlan = null;
			hasLastPlanWidthPoint = false;
			lastPlanOneSided = false;
			lastPlanTerrainFit = false;
			statusHoldUntil = 0f;
			preview.Hide();
			if (!applying)
			{
				status = string.Empty;
			}
		}

		private static float HorizontalDistance(Vector3 a, Vector3 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_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			float num = b.x - a.x;
			float num2 = b.z - a.z;
			return Mathf.Sqrt(num * num + num2 * num2);
		}

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

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

		internal void PollProfileSelectionInput()
		{
			if (stage == 2)
			{
				int num = 0;
				if (ZInput.instance != null && TerrainRampPlugin.PreviousProfileButton != null && ZInput.GetButtonDown(TerrainRampPlugin.PreviousProfileButton.Name))
				{
					num = -1;
				}
				else if (ZInput.instance != null && TerrainRampPlugin.NextProfileButton != null && ZInput.GetButtonDown(TerrainRampPlugin.NextProfileButton.Name))
				{
					num = 1;
				}
				if (num != 0 && lastProfileInputFrame != Time.frameCount)
				{
					lastProfileInputFrame = Time.frameCount;
					int num2 = (int)(selectedProfile + num + 4) % 4;
					selectedProfile = (RampProfile)num2;
					currentPlan = null;
					hasLastPlanWidthPoint = false;
					status = RampLocalization.Text("profile_selected", RampLocalization.ProfileName(selectedProfile));
					statusHoldUntil = Time.time + 1.2f;
				}
			}
		}
	}
	internal static class RampSound
	{
		public static void PlayPoint(Piece piece, Vector3 point, int pointNumber)
		{
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0136: Expected O, but got Unknown
			if (!Object.op_Implicit((Object)(object)piece) || piece.m_placeEffect == null || piece.m_placeEffect.m_effectPrefabs == null)
			{
				return;
			}
			EffectData[] effectPrefabs = piece.m_placeEffect.m_effectPrefabs;
			foreach (EffectData val in effectPrefabs)
			{
				if (val != null && val.m_enabled && Object.op_Implicit((Object)(object)val.m_prefab))
				{
					AudioSource componentInChildren = val.m_prefab.GetComponentInChildren<AudioSource>(true);
					if (Object.op_Implicit((Object)(object)componentInChildren) && Object.op_Implicit((Object)(object)componentInChildren.clip))
					{
						GameObject val2 = new GameObject("TerrainRamp_PointSound")
						{
							hideFlags = (HideFlags)61
						};
						val2.transform.position = point;
						AudioSource val3 = val2.AddComponent<AudioSource>();
						val3.clip = componentInChildren.clip;
						val3.outputAudioMixerGroup = componentInChildren.outputAudioMixerGroup;
						val3.volume = Mathf.Clamp01(componentInChildren.volume * 0.7f);
						val3.pitch = ((pointNumber == 1) ? 0.92f : 1.08f);
						val3.spatialBlend = 1f;
						val3.minDistance = 1f;
						val3.maxDistance = 18f;
						val3.Play();
						Object.Destroy((Object)val2, componentInChildren.clip.length / val3.pitch + 0.2f);
						break;
					}
				}
			}
		}
	}
	internal sealed class RampVertexEdit
	{
		public Heightmap Heightmap;

		public int GridX;

		public int GridZ;

		public Vector3 WorldPosition;

		public float TargetHeight;
	}
	internal sealed class RampBuildPlan
	{
		public RampDefinition Definition;

		public readonly List<RampVertexEdit> Edits = new List<RampVertexEdit>();

		public float MaximumDrivenSlopeDegrees;

		public bool IsValid;

		public string InvalidReason;
	}
	internal static class RampTerrain
	{
		private struct WorldVertexKey
		{
			private readonly int xMillimetres;

			private readonly int zMillimetres;

			public WorldVertexKey(float x, float z)
			{
				xMillimetres = Mathf.RoundToInt(x * 1000f);
				zMillimetres = Mathf.RoundToInt(z * 1000f);
			}

			public override bool Equals(object obj)
			{
				if (!(obj is WorldVertexKey worldVertexKey))
				{
					return false;
				}
				if (xMillimetres == worldVertexKey.xMillimetres)
				{
					return zMillimetres == worldVertexKey.zMillimetres;
				}
				return false;
			}

			public override int GetHashCode()
			{
				return (xMillimetres * 397) ^ zMillimetres;
			}
		}

		private struct GridVertexKey
		{
			public readonly int X;

			public readonly int Z;

			public GridVertexKey(int x, int z)
			{
				X = x;
				Z = z;
			}

			public override bool Equals(object obj)
			{
				if (!(obj is GridVertexKey gridVertexKey))
				{
					return false;
				}
				if (X == gridVertexKey.X)
				{
					return Z == gridVertexKey.Z;
				}
				return false;
			}

			public override int GetHashCode()
			{
				return (X * 397) ^ Z;
			}
		}

		private struct GridCellKey
		{
			public readonly int X;

			public readonly int Z;

			public GridCellKey(int x, int z)
			{
				X = x;
				Z = z;
			}

			public override bool Equals(object obj)
			{
				if (!(obj is GridCellKey gridCellKey))
				{
					return false;
				}
				if (X == gridCellKey.X)
				{
					return Z == gridCellKey.Z;
				}
				return false;
			}

			public override int GetHashCode()
			{
				return (X * 397) ^ Z;
			}
		}

		private const float VanillaTerrainDeltaLimit = 8f;

		private const float DeltaSafetyMargin = 0.02f;

		private static readonly MethodInfo GetWorldBaseHeightMethod = AccessTools.Method(typeof(Heightmap), "GetWorldBaseHeight", (Type[])null, (Type[])null);

		public static bool TrySnapToVertex(Vector3 point, out Vector3 snapped)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			snapped = default(Vector3);
			Heightmap val = Heightmap.FindHeightmap(point);
			if (!Object.op_Implicit((Object)(object)val))
			{
				return false;
			}
			int num = default(int);
			int num2 = default(int);
			val.WorldToVertex(point, ref num, ref num2);
			if (num < 0 || num2 < 0 || num > val.m_width || num2 > val.m_width)
			{
				return false;
			}
			snapped = GetWorldVertex(val, num, num2);
			return true;
		}

		public static bool TryFitEndpointPlane(Vector3 center, Vector2 roadAxis, out RampEndpointPlane plane, out float terrainGridScale)
		{
			//IL_001e: 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_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_017c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0181: Unknown result type (might be due to invalid IL or missing references)
			//IL_0183: Unknown result type (might be due to invalid IL or missing references)
			//IL_018a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0194: Unknown result type (might be due to invalid IL or missing references)
			//IL_019b: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01be: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00df: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0102: Unknown result type (might be due to invalid IL or missing references)
			//IL_0135: Unknown result type (might be due to invalid IL or missing references)
			plane = default(RampEndpointPlane);
			terrainGridScale = 0f;
			if (((Vector2)(ref roadAxis)).sqrMagnitude < 0.0001f)
			{
				return false;
			}
			Heightmap val = Heightmap.FindHeightmap(center);
			if (!Object.op_Implicit((Object)(object)val) || val.m_scale <= 0f)
			{
				return false;
			}
			terrainGridScale = val.m_scale;
			float num = terrainGridScale * 2f;
			((Vector2)(ref roadAxis)).Normalize();
			Vector2 val2 = default(Vector2);
			((Vector2)(ref val2))..ctor(0f - roadAxis.y, roadAxis.x);
			List<Vector3> list = new List<Vector3>(9);
			for (int i = -1; i <= 1; i++)
			{
				for (int j = -1; j <= 1; j++)
				{
					if (!TryGetAveragedSnappedVertex(center + new Vector3((roadAxis.x * (float)i + val2.x * (float)j) * num, 0f, (roadAxis.y * (float)i + val2.y * (float)j) * num), out var snapped))
					{
						return false;
					}
					bool flag = false;
					foreach (Vector3 item in list)
					{
						Vector2 val3 = new Vector2(item.x - snapped.x, item.z - snapped.z);
						if (((Vector2)(ref val3)).sqrMagnitude < 0.0001f)
						{
							flag = true;
							break;
						}
					}
					if (!flag)
					{
						list.Add(snapped);
					}
				}
			}
			if (list.Count < 5)
			{
				return false;
			}
			List<RampPlaneSample> list2 = new List<RampPlaneSample>(list.Count);
			foreach (Vector3 item2 in list)
			{
				double num2 = item2.x - center.x;
				double num3 = item2.z - center.z;
				list2.Add(new RampPlaneSample(num2 * (double)roadAxis.x + num3 * (double)roadAxis.y, num2 * (double)val2.x + num3 * (double)val2.y, item2.y));
			}
			return RampPlaneFitting.TryFit(list2, out plane);
		}

		private static bool TryGetAveragedSnappedVertex(Vector3 point, out Vector3 snapped)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: 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_00a2: Unknown result type (might be due to invalid IL or missing references)
			if (!TrySnapToVertex(point, out snapped))
			{
				return false;
			}
			float num = 0f;
			int num2 = 0;
			int num3 = default(int);
			int num4 = default(int);
			foreach (Heightmap allHeightmap in Heightmap.GetAllHeightmaps())
			{
				if (!Object.op_Implicit((Object)(object)allHeightmap))
				{
					continue;
				}
				allHeightmap.WorldToVertex(snapped, ref num3, ref num4);
				if (num3 >= 0 && num4 >= 0 && num3 <= allHeightmap.m_width && num4 <= allHeightmap.m_width)
				{
					Vector3 worldVertex = GetWorldVertex(allHeightmap, num3, num4);
					if (!(Mathf.Abs(worldVertex.x - snapped.x) > 0.001f) && !(Mathf.Abs(worldVertex.z - snapped.z) > 0.001f))
					{
						num += worldVertex.y;
						num2++;
					}
				}
			}
			if (num2 == 0)
			{
				return false;
			}
			snapped.y = num / (float)num2;
			return true;
		}

		public static RampBuildPlan BuildPlan(RampDefinition definition, int maximumVertices, float maximumSlopeDegrees)
		{
			//IL_0126: Unknown result type (might be due to invalid IL or missing references)
			//IL_012b: Unknown result type (might be due to invalid IL or missing references)
			//IL_012e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0136: Unknown result type (might be due to invalid IL or missing references)
			//IL_013e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0159: Unknown result type (might be due to invalid IL or missing references)
			//IL_015b: Unknown result type (might be due to invalid IL or missing references)
			//IL_015f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0166: Unknown result type (might be due to invalid IL or missing references)
			//IL_019f: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0201: Unknown result type (might be due to invalid IL or missing references)
			//IL_01aa: Unknown result type (might be due to invalid IL or missing references)
			RampBuildPlan rampBuildPlan = new RampBuildPlan
			{
				Definition = definition,
				IsValid = true,
				InvalidReason = string.Empty
			};
			if (definition.MaximumSlopeDegrees > (double)maximumSlopeDegrees)
			{
				Invalidate(rampBuildPlan, RampLocalization.Text("slope_analytic", definition.MaximumSlopeDegrees, maximumSlopeDegrees));
			}
			if (ExceedsFootprintWorkBudget(definition, maximumVertices))
			{
				Invalidate(rampBuildPlan, RampLocalization.Text("footprint_budget", maximumVertices));
				return rampBuildPlan;
			}
			if (!AreFootprintZonesLoaded(definition))
			{
				Invalidate(rampBuildPlan, RampLocalization.Text("zones_loaded"));
			}
			double x = (definition.StartX + definition.EndX)