Decompiled source of RailroadsAutomation v0.3.2

plugins/RailroadsAutomation/RailroadsAutomation.dll

Decompiled 2 days ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Jotunn.Configs;
using Jotunn.Entities;
using Jotunn.Managers;
using Jotunn.Utils;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyVersion("0.0.0.0")]
namespace RailroadsAutomation;

[Serializable]
public sealed class Pier
{
	public V Foot;

	public V Origin;

	public double Step;

	public double Radius;

	public int Blocks;

	public int Stone;

	[JsonIgnore]
	public V Top => Foot + new V(0.0, Step * (double)Blocks, 0.0);
}
internal static class BridgeRules
{
	internal static bool Elevated(double deck, double ground, double water, double threshold)
	{
		if (!(ground < water - 0.5))
		{
			return deck - ground > threshold;
		}
		return true;
	}

	internal static double Guide(double reference, double ground, double water, double bed, double threshold)
	{
		if (!Elevated(reference - bed, ground, water, threshold))
		{
			return ground + bed;
		}
		return reference;
	}

	internal static V Nearest(V point, V[] path)
	{
		double num = double.PositiveInfinity;
		V result = path[0];
		for (int i = 1; i < path.Length; i++)
		{
			V v = path[i] - path[i - 1];
			double num2 = v.x * v.x + v.z * v.z;
			double num3 = ((num2 < 1E-10) ? 0.0 : Math.Max(0.0, Math.Min(1.0, ((point.x - path[i - 1].x) * v.x + (point.z - path[i - 1].z) * v.z) / num2)));
			V v2 = path[i - 1] + v * num3;
			double num4 = (point.x - v2.x) * (point.x - v2.x) + (point.z - v2.z) * (point.z - v2.z);
			if (num4 < num)
			{
				num = num4;
				result = v2;
			}
		}
		return result;
	}

	internal static List<Pier> Plan(V[] path, Func<V, double> ground, double water, double bed, double threshold, double maximum, double blockHeight, V offset, double radius, int stone)
	{
		List<Pier> list = new List<Pier>();
		double num = 0.0;
		for (int i = 1; i < path.Length; i++)
		{
			num += (path[i] - path[i - 1]).Length;
		}
		int num2 = Math.Max(1, (int)Math.Ceiling(num / 8.0));
		for (int j = 0; j < num2; j++)
		{
			double num3 = num * ((double)j + 0.5) / (double)num2;
			V v = path[0];
			for (int k = 1; k < path.Length; k++)
			{
				double length = (path[k] - path[k - 1]).Length;
				if (num3 <= length || k == path.Length - 1)
				{
					v = path[k - 1] + (path[k] - path[k - 1]) * ((length < 1E-10) ? 0.0 : Math.Min(1.0, num3 / length));
					break;
				}
				num3 -= length;
			}
			double num4 = ground(v);
			double num5 = v.y - bed;
			if (!Elevated(num5, num4, water, threshold))
			{
				continue;
			}
			double num6 = num4;
			double[] array = new double[3]
			{
				0.0 - radius,
				0.0,
				radius
			};
			foreach (double a in array)
			{
				double[] array2 = new double[3]
				{
					0.0 - radius,
					0.0,
					radius
				};
				foreach (double c in array2)
				{
					num6 = Math.Min(num6, ground(v + new V(a, 0.0, c)));
				}
			}
			double num7 = num5 - num6;
			if (num7 > maximum + 0.001)
			{
				throw new InvalidOperationException(L.Key("raa_bridge_depth"));
			}
			int num8 = Math.Max(1, (int)Math.Ceiling((num7 + 0.15) / blockHeight));
			V v2 = new V(v.x, num5 - (double)num8 * blockHeight, v.z);
			list.Add(new Pier
			{
				Foot = v2,
				Origin = v2 + offset,
				Blocks = num8,
				Step = blockHeight,
				Radius = radius,
				Stone = stone
			});
		}
		return list;
	}

	internal static int StoneCost(IList<Pier> piers)
	{
		int num = 0;
		if (piers != null)
		{
			foreach (Pier pier in piers)
			{
				num = checked(num + pier.Blocks * pier.Stone);
			}
		}
		return num;
	}

	internal static bool Valid(Pier p)
	{
		if (p != null && p.Foot.Finite && p.Origin.Finite && p.Step >= 0.5 && p.Step <= 4.0 && p.Radius >= 0.1 && p.Radius <= 3.0 && p.Blocks >= 1 && p.Blocks <= 256 && p.Step * (double)p.Blocks <= 132.0 && p.Stone >= 1 && p.Stone <= 1000)
		{
			return (p.Origin - p.Foot).Length <= 10.0;
		}
		return false;
	}

	internal static V[] Clearance(IList<Pier> piers)
	{
		List<V> list = new List<V>();
		if (piers != null)
		{
			foreach (Pier pier in piers)
			{
				int num = (int)Math.Ceiling(pier.Step * (double)pier.Blocks / 0.5);
				for (int i = 0; i <= num; i++)
				{
					list.Add(pier.Foot + (pier.Top - pier.Foot) * ((double)i / (double)num));
				}
			}
		}
		return list.ToArray();
	}
}
internal static class BridgeWorks
{
	internal const string Prefab = "stone_pillar";

	private static readonly int Part = StringExtensionMethods.GetStableHashCode("local.raa.pier");

	private static double height;

	private static double radius;

	private static V offset;

	private static int stone;

	internal static bool IsPier(ZDO zdo)
	{
		if (zdo.GetPrefab() == StringExtensionMethods.GetStableHashCode("stone_pillar") && zdo.GetInt(Part, -1) >= 0)
		{
			return !string.IsNullOrEmpty(zdo.GetString(Keys.JobTag, ""));
		}
		return false;
	}

	internal static void Ready()
	{
		//IL_0045: Unknown result type (might be due to invalid IL or missing references)
		//IL_004a: Unknown result type (might be due to invalid IL or missing references)
		//IL_004f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0054: Unknown result type (might be due to invalid IL or missing references)
		//IL_0078: Unknown result type (might be due to invalid IL or missing references)
		//IL_0212: Unknown result type (might be due to invalid IL or missing references)
		//IL_0224: Unknown result type (might be due to invalid IL or missing references)
		//IL_0230: Unknown result type (might be due to invalid IL or missing references)
		//IL_0247: Unknown result type (might be due to invalid IL or missing references)
		//IL_0255: Unknown result type (might be due to invalid IL or missing references)
		//IL_0263: 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_00c7: Unknown result type (might be due to invalid IL or missing references)
		//IL_0110: Unknown result type (might be due to invalid IL or missing references)
		//IL_011c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0121: Unknown result type (might be due to invalid IL or missing references)
		//IL_0126: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f6: Unknown result type (might be due to invalid IL or missing references)
		//IL_0186: Unknown result type (might be due to invalid IL or missing references)
		//IL_018d: 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_01a0: Unknown result type (might be due to invalid IL or missing references)
		//IL_01a5: 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)
		//IL_01af: 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_01b6: Unknown result type (might be due to invalid IL or missing references)
		//IL_01b8: Unknown result type (might be due to invalid IL or missing references)
		GameObject prefab = ZNetScene.instance.GetPrefab("stone_pillar");
		if (Object.op_Implicit((Object)(object)prefab) && Object.op_Implicit((Object)(object)prefab.GetComponent<ZNetView>()) && Object.op_Implicit((Object)(object)prefab.GetComponent<WearNTear>()) && Object.op_Implicit((Object)(object)prefab.GetComponent<Piece>()))
		{
			Vector3 val = prefab.transform.localScale - Vector3.one;
			if (!(((Vector3)(ref val)).sqrMagnitude > 0.0001f))
			{
				bool flag = false;
				Bounds val2 = default(Bounds);
				Collider[] componentsInChildren = prefab.GetComponentsInChildren<Collider>();
				Bounds bounds = default(Bounds);
				foreach (Collider val3 in componentsInChildren)
				{
					if (val3.isTrigger)
					{
						continue;
					}
					BoxCollider val4 = (BoxCollider)(object)((val3 is BoxCollider) ? val3 : null);
					MeshCollider val5 = (MeshCollider)(object)((val3 is MeshCollider) ? val3 : null);
					if (Object.op_Implicit((Object)(object)val4))
					{
						((Bounds)(ref bounds))..ctor(val4.center, val4.size);
					}
					else
					{
						if (!Object.op_Implicit((Object)(object)val5) || !Object.op_Implicit((Object)(object)val5.sharedMesh))
						{
							throw new InvalidOperationException(L.Key("raa_bridge_prefab"));
						}
						bounds = val5.sharedMesh.bounds;
					}
					Matrix4x4 val6 = prefab.transform.worldToLocalMatrix * ((Component)val3).transform.localToWorldMatrix;
					int[] array = new int[2] { -1, 1 };
					foreach (int num in array)
					{
						int[] array2 = new int[2] { -1, 1 };
						foreach (int num2 in array2)
						{
							int[] array3 = new int[2] { -1, 1 };
							foreach (int num3 in array3)
							{
								Vector3 val7 = ((Matrix4x4)(ref val6)).MultiplyPoint3x4(((Bounds)(ref bounds)).center + Vector3.Scale(((Bounds)(ref bounds)).extents, new Vector3((float)num, (float)num2, (float)num3)));
								if (!flag)
								{
									((Bounds)(ref val2))..ctor(val7, Vector3.zero);
									flag = true;
								}
								else
								{
									((Bounds)(ref val2)).Encapsulate(val7);
								}
							}
						}
					}
				}
				height = ((Bounds)(ref val2)).size.y;
				radius = Math.Max(((Bounds)(ref val2)).extents.x, ((Bounds)(ref val2)).extents.z);
				offset = new V(0f - ((Bounds)(ref val2)).center.x, 0f - ((Bounds)(ref val2)).min.y, 0f - ((Bounds)(ref val2)).center.z);
				Requirement[] array4 = prefab.GetComponent<Piece>().m_resources.Where((Requirement r) => Object.op_Implicit((Object)(object)r.m_resItem) && r.m_amount > 0).ToArray();
				if (!flag || array4.Length != 1 || ((Object)((Component)array4[0].m_resItem).gameObject).name != "Stone" || height < 0.5 || height > 4.0 || radius < 0.1 || radius > 3.0)
				{
					throw new InvalidOperationException(L.Key("raa_bridge_prefab"));
				}
				stone = array4[0].m_amount;
				return;
			}
		}
		throw new InvalidOperationException(L.Key("raa_bridge_prefab"));
	}

	internal static void Plan(List<Segment> route, Ground ground)
	{
		if (!Plugin.Bridges.Value)
		{
			return;
		}
		Ready();
		foreach (Segment item in route)
		{
			item.Piers = BridgeRules.Plan(RailBridge.Points(item).ToArray(), ground.Height, ZoneSystem.instance.m_waterLevel, RailBridge.BedOffset, Plugin.BridgeThreshold.Value, Plugin.MaxPierHeight.Value, height, offset, radius, stone);
		}
	}

	internal static void Validate(Segment step, Ground ground)
	{
		if (step.Piers == null)
		{
			return;
		}
		if (step.Piers.Count > 0 && !Plugin.Bridges.Value)
		{
			throw new InvalidOperationException(L.Key("raa_bridge_changed"));
		}
		foreach (Pier pier in step.Piers)
		{
			if (!BridgeRules.Valid(pier))
			{
				throw new InvalidOperationException(L.Key("raa_bridge_prefab"));
			}
			double[] array = new double[3]
			{
				0.0 - pier.Radius,
				0.0,
				pier.Radius
			};
			foreach (double a in array)
			{
				double[] array2 = new double[3]
				{
					0.0 - pier.Radius,
					0.0,
					pier.Radius
				};
				foreach (double c in array2)
				{
					double num = ground.Height(pier.Foot + new V(a, 0.0, c));
					if (num < pier.Foot.y - 0.05 || num > pier.Top.y - 0.1)
					{
						throw new InvalidOperationException(L.Key("raa_bridge_changed"));
					}
				}
			}
		}
	}

	internal static void Create(Segment step, Job job, long creator)
	{
		//IL_010e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0173: Unknown result type (might be due to invalid IL or missing references)
		//IL_0190: Unknown result type (might be due to invalid IL or missing references)
		//IL_01b7: Unknown result type (might be due to invalid IL or missing references)
		//IL_0137: Unknown result type (might be due to invalid IL or missing references)
		//IL_013c: Unknown result type (might be due to invalid IL or missing references)
		if (step.Piers == null || step.Piers.Count == 0)
		{
			return;
		}
		GameObject prefab = ZNetScene.instance.GetPrefab("stone_pillar");
		if (!Object.op_Implicit((Object)(object)prefab))
		{
			throw new InvalidOperationException(L.Key("raa_bridge_prefab"));
		}
		ZNetView component = prefab.GetComponent<ZNetView>();
		WearNTear component2 = prefab.GetComponent<WearNTear>();
		ZDO[] source = RailBridge.All.Where((ZDO z) => z.GetPrefab() == StringExtensionMethods.GetStableHashCode("stone_pillar") && z.GetString(Keys.JobTag, "") == job.Id && z.GetInt(Keys.Index, -1) == job.Index).ToArray();
		for (int num = 0; num < step.Piers.Count; num++)
		{
			for (int num2 = 0; num2 < step.Piers[num].Blocks; num2++)
			{
				int part = num * 256 + num2;
				V v = step.Piers[num].Origin + new V(0.0, step.Piers[num].Step * (double)num2, 0.0);
				ZDO[] array = source.Where((ZDO z) => z.GetInt(Part, -1) == part).ToArray();
				if (array.Length > 1 || (array.Length == 1 && ((RailBridge.V3(array[0].GetPosition()) - v).Length > 0.02 || (double)Quaternion.Angle(array[0].GetRotation(), Quaternion.identity) > 0.1)))
				{
					throw new InvalidOperationException(L.Key("raa_bridge_identity"));
				}
				if (array.Length != 1)
				{
					ZDO obj = ZDOMan.instance.CreateNewZDO(RailBridge.U(v), StringExtensionMethods.GetStableHashCode("stone_pillar"));
					obj.Persistent = true;
					obj.Type = component.m_type;
					obj.Distant = component.m_distant;
					obj.SetPrefab(StringExtensionMethods.GetStableHashCode("stone_pillar"));
					obj.SetRotation(Quaternion.identity);
					obj.Set(ZDOVars.s_creator, creator);
					obj.Set(Keys.JobTag, job.Id);
					obj.Set(Keys.Index, job.Index, false);
					obj.Set(Part, part, false);
					obj.Set(ZDOVars.s_health, component2.m_health);
					obj.Set(ZDOVars.s_support, 1000f);
				}
			}
		}
	}
}
internal sealed class Ground
{
	internal sealed class Region
	{
		internal Vector3 Center;

		internal Heightmap Loaded;

		internal ZDO Zdo;

		internal byte[] Before;

		internal TerrainData Data;

		internal HMBuildData Base;

		internal double[] Current;

		internal byte[] Protection;

		internal bool Changed;
	}

	private readonly Dictionary<Vector2s, Region> regions = new Dictionary<Vector2s, Region>();

	private readonly Dictionary<Vector2s, List<ZDO>> compilers;

	private readonly List<Tuple<Vector3, float>> legacy = new List<Tuple<Vector3, float>>();

	private readonly TrackCorridor existingRails;

	private readonly List<StructureBox> stations = new List<StructureBox>();

	internal readonly Heightmap Template;

	internal readonly int Width;

	internal readonly int Pitch;

	internal readonly float Scale;

	internal Ground()
	{
		//IL_01d6: Unknown result type (might be due to invalid IL or missing references)
		existingRails = new TrackCorridor(RailBridge.Tracks.SelectMany(delegate(ZDO z)
		{
			GameObject prefab2 = ZNetScene.instance.GetPrefab(z.GetPrefab());
			return (!Object.op_Implicit((Object)(object)prefab2)) ? Enumerable.Empty<V[]>() : RailBridge.WorldLines(prefab2, z);
		}), (double)Plugin.BedHalfWidth.Value + 0.5);
		Template = ZoneSystem.instance.m_zonePrefab.GetComponentInChildren<Heightmap>();
		Width = Template.m_width;
		Scale = Template.m_scale;
		Pitch = Width + 1;
		if (Math.Abs((float)Width * Scale - ZoneSystem.instance.m_zoneSize) > 0.01f)
		{
			throw new InvalidOperationException(L.Key("raa_earthworks_001"));
		}
		int hash = StringExtensionMethods.GetStableHashCode(((Object)Template.m_terrainCompilerPrefab).name);
		compilers = (from z in RailBridge.All
			where z.GetPrefab() == hash
			group z by ZoneSystem.GetZone(z.GetPosition())).ToDictionary((IGrouping<Vector2s, ZDO> g) => g.Key, (IGrouping<Vector2s, ZDO> g) => g.ToList());
		foreach (ZDO item in RailBridge.All)
		{
			GameObject prefab = ZNetScene.instance.GetPrefab(item.GetPrefab());
			TerrainModifier val = (Object.op_Implicit((Object)(object)prefab) ? prefab.GetComponent<TerrainModifier>() : null);
			if (Object.op_Implicit((Object)(object)val))
			{
				legacy.Add(Tuple.Create<Vector3, float>(item.GetPosition(), val.GetRadius()));
			}
			if (Object.op_Implicit((Object)(object)prefab) && RailBridge.IsStation(prefab))
			{
				stations.AddRange(RailBridge.StationBoxes(prefab, item));
			}
		}
	}

	internal Region Get(Vector2s zone)
	{
		//IL_0013: Unknown result type (might be due to invalid IL or missing references)
		//IL_002f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0030: Unknown result type (might be due to invalid IL or missing references)
		//IL_0035: Unknown result type (might be due to invalid IL or missing references)
		//IL_0045: 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_011c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0127: Unknown result type (might be due to invalid IL or missing references)
		//IL_019a: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ed: Unknown result type (might be due to invalid IL or missing references)
		//IL_0327: Unknown result type (might be due to invalid IL or missing references)
		if (regions.TryGetValue(zone, out var r))
		{
			return r;
		}
		r = new Region
		{
			Center = ZoneSystem.GetZonePos(zone)
		};
		if (compilers.TryGetValue(zone, out var value))
		{
			if (value.Count != 1)
			{
				throw new InvalidOperationException(L.Key("raa_earthworks_002"));
			}
			r.Zdo = value[0];
		}
		Region region = r;
		ZDO zdo = r.Zdo;
		region.Before = ((zdo != null) ? zdo.GetByteArray(ZDOVars.s_TCData, (byte[])null) : null);
		r.Data = TerrainData.Read((r.Before == null) ? null : Utils.Decompress(r.Before), Pitch * Pitch);
		r.Loaded = Heightmap.FindHeightmap(r.Center);
		if (Object.op_Implicit((Object)(object)r.Loaded) && Vector3.Distance(((Component)r.Loaded).transform.position, r.Center) > 0.01f)
		{
			r.Loaded = null;
		}
		if (!Object.op_Implicit((Object)(object)r.Loaded) && legacy.Any((Tuple<Vector3, float> m) => (double)Math.Abs(m.Item1.x - r.Center.x) <= (double)((float)Width * Scale) * 0.5 + (double)m.Item2 && (double)Math.Abs(m.Item1.z - r.Center.z) <= (double)((float)Width * Scale) * 0.5 + (double)m.Item2))
		{
			throw new InvalidOperationException(L.Key("raa_earthworks_003"));
		}
		TerrainComp val = (Object.op_Implicit((Object)(object)r.Loaded) ? TerrainComp.FindTerrainCompiler(r.Center) : null);
		if (Object.op_Implicit((Object)(object)val))
		{
			Earthworks.ReloadCompiler.Invoke(val, null);
		}
		if (Object.op_Implicit((Object)(object)r.Loaded))
		{
			r.Loaded.Regenerate();
		}
		r.Base = HeightmapBuilder.instance.RequestTerrainSync(r.Center, Width, Scale, false, WorldGenerator.instance);
		r.Current = new double[Pitch * Pitch];
		r.Protection = new byte[Pitch * Pitch];
		for (int num = 0; num < Pitch; num++)
		{
			for (int num2 = 0; num2 < Pitch; num2++)
			{
				int num3 = num * Pitch + num2;
				r.Current[num3] = r.Center.y + (Object.op_Implicit((Object)(object)r.Loaded) ? r.Loaded.GetHeight(num2, num) : (r.Base.m_baseHeights[num3] + Math.Max(-8f, Math.Min(8f, r.Data.Level[num3] + r.Data.Smooth[num3]))));
			}
		}
		regions[zone] = r;
		return r;
	}

	internal double Height(V point)
	{
		//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)
		Region region = Get(ZoneSystem.GetZone(RailBridge.U(point)));
		double num = (point.x - (double)region.Center.x) / (double)Scale + (double)Width * 0.5;
		double num2 = (point.z - (double)region.Center.z) / (double)Scale + (double)Width * 0.5;
		int num3 = Math.Max(0, Math.Min(Width - 1, (int)Math.Floor(num)));
		int num4 = Math.Max(0, Math.Min(Width - 1, (int)Math.Floor(num2)));
		double num5 = Math.Max(0.0, Math.Min(1.0, num - (double)num3));
		double num6 = Math.Max(0.0, Math.Min(1.0, num2 - (double)num4));
		return (Vertex(region, num3, num4) * (1.0 - num5) + Vertex(region, num3 + 1, num4) * num5) * (1.0 - num6) + (Vertex(region, num3, num4 + 1) * (1.0 - num5) + Vertex(region, num3 + 1, num4 + 1) * num5) * num6;
	}

	internal double Vertex(Region r, int x, int z)
	{
		return r.Current[z * Pitch + x];
	}

	internal double Cost(V[] path)
	{
		if (Evaluate(path, path, write: false, requireKnown: false, out var _, out var cost) != null)
		{
			return double.PositiveInfinity;
		}
		return cost;
	}

	internal void Validate(V[] path, V[] profile)
	{
		List<Region> result;
		double cost;
		string text = Evaluate(path, profile, write: false, requireKnown: false, out result, out cost);
		if (text != null)
		{
			throw new InvalidOperationException(text);
		}
	}

	internal List<Region> Grade(V[] path, V[] profile)
	{
		List<Region> result;
		double cost;
		string text = Evaluate(path, profile, write: true, requireKnown: true, out result, out cost);
		if (text != null)
		{
			throw new InvalidOperationException(text);
		}
		return result;
	}

	private string Evaluate(V[] path, V[] profile, bool write, bool requireKnown, out List<Region> result, out double cost)
	{
		//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
		//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
		//IL_0734: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
		//IL_0720: Unknown result type (might be due to invalid IL or missing references)
		//IL_010b: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
		result = new List<Region>();
		cost = 0.0;
		int num = 0;
		double num2 = Plugin.BedHalfWidth.Value;
		double num3 = Plugin.ShoulderWidth.Value;
		double num4 = num2 + num3;
		V p = path.Aggregate((V a, V b) => new V(Math.Min(a.x, b.x), 0.0, Math.Min(a.z, b.z))) - new V(num4, 0.0, num4);
		V p2 = path.Aggregate((V a, V b) => new V(Math.Max(a.x, b.x), 0.0, Math.Max(a.z, b.z))) + new V(num4, 0.0, num4);
		Vector2s zone = ZoneSystem.GetZone(RailBridge.U(p));
		Vector2s zone2 = ZoneSystem.GetZone(RailBridge.U(p2));
		Vector2s val = default(Vector2s);
		for (int num5 = zone.y; num5 <= zone2.y; num5++)
		{
			for (int num6 = zone.x; num6 <= zone2.x; num6++)
			{
				((Vector2s)(ref val))..ctor(num6, num5);
				if (requireKnown && !RailBridge.Generated(ZoneSystem.GetZonePos(val)))
				{
					return L.Key("raa_earthworks_004");
				}
				Region region = Get(val);
				if (write && region.Zdo != null && region.Zdo.GetOwner() != 0L && !region.Zdo.IsOwner())
				{
					return L.Key("raa_earthworks_005");
				}
				int num7 = Math.Max(0, (int)Math.Ceiling((p.x - (double)region.Center.x) / (double)Scale + (double)Width * 0.5));
				int num8 = Math.Min(Width, (int)Math.Floor((p2.x - (double)region.Center.x) / (double)Scale + (double)Width * 0.5));
				int num9 = Math.Max(0, (int)Math.Ceiling((p.z - (double)region.Center.z) / (double)Scale + (double)Width * 0.5));
				int num10 = Math.Min(Width, (int)Math.Floor((p2.z - (double)region.Center.z) / (double)Scale + (double)Width * 0.5));
				for (int num11 = num9; num11 <= num10; num11++)
				{
					for (int num12 = num7; num12 <= num8; num12++)
					{
						V p3 = new V((double)region.Center.x + ((double)num12 - (double)Width * 0.5) * (double)Scale, 0.0, (double)region.Center.z + ((double)num11 - (double)Width * 0.5) * (double)Scale);
						if (!Grading.Project(p3, path, out var distance, out var height) || distance > num4)
						{
							continue;
						}
						int num13 = num11 * Pitch + num12;
						if (region.Protection[num13] == 0)
						{
							region.Protection[num13] = (byte)((!existingRails.ProtectsGround(p3) && !stations.Any((StructureBox box) => box.ProtectsGround(p3))) ? 1u : 2u);
						}
						if (region.Protection[num13] == 2)
						{
							continue;
						}
						double num14 = Vertex(region, num12, num11);
						Grading.Project(p3, profile, out var _, out height, extendEnds: true);
						double num15 = height - RailBridge.BedOffset;
						if (Plugin.Bridges.Value)
						{
							V point = BridgeRules.Nearest(p3, path);
							double num16 = Height(point);
							double num17 = ZoneSystem.instance.m_waterLevel;
							if (num16 < num17 - 0.5 && point.y < num17 + 0.8)
							{
								return L.Key("raa_bridge_water");
							}
							if (BridgeRules.Elevated(point.y - RailBridge.BedOffset, num16, num17, Plugin.BridgeThreshold.Value))
							{
								if (point.y - RailBridge.BedOffset - num16 > (double)Plugin.MaxPierHeight.Value)
								{
									return L.Key("raa_bridge_depth");
								}
								if (num14 <= num15)
								{
									continue;
								}
							}
						}
						if (num14 < (double)ZoneSystem.instance.m_waterLevel - 0.5)
						{
							continue;
						}
						double target;
						double delta;
						switch (Grading.Evaluate(num14, num15, distance, num2, num3, region.Data.Level[num13], region.Data.Smooth[num13], Plugin.MaxEarthwork.Value, out target, out delta))
						{
						case Grading.Limit.TooMuch:
							return L.Key("raa_earthworks_006") + Math.Round(p3.x) + ", " + Math.Round(p3.z) + L.Key("raa_earthworks_007") + Math.Round(Math.Abs(target - num14), 1) + L.Key("raa_earthworks_008") + Plugin.MaxEarthwork.Value + L.Key("raa_earthworks_009");
						case Grading.Limit.NativeLimit:
							return L.Key("raa_earthworks_010") + Math.Round(p3.x) + ", " + Math.Round(p3.z) + L.Key("raa_earthworks_011");
						case Grading.Limit.Outside:
							continue;
						}
						cost += (target - num14) * (target - num14);
						num++;
						if (write)
						{
							delta = Math.Max(-8.0, Math.Min(8.0, delta));
							if (Math.Abs(target - num14) > 0.005)
							{
								region.Data.Level[num13] = (float)delta;
								region.Data.Smooth[num13] = 0f;
								region.Data.Modified[num13] = true;
								region.Changed = true;
							}
							if (distance <= num2 && (!region.Data.Painted[num13] || region.Data.Colors[num13][0] != 1f || region.Data.Colors[num13][1] != 0f || region.Data.Colors[num13][2] != 0f || region.Data.Colors[num13][3] != 1f))
							{
								region.Data.Painted[num13] = true;
								region.Data.Colors[num13] = new float[4] { 1f, 0f, 0f, 1f };
								region.Changed = true;
							}
						}
					}
				}
				if (region.Changed)
				{
					result.Add(region);
				}
			}
		}
		cost /= Math.Max(1, num);
		return null;
	}

	internal void Apply(List<Region> changes, V center)
	{
		//IL_0085: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
		//IL_0116: Unknown result type (might be due to invalid IL or missing references)
		//IL_014f: Unknown result type (might be due to invalid IL or missing references)
		//IL_024b: Unknown result type (might be due to invalid IL or missing references)
		foreach (Region change in changes)
		{
			if (change.Zdo != null && !Same(change.Before, change.Zdo.GetByteArray(ZDOVars.s_TCData, (byte[])null)))
			{
				throw new InvalidOperationException(L.Key("raa_earthworks_012"));
			}
		}
		foreach (Region change2 in changes)
		{
			TerrainComp val = (Object.op_Implicit((Object)(object)change2.Loaded) ? TerrainComp.FindTerrainCompiler(change2.Center) : null);
			if (change2.Zdo == null)
			{
				if (Object.op_Implicit((Object)(object)change2.Loaded))
				{
					val = change2.Loaded.GetAndCreateTerrainCompiler();
					change2.Zdo = ((Component)val).GetComponent<ZNetView>().GetZDO();
				}
				else
				{
					GameObject terrainCompilerPrefab = Template.m_terrainCompilerPrefab;
					ZNetView component = terrainCompilerPrefab.GetComponent<ZNetView>();
					change2.Zdo = ZDOMan.instance.CreateNewZDO(change2.Center, StringExtensionMethods.GetStableHashCode(((Object)terrainCompilerPrefab).name));
					change2.Zdo.Persistent = true;
					change2.Zdo.Type = component.m_type;
					change2.Zdo.Distant = component.m_distant;
					change2.Zdo.SetPrefab(StringExtensionMethods.GetStableHashCode(((Object)terrainCompilerPrefab).name));
					change2.Zdo.SetRotation(Quaternion.identity);
				}
			}
			if (change2.Zdo.GetOwner() == 0L)
			{
				change2.Zdo.SetOwner(ZNet.GetUID());
			}
			if (!change2.Zdo.IsOwner())
			{
				throw new InvalidOperationException(L.Key("raa_earthworks_013"));
			}
			change2.Data.Operations++;
			change2.Data.X = (float)center.x;
			change2.Data.Y = (float)center.y;
			change2.Data.Z = (float)center.z;
			change2.Data.Radius = 35f;
			change2.Zdo.Set(ZDOVars.s_TCData, Utils.Compress(change2.Data.Write()));
			if (Object.op_Implicit((Object)(object)val))
			{
				Earthworks.ReloadCompiler.Invoke(val, null);
			}
			if (Object.op_Implicit((Object)(object)change2.Loaded))
			{
				change2.Loaded.Regenerate();
			}
			if (Object.op_Implicit((Object)(object)ClutterSystem.instance))
			{
				ClutterSystem.instance.ResetGrass(change2.Center, (float)Width * Scale * 0.5f);
			}
		}
	}

	private static bool Same(byte[] a, byte[] b)
	{
		if (a != null)
		{
			if (b != null)
			{
				return a.SequenceEqual(b);
			}
			return false;
		}
		return b == null;
	}
}
internal static class Earthworks
{
	private sealed class ClearAction
	{
		internal ZDO Zdo;

		internal Action Apply;
	}

	internal static readonly MethodInfo ReloadCompiler = AccessTools.Method(typeof(TerrainComp), "CheckLoad", (Type[])null, (Type[])null);

	private static readonly MethodInfo RockReload = AccessTools.Method(typeof(MineRock5), "CheckForUpdate", (Type[])null, (Type[])null);

	private static readonly MethodInfo OldRockReload = AccessTools.Method(typeof(MineRock), "UpdateVisability", (Type[])null, (Type[])null);

	internal static List<Segment> Plan(Goal start, List<Goal> goals)
	{
		//IL_016c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0171: Unknown result type (might be due to invalid IL or missing references)
		//IL_0190: Unknown result type (might be due to invalid IL or missing references)
		//IL_0195: Unknown result type (might be due to invalid IL or missing references)
		if (!Plugin.FollowTerrain.Value)
		{
			List<Segment> list = RoutePlanner.Plan(start.Position, start.Direction, goals, RailBridge.Primitives, Plugin.SearchNodes.Value, Plugin.MaxSegments.Value);
			ValidateRails(list);
			Ground ground = new Ground();
			if (Plugin.Bridges.Value)
			{
				for (int i = 0; i < list.Count; i++)
				{
					ground.Validate(RailBridge.Points(list[i]).ToArray(), Profile(list, i));
				}
			}
			BridgeWorks.Plan(list, ground);
			return list;
		}
		Ground ground2 = new Ground();
		List<Goal> list2 = TerrainSearch.Goals(start, goals, ground2.Height, ZoneSystem.instance.m_waterLevel, RailBridge.BedOffset, Plugin.Bridges.Value, Plugin.BridgeThreshold.Value);
		Stopwatch watch = Stopwatch.StartNew();
		SearchStats stats = new SearchStats();
		Func<Segment, double> terrainCost = delegate(Segment step)
		{
			if (watch.Elapsed.TotalSeconds > 8.0)
			{
				throw new InvalidOperationException(L.Key("raa_earthworks_014") + stats.Goal + " / " + stats.Goals + L.Key("raa_earthworks_015") + stats.Evaluated + L.Key("raa_earthworks_016") + stats.Rejected + L.Key("raa_earthworks_017"));
			}
			return TerrainSearch.Penalty(ground2.Cost(RailBridge.Points(step).ToArray()), step.Length);
		};
		List<Segment> list3;
		try
		{
			list3 = RoutePlanner.Plan(start.Position, start.Direction, list2, RailBridge.Primitives, Plugin.SearchNodes.Value, Plugin.MaxSegments.Value, terrainCost, stats);
		}
		finally
		{
			ManualLogSource log = Plugin.Log;
			if (log != null)
			{
				object[] obj = new object[20]
				{
					"Terrain search: A=", null, null, null, null, null, null, null, null, null,
					null, null, null, null, null, null, null, null, null, null
				};
				Vector3 val = RailBridge.U(start.Position);
				obj[1] = ((Vector3)(ref val)).ToString("F3");
				obj[2] = ", heading=";
				val = RailBridge.U(start.Direction);
				obj[3] = ((Vector3)(ref val)).ToString("F3");
				obj[4] = ", goals=";
				obj[5] = string.Join("; ", list2.Select(delegate(Goal g)
				{
					//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)
					object[] array = new object[5];
					Vector3 val2 = RailBridge.U(g.Position);
					array[0] = ((Vector3)(ref val2)).ToString("F3");
					array[1] = " tolerance=";
					array[2] = g.Tolerance;
					array[3] = " match=";
					array[4] = g.MatchDirection.ToString();
					return string.Concat(array);
				}).ToArray());
				obj[6] = ", leg=";
				obj[7] = stats.Goal;
				obj[8] = "/";
				obj[9] = stats.Goals;
				obj[10] = ", popped=";
				obj[11] = stats.Popped;
				obj[12] = ", expanded=";
				obj[13] = stats.Expanded;
				obj[14] = ", terrain checks=";
				obj[15] = stats.Evaluated;
				obj[16] = ", rejected=";
				obj[17] = stats.Rejected;
				obj[18] = ", ms=";
				obj[19] = watch.ElapsedMilliseconds;
				log.LogInfo((object)string.Concat(obj));
			}
		}
		ValidateRails(list3);
		if (Plugin.AutoGrade.Value || Plugin.Bridges.Value)
		{
			for (int num = 0; num < list3.Count; num++)
			{
				try
				{
					ground2.Validate(RailBridge.Points(list3[num]).ToArray(), Profile(list3, num));
				}
				catch (Exception ex)
				{
					throw new InvalidOperationException(L.Key("raa_earthworks_018") + (num + 1) + ": " + ex.Message);
				}
			}
		}
		BridgeWorks.Plan(list3, ground2);
		return list3;
	}

	internal static V[] Profile(IList<Segment> route, int index)
	{
		List<V> list = new List<V>();
		for (int i = Math.Max(0, index - 1); i <= Math.Min(route.Count - 1, index + 1); i++)
		{
			IEnumerable<V> enumerable = RailBridge.Points(route[i]);
			list.AddRange((list.Count == 0) ? enumerable : enumerable.Skip(1));
		}
		return list.ToArray();
	}

	private static void ValidateRails(List<Segment> route)
	{
		//IL_001e: Unknown result type (might be due to invalid IL or missing references)
		for (int i = 0; i < route.Count; i++)
		{
			ZDO val = RailBridge.Existing(route[i], null, -1);
			HashSet<ZDOID> hashSet = new HashSet<ZDOID>();
			if (val != null)
			{
				hashSet.Add(val.m_uid);
			}
			string text = RailsConflict(route[i], hashSet);
			if (text != null)
			{
				throw new InvalidOperationException(L.Key("raa_earthworks_019") + (i + 1) + ": " + text + L.Key("raa_earthworks_020"));
			}
		}
	}

	private static string RailsConflict(Segment step, HashSet<ZDOID> own)
	{
		//IL_0033: Unknown result type (might be due to invalid IL or missing references)
		//IL_01c4: Unknown result type (might be due to invalid IL or missing references)
		//IL_01c9: Unknown result type (might be due to invalid IL or missing references)
		//IL_020a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0227: Unknown result type (might be due to invalid IL or missing references)
		List<Tuple<ZDO, V[]>> rails = new List<Tuple<ZDO, V[]>>();
		foreach (ZDO track in RailBridge.Tracks)
		{
			V v = RailBridge.V3(track.GetPosition());
			V v2 = (step.Start + step.End) * 0.5;
			double num = step.Length * 0.5 + 100.0;
			if (Math.Abs(v.x - v2.x) > num || Math.Abs(v.z - v2.z) > num)
			{
				continue;
			}
			GameObject prefab = ZNetScene.instance.GetPrefab(track.GetPrefab());
			if (!Object.op_Implicit((Object)(object)prefab))
			{
				continue;
			}
			foreach (V[] item2 in RailBridge.WorldLines(prefab, track))
			{
				rails.Add(Tuple.Create<ZDO, V[]>(track, item2));
			}
		}
		HashSet<int> ignored = new HashSet<int>(from i in Enumerable.Range(0, rails.Count)
			where own.Contains(rails[i].Item1.m_uid)
			select i);
		int num2 = new TrackCorridor(rails.Select((Tuple<ZDO, V[]> r) => r.Item2), (double)Plugin.BedHalfWidth.Value + 0.5).Conflict(RailBridge.Points(step).ToArray(), ignored);
		if (num2 >= 0)
		{
			ZDO item = rails[num2].Item1;
			Vector3 position = item.GetPosition();
			GameObject prefab2 = ZNetScene.instance.GetPrefab(item.GetPrefab());
			return L.Key("raa_earthworks_021") + ((Object)prefab2).name + L.Key("raa_earthworks_022") + Math.Round(position.x) + ", " + Math.Round(position.z) + L.Key("raa_earthworks_023");
		}
		return null;
	}

	internal static string Prepare(Segment step, HashSet<ZDOID> own, V[] profile)
	{
		//IL_0031: 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_0130: Unknown result type (might be due to invalid IL or missing references)
		//IL_01d2: Unknown result type (might be due to invalid IL or missing references)
		//IL_0208: Unknown result type (might be due to invalid IL or missing references)
		//IL_020d: Unknown result type (might be due to invalid IL or missing references)
		//IL_024c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0268: Unknown result type (might be due to invalid IL or missing references)
		//IL_098b: Unknown result type (might be due to invalid IL or missing references)
		//IL_031c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0322: Invalid comparison between Unknown and I4
		//IL_04fe: Unknown result type (might be due to invalid IL or missing references)
		//IL_051b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0867: Unknown result type (might be due to invalid IL or missing references)
		//IL_0884: Unknown result type (might be due to invalid IL or missing references)
		V[] path = RailBridge.Sample(step, 0.5);
		V[] array = path;
		for (int i = 0; i < array.Length; i++)
		{
			if (!RailBridge.Generated(RailBridge.U(array[i])))
			{
				return L.Key("raa_earthworks_024");
			}
		}
		V[] pierPath = BridgeRules.Clearance(step.Piers);
		double pierRadius = ((step.Piers == null || step.Piers.Count == 0) ? 0.0 : (step.Piers.Max((Pier pier) => pier.Radius) + 0.15));
		if (!Object.op_Implicit((Object)(object)Player.m_localPlayer))
		{
			return L.Key("raa_earthworks_025");
		}
		array = path;
		for (int i = 0; i < array.Length; i++)
		{
			if (!PrivateArea.CheckAccess(RailBridge.U(array[i]), 0f, false, false))
			{
				return L.Key("raa_earthworks_026");
			}
		}
		array = pierPath;
		for (int i = 0; i < array.Length; i++)
		{
			if (!PrivateArea.CheckAccess(RailBridge.U(array[i]), 0f, false, false))
			{
				return L.Key("raa_earthworks_026");
			}
		}
		string text = RailsConflict(step, own);
		if (text != null)
		{
			return text;
		}
		List<ClearAction> list = new List<ClearAction>();
		double width = Plugin.BedHalfWidth.Value + Plugin.ShoulderWidth.Value;
		ZDO[] array2 = RailBridge.All.ToArray();
		foreach (ZDO zdo in array2)
		{
			if (own.Contains(zdo.m_uid) || Registry.IsMarker(zdo.GetPrefab()))
			{
				continue;
			}
			Vector3 position = zdo.GetPosition();
			double num = step.Length * 0.5 + 100.0;
			V v = (step.Start + step.End) * 0.5;
			if (Math.Abs((double)position.x - v.x) > num || Math.Abs((double)position.z - v.z) > num)
			{
				continue;
			}
			GameObject prefab = ZNetScene.instance.GetPrefab(zdo.GetPrefab());
			if (!Object.op_Implicit((Object)(object)prefab))
			{
				continue;
			}
			bool flag = Object.op_Implicit((Object)(object)prefab.GetComponent<MineRock5>());
			bool flag2 = Object.op_Implicit((Object)(object)prefab.GetComponent<MineRock>());
			Destructible component = prefab.GetComponent<Destructible>();
			bool num2 = Object.op_Implicit((Object)(object)prefab.GetComponent<TreeBase>()) || Object.op_Implicit((Object)(object)prefab.GetComponent<TreeLog>()) || (Object.op_Implicit((Object)(object)component) && (int)component.m_destructibleType == 2);
			ObstacleFeatures obstacleFeatures = ObstacleFeatures.None;
			if (num2)
			{
				obstacleFeatures |= ObstacleFeatures.Tree;
			}
			if (flag2 || flag)
			{
				obstacleFeatures |= ObstacleFeatures.Rock;
			}
			if (Object.op_Implicit((Object)(object)component))
			{
				obstacleFeatures |= ObstacleFeatures.Destructible;
			}
			if (Object.op_Implicit((Object)(object)prefab.GetComponent<Pickable>()))
			{
				obstacleFeatures |= ObstacleFeatures.Pickable;
			}
			if (Object.op_Implicit((Object)(object)prefab.GetComponentInChildren<Piece>(true)))
			{
				obstacleFeatures |= ObstacleFeatures.Piece;
			}
			if (Object.op_Implicit((Object)(object)prefab.GetComponentInChildren<Container>(true)))
			{
				obstacleFeatures |= ObstacleFeatures.Container;
			}
			if (Object.op_Implicit((Object)(object)prefab.GetComponentInChildren<Character>(true)))
			{
				obstacleFeatures |= ObstacleFeatures.Character;
			}
			if (Object.op_Implicit((Object)(object)prefab.GetComponent<TeleportWorld>()))
			{
				obstacleFeatures |= ObstacleFeatures.Portal;
			}
			ObstacleKind obstacleKind = ObstaclePolicy.Classify(((Object)prefab).name, obstacleFeatures);
			if (obstacleKind == ObstacleKind.Ignore)
			{
				continue;
			}
			bool flag3 = obstacleKind != ObstacleKind.Natural;
			if (RailBridge.IsTrack(prefab) && obstacleKind != ObstacleKind.Container && (obstacleFeatures & ObstacleFeatures.Portal) == 0)
			{
				if (prefab.GetComponentsInChildren<Collider>().Any((Collider c) => !c.isTrigger && Contact(prefab, zdo, c, pierPath, pierRadius, 0.0, 0.0)))
				{
					return L.Key("raa_bridge_blocked");
				}
				continue;
			}
			if (RailBridge.IsStation(prefab) && obstacleKind != ObstacleKind.Container && (obstacleFeatures & ObstacleFeatures.Portal) == 0)
			{
				StructureBox[] array3 = RailBridge.StationBoxes(prefab, zdo).ToArray();
				if (array3.Length == 0)
				{
					return L.Key("raa_earthworks_027");
				}
				if (array3.Any((StructureBox box) => path.Any((V point) => box.Touches(point, 1.125, 0.4, 5.5))))
				{
					return L.Key("raa_earthworks_028") + Math.Round(position.x) + ", " + Math.Round(position.z) + L.Key("raa_earthworks_029");
				}
				if (array3.Any((StructureBox box) => pierPath.Any((V point) => box.Touches(point, pierRadius, 0.0, 0.0))))
				{
					return L.Key("raa_bridge_blocked");
				}
				continue;
			}
			Collider[] colliders = ((flag2 && Object.op_Implicit((Object)(object)prefab.GetComponent<MineRock>().m_areaRoot)) ? prefab.GetComponent<MineRock>().m_areaRoot.GetComponentsInChildren<Collider>() : prefab.GetComponentsInChildren<Collider>());
			float num3 = (flag ? prefab.GetComponent<MineRock5>().m_health : 0f);
			float[] health = (flag ? RockHealth(zdo, colliders.Length, num3 + (float)Game.m_worldLevel * num3 * Game.instance.m_worldLevelMineHPMultiplier) : null);
			List<int> hits = new List<int>();
			for (int num4 = 0; num4 < colliders.Length; num4++)
			{
				if (!colliders[num4].isTrigger && (!flag || !(health[num4] <= 0f)) && (!flag2 || !(zdo.GetFloat(StringExtensionMethods.GetStableHashCode("Health" + num4), 1f) <= 0f)) && ((BridgeWorks.IsPier(zdo) ? Contact(prefab, zdo, colliders[num4], path, 1.125, 5.0, RailBridge.BedOffset - 0.05) : Contact(prefab, zdo, colliders[num4], path, width, flag3 ? 12 : 5)) || Contact(prefab, zdo, colliders[num4], pierPath, pierRadius, 0.0, 0.0)))
				{
					hits.Add(num4);
				}
			}
			if (hits.Count != 0)
			{
				if (flag3)
				{
					return string.Concat(L.Key("raa_earthworks_030"), obstacleKind switch
					{
						ObstacleKind.Building => L.Key("raa_earthworks_032"), 
						ObstacleKind.Container => L.Key("raa_earthworks_031"), 
						_ => L.Key("raa_earthworks_033"), 
					}, " «", ((Object)prefab).name, L.Key("raa_earthworks_034"), Math.Round(position.x), ", ", Math.Round(position.z), ".");
				}
				if (!Plugin.AutoClear.Value)
				{
					return L.Key("raa_earthworks_035");
				}
				if (zdo.GetOwner() != 0L && !zdo.IsOwner())
				{
					return L.Key("raa_earthworks_036");
				}
				ZDO target = zdo;
				list.Add(new ClearAction
				{
					Zdo = target,
					Apply = delegate
					{
						Clear(target, prefab, colliders.Length, hits, health);
					}
				});
			}
		}
		Ground ground = new Ground();
		List<Ground.Region> list2 = null;
		BridgeWorks.Validate(step, ground);
		if (Plugin.AutoGrade.Value)
		{
			list2 = ground.Grade(RailBridge.Points(step).ToArray(), profile);
		}
		else
		{
			array = path;
			float num5 = default(float);
			for (int i = 0; i < array.Length; i++)
			{
				V p = array[i];
				if (Heightmap.GetHeight(RailBridge.U(p), ref num5) && p.y < (double)num5 + 0.1)
				{
					return L.Key("raa_earthworks_037");
				}
			}
		}
		if (list.Count > 0)
		{
			foreach (ClearAction item in list.Take(24))
			{
				if (item.Zdo.GetOwner() == 0L)
				{
					item.Zdo.SetOwner(ZNet.GetUID());
				}
				item.Apply();
			}
			return L.Key("raa_earthworks_038") + Math.Min(24, list.Count) + L.Key("raa_earthworks_039") + list.Count + L.Key("raa_earthworks_040");
		}
		if (list2 != null)
		{
			ground.Apply(list2, (step.Start + step.End) * 0.5);
		}
		return null;
	}

	private static float[] RockHealth(ZDO zdo, int count, float initial)
	{
		//IL_002e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0034: Expected O, but got Unknown
		float[] array = Enumerable.Repeat(initial, count).ToArray();
		string text = zdo.GetString(ZDOVars.s_health, "");
		if (string.IsNullOrEmpty(text))
		{
			return array;
		}
		ZPackage val = new ZPackage(Convert.FromBase64String(text));
		if (val.ReadInt() != count)
		{
			throw new InvalidOperationException(L.Key("raa_earthworks_041"));
		}
		for (int i = 0; i < count; i++)
		{
			array[i] = val.ReadSingle();
		}
		return array;
	}

	private static void Clear(ZDO zdo, GameObject prefab, int count, List<int> hits, float[] health)
	{
		//IL_0035: Unknown result type (might be due to invalid IL or missing references)
		//IL_007c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0082: Expected O, but got Unknown
		if (!zdo.IsOwner())
		{
			throw new InvalidOperationException(L.Key("raa_earthworks_042"));
		}
		GameObject val = ZNetScene.instance.FindInstance(zdo.m_uid);
		if (health != null)
		{
			foreach (int hit in hits)
			{
				health[hit] = 0f;
			}
			ZPackage val2 = new ZPackage();
			val2.Write(count);
			foreach (float num in health)
			{
				val2.Write(num);
			}
			zdo.Set(ZDOVars.s_health, Convert.ToBase64String(val2.GetArray()));
			if (Object.op_Implicit((Object)(object)val))
			{
				RockReload.Invoke(val.GetComponent<MineRock5>(), null);
			}
			if (health.Any((float h) => h > 0f))
			{
				return;
			}
		}
		else if (Object.op_Implicit((Object)(object)prefab.GetComponent<MineRock>()))
		{
			foreach (int hit2 in hits)
			{
				zdo.Set(StringExtensionMethods.GetStableHashCode("Health" + hit2), 0f);
			}
			if (Object.op_Implicit((Object)(object)val))
			{
				OldRockReload.Invoke(val.GetComponent<MineRock>(), null);
			}
			if (Enumerable.Range(0, count).Any((int num2) => zdo.GetFloat(StringExtensionMethods.GetStableHashCode("Health" + num2), 1f) > 0f) || !prefab.GetComponent<MineRock>().m_removeWhenDestroyed)
			{
				return;
			}
		}
		if (Object.op_Implicit((Object)(object)val))
		{
			ZNetScene.instance.Destroy(val);
		}
		else
		{
			ZDOMan.instance.DestroyZDO(zdo);
		}
	}

	private static bool Contact(GameObject prefab, ZDO zdo, Collider collider, V[] path, double width, double clearance, double below = double.NaN)
	{
		//IL_0028: 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_0045: 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_005b: 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_0089: Unknown result type (might be due to invalid IL or missing references)
		//IL_0103: Unknown result type (might be due to invalid IL or missing references)
		//IL_010a: 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_0114: Unknown result type (might be due to invalid IL or missing references)
		//IL_0117: Unknown result type (might be due to invalid IL or missing references)
		//IL_011d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0122: Unknown result type (might be due to invalid IL or missing references)
		//IL_0124: Unknown result type (might be due to invalid IL or missing references)
		//IL_012f: 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_013f: 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_0149: Unknown result type (might be due to invalid IL or missing references)
		//IL_014f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0154: Unknown result type (might be due to invalid IL or missing references)
		//IL_0159: Unknown result type (might be due to invalid IL or missing references)
		//IL_015f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0173: Unknown result type (might be due to invalid IL or missing references)
		//IL_0178: Unknown result type (might be due to invalid IL or missing references)
		//IL_017d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0188: Unknown result type (might be due to invalid IL or missing references)
		//IL_0197: Unknown result type (might be due to invalid IL or missing references)
		//IL_019c: 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_01b1: Unknown result type (might be due to invalid IL or missing references)
		//IL_01bb: Unknown result type (might be due to invalid IL or missing references)
		//IL_01c0: Unknown result type (might be due to invalid IL or missing references)
		//IL_01c5: Unknown result type (might be due to invalid IL or missing references)
		//IL_01c9: Unknown result type (might be due to invalid IL or missing references)
		//IL_01d5: Unknown result type (might be due to invalid IL or missing references)
		//IL_01e2: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ef: Unknown result type (might be due to invalid IL or missing references)
		//IL_01fb: Unknown result type (might be due to invalid IL or missing references)
		//IL_0208: Unknown result type (might be due to invalid IL or missing references)
		//IL_0215: Unknown result type (might be due to invalid IL or missing references)
		//IL_0221: Unknown result type (might be due to invalid IL or missing references)
		//IL_022e: 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_00a2: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e1: 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_0293: Unknown result type (might be due to invalid IL or missing references)
		//IL_02a1: 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_02c3: Unknown result type (might be due to invalid IL or missing references)
		//IL_02d0: Unknown result type (might be due to invalid IL or missing references)
		//IL_02d7: Unknown result type (might be due to invalid IL or missing references)
		//IL_02ec: 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)
		BoxCollider val = (BoxCollider)(object)((collider is BoxCollider) ? collider : null);
		CapsuleCollider val2 = (CapsuleCollider)(object)((collider is CapsuleCollider) ? collider : null);
		SphereCollider val3 = (SphereCollider)(object)((collider is SphereCollider) ? collider : null);
		MeshCollider val4 = (MeshCollider)(object)((collider is MeshCollider) ? collider : null);
		Bounds bounds = default(Bounds);
		if (Object.op_Implicit((Object)(object)val))
		{
			((Bounds)(ref bounds))..ctor(val.center, val.size);
		}
		else if (Object.op_Implicit((Object)(object)val2))
		{
			Vector3 val5 = Vector3.one * (val2.radius * 2f);
			((Vector3)(ref val5))[val2.direction] = Math.Max(val2.height, val2.radius * 2f);
			((Bounds)(ref bounds))..ctor(val2.center, val5);
		}
		else if (Object.op_Implicit((Object)(object)val3))
		{
			((Bounds)(ref bounds))..ctor(val3.center, Vector3.one * val3.radius * 2f);
		}
		else
		{
			if (!Object.op_Implicit((Object)(object)val4) || !Object.op_Implicit((Object)(object)val4.sharedMesh))
			{
				return false;
			}
			bounds = val4.sharedMesh.bounds;
		}
		float num = zdo.GetFloat(ZDOVars.s_scaleScalarHash, 1f);
		Vector3 vec = zdo.GetVec3(ZDOVars.s_scaleHash, Vector3.one * num);
		Matrix4x4 val6 = Matrix4x4.TRS(zdo.GetPosition(), zdo.GetRotation(), vec) * prefab.transform.worldToLocalMatrix * ((Component)collider).transform.localToWorldMatrix;
		Vector3 val7 = ((Matrix4x4)(ref val6)).MultiplyPoint3x4(((Bounds)(ref bounds)).center);
		Vector3 val8 = ((Matrix4x4)(ref val6)).MultiplyVector(new Vector3(((Bounds)(ref bounds)).extents.x, 0f, 0f));
		Vector3 val9 = ((Matrix4x4)(ref val6)).MultiplyVector(new Vector3(0f, ((Bounds)(ref bounds)).extents.y, 0f));
		Vector3 val10 = ((Matrix4x4)(ref val6)).MultiplyVector(new Vector3(0f, 0f, ((Bounds)(ref bounds)).extents.z));
		Vector3 val11 = default(Vector3);
		((Vector3)(ref val11))..ctor(Math.Abs(val8.x) + Math.Abs(val9.x) + Math.Abs(val10.x), Math.Abs(val8.y) + Math.Abs(val9.y) + Math.Abs(val10.y), Math.Abs(val8.z) + Math.Abs(val9.z) + Math.Abs(val10.z));
		double num2 = (double.IsNaN(below) ? (RailBridge.BedOffset + ((clearance > 5.0) ? 8.0 : 0.4)) : below);
		for (int i = 0; i < path.Length; i++)
		{
			V v = path[i];
			if (Math.Abs(v.x - (double)val7.x) <= (double)val11.x + width && Math.Abs(v.z - (double)val7.z) <= (double)val11.z + width && (double)(val7.y - val11.y) <= v.y + clearance && (double)(val7.y + val11.y) >= v.y - num2)
			{
				return true;
			}
		}
		return false;
	}
}
internal static class Keys
{
	internal static readonly int State = StringExtensionMethods.GetStableHashCode("local.raa.state.v1");

	internal static readonly int Name = StringExtensionMethods.GetStableHashCode("local.raa.line");

	internal static readonly int JobTag = StringExtensionMethods.GetStableHashCode("local.raa.job");

	internal static readonly int Index = StringExtensionMethods.GetStableHashCode("local.raa.segment");

	internal static readonly int Error = StringExtensionMethods.GetStableHashCode("local.raa.error");
}
internal static class Engine
{
	internal static OrderBook ReadBook(ZDO site)
	{
		return OrderBook.Read(site.GetString(Keys.State, ""), site.GetString(Keys.Name, "Линия1"));
	}

	internal static Job Read(ZDO site)
	{
		return ReadBook(site).Current;
	}

	private static void SaveBook(ZDO site, OrderBook book)
	{
		if (!site.IsOwner())
		{
			throw new InvalidOperationException(L.Key("raa_engine_001"));
		}
		string text = book.Write();
		site.Set(Keys.State, text);
		site.Set(Keys.Name, book.Selected);
	}

	internal static void Save(ZDO site, Job job)
	{
		OrderBook orderBook = ReadBook(site);
		orderBook.Put(job);
		SaveBook(site, orderBook);
	}

	internal static string[] Lines(ZDO site)
	{
		long owner = site.GetLong(ZDOVars.s_creator, 0L);
		return ReadBook(site).Jobs.Select((Job j) => j.Line).Concat(from z in RailBridge.All
			where (z.GetPrefab() == Registry.StartHash || z.GetPrefab() == Registry.EndHash) && z.GetLong(ZDOVars.s_creator, 0L) == owner
			select z.GetString(Keys.Name, "Линия1")).Distinct<string>(StringComparer.OrdinalIgnoreCase)
			.OrderBy<string, string>((string s) => s, StringComparer.OrdinalIgnoreCase)
			.ToArray();
	}

	internal static void Command(ZDO site, string command, string line)
	{
		//IL_023b: Unknown result type (might be due to invalid IL or missing references)
		//IL_024d: Unknown result type (might be due to invalid IL or missing references)
		OrderBook orderBook = ReadBook(site);
		Job current = orderBook.Current;
		if (command == "line")
		{
			orderBook.Select(line);
			SaveBook(site, orderBook);
			return;
		}
		if (!string.Equals(JobRules.Line(line), orderBook.Selected, StringComparison.OrdinalIgnoreCase))
		{
			throw new InvalidOperationException(L.Key("raa_engine_002"));
		}
		if (command == "pause")
		{
			current.Active = false;
			current.Status = L.Key("raa_engine_003");
			Save(site, current);
			return;
		}
		if (current.Pending != null)
		{
			throw new InvalidOperationException(L.Key("raa_engine_004"));
		}
		switch (command)
		{
		case "remove_markers":
			RemoveMarkers(site, current);
			break;
		case "plan":
		{
			if (current.Active)
			{
				throw new InvalidOperationException(L.Key("raa_engine_005"));
			}
			string name = current.Line;
			long owner = site.GetLong(ZDOVars.s_creator, 0L);
			List<ZDO> source = RailBridge.All.Where((ZDO z) => Registry.IsMarker(z.GetPrefab()) && z.GetLong(ZDOVars.s_creator, 0L) == owner).ToList();
			List<ZDO> list = source.Where((ZDO z) => z.GetPrefab() == Registry.StartHash && string.Equals(z.GetString(Keys.Name, "Линия1"), name, StringComparison.OrdinalIgnoreCase)).ToList();
			List<ZDO> list2 = source.Where((ZDO z) => z.GetPrefab() == Registry.EndHash && string.Equals(z.GetString(Keys.Name, "Линия1"), name, StringComparison.OrdinalIgnoreCase)).ToList();
			if (list.Count != 1 || list2.Count != 1)
			{
				throw new InvalidOperationException(L.Key("raa_engine_006") + name + L.Key("raa_engine_007"));
			}
			Goal a = RailBridge.Anchor(list[0], start: true);
			Goal item = RailBridge.Anchor(list2[0], start: false);
			ZDO[] ways = source.Where((ZDO z) => z.GetPrefab() == Registry.WayHash).ToArray();
			bool automatic;
			int[] array = WaypointRules.Select(name, ways.Select((ZDO z) => z.GetString(Keys.Name, "Линия1:1")).ToArray(), ways.Select((ZDO z) => RailBridge.V3(z.GetPosition())).ToArray(), RailBridge.V3(list[0].GetPosition()), RailBridge.V3(list2[0].GetPosition()), out automatic, Lines(site));
			List<Goal> list3 = (from i in array
				select new Goal
				{
					Position = RailBridge.Anchor(ways[i], start: false).Position,
					Tolerance = 1.25
				} into g
				where !RailAnchors.AlreadyPassed(g.Position, a)
				select g).ToList();
			string text = L.Key("raa_engine_008") + list3.Count + " / " + array.Length + (automatic ? L.Key("raa_engine_009") : L.Key("raa_engine_010"));
			ManualLogSource log = Plugin.Log;
			if (log != null)
			{
				log.LogInfo((object)L.Render("Planning line «" + name + "»: " + text, "English"));
			}
			list3.Add(item);
			List<Segment> list4 = ((list3.Count == 1 && (list3[0].Position - a.Position).Length <= list3[0].Tolerance) ? new List<Segment>() : Earthworks.Plan(a, list3));
			current = new Job
			{
				Line = name,
				Segments = list4,
				PlanRevision = 4
			};
			current.Estimate = (from c in list4.SelectMany(RailBridge.Costs)
				group c by c.Prefab into g
				select new Cost
				{
					Prefab = g.Key,
					Amount = g.Sum((Cost c) => c.Amount)
				}).ToList();
			current.Status = ((list4.Count == 0) ? L.Key("raa_engine_011") : (L.Key("raa_engine_012") + list4.Count + L.Key("raa_engine_013") + Math.Round(list4.Sum((Segment s) => s.Length)) + L.Key("raa_engine_014") + text + ((a.ExistingLength > 0.0) ? (L.Key("raa_engine_015") + Math.Round(a.ExistingLength) + L.Key("raa_engine_016")) : "") + L.Key("raa_engine_017")));
			current.Write();
			Save(site, current);
			break;
		}
		case "start":
			if (current.Segments.Count == 0 || current.Index >= current.Segments.Count)
			{
				throw new InvalidOperationException(L.Key("raa_engine_018"));
			}
			if (current.PlanRevision < 4)
			{
				throw new InvalidOperationException(L.Key("raa_engine_019"));
			}
			current.Active = true;
			current.Status = L.Key("raa_engine_020");
			current.Next = 0.0;
			Save(site, current);
			break;
		default:
			throw new InvalidOperationException(L.Key("raa_engine_021"));
		}
	}

	private static void RemoveMarkers(ZDO site, Job job)
	{
		//IL_0057: Unknown result type (might be due to invalid IL or missing references)
		//IL_0179: Unknown result type (might be due to invalid IL or missing references)
		long owner = site.GetLong(ZDOVars.s_creator, 0L);
		string[] knownLines = Lines(site);
		ZDO[] array = RailBridge.All.Where((ZDO z) => z.IsValid() && MarkerRules.Matches(z.GetPrefab(), Registry.StartHash, Registry.EndHash, z.GetLong(ZDOVars.s_creator, 0L), owner, z.GetString(Keys.Name, (z.GetPrefab() == Registry.WayHash) ? "Линия1:1" : "Линия1"), job.Line, Registry.WayHash, knownLines)).ToArray();
		ZDO[] array2 = array;
		foreach (ZDO val in array2)
		{
			if (!PrivateArea.CheckAccess(val.GetPosition(), 0f, false, false))
			{
				throw new InvalidOperationException(L.Key("raa_engine_022"));
			}
			if (val.GetOwner() != 0L && !val.IsOwner())
			{
				throw new InvalidOperationException(L.Key("raa_engine_023"));
			}
		}
		MarkerRules.PauseAndInvalidate(job, array.Count((ZDO z) => z.GetPrefab() == Registry.StartHash), array.Count((ZDO z) => z.GetPrefab() == Registry.EndHash), array.Count((ZDO z) => z.GetPrefab() == Registry.WayHash));
		string status = job.Status;
		job.Status = L.Key("raa_engine_024");
		Save(site, job);
		array2 = array;
		foreach (ZDO val2 in array2)
		{
			if (val2.GetOwner() == 0L)
			{
				val2.SetOwner(ZNet.GetUID());
			}
			GameObject val3 = ZNetScene.instance.FindInstance(val2.m_uid);
			if (Object.op_Implicit((Object)(object)val3))
			{
				ZNetScene.instance.Destroy(val3);
			}
			else
			{
				ZDOMan.instance.DestroyZDO(val2);
			}
		}
		job.Status = status;
		Save(site, job);
		ManualLogSource log = Plugin.Log;
		if (log != null)
		{
			log.LogInfo((object)L.Render("Removed " + array.Length + " route markers of line «" + job.Line + "»; rails and stock retained.", "English"));
		}
	}

	internal static void Tick(ZDO site, double now)
	{
		if (!site.IsOwner())
		{
			return;
		}
		OrderBook orderBook;
		try
		{
			orderBook = ReadBook(site);
		}
		catch (Exception ex)
		{
			site.Set(Keys.Error, ex.Message);
			return;
		}
		Job job = orderBook.Jobs.FirstOrDefault((Job j) => j.Pending != null);
		if (job != null)
		{
			TickOrder(site, job, now);
			return;
		}
		foreach (Job item in orderBook.Jobs.Where((Job j) => j.Active))
		{
			TickOrder(site, item, now);
			if (ReadBook(site).Jobs.Any((Job j) => j.Pending != null))
			{
				break;
			}
		}
	}

	private static void TickOrder(ZDO site, Job job, double now)
	{
		//IL_0189: Unknown result type (might be due to invalid IL or missing references)
		if (job.Pending == null && job.Active && job.PlanRevision < 4)
		{
			job.Active = false;
			job.Status = L.Key("raa_engine_025");
			Save(site, job);
		}
		else
		{
			if (job.Pending == null && (!job.Active || job.Index >= job.Segments.Count || job.Next > now))
			{
				return;
			}
			if (!RailBridge.Busy(site))
			{
				try
				{
					if (job.Pending != null)
					{
						Recover(site, job, now);
					}
					else
					{
						Segment segment = job.Segments[job.Index];
						bool flag = RailBridge.Existing(segment, job.Id, job.Index) != null;
						HashSet<ZDOID> hashSet = new HashSet<ZDOID>(from z in RailBridge.All
							where z.GetString(Keys.JobTag, "") == job.Id
							select z.m_uid);
						ZDO val = RailBridge.Existing(segment, job.Id, job.Index);
						if (val != null)
						{
							hashSet.Add(val.m_uid);
						}
						string text = RailBridge.TextInventory(site);
						List<Cost> costs = (flag ? new List<Cost>() : RailBridge.Costs(segment));
						string text2 = (flag ? text : RailBridge.Debit(text, costs));
						string text3 = Earthworks.Prepare(segment, hashSet, Earthworks.Profile(job.Segments, job.Index));
						if (text3 != null)
						{
							Status(site, job, text3);
						}
						else if (flag)
						{
							Complete(site, job, now, L.Key("raa_engine_027"));
						}
						else
						{
							job.Pending = new Pending
							{
								Index = job.Index,
								Before = text,
								After = text2,
								Costs = costs
							};
							Save(site, job);
							RailBridge.StoreInventory(site, text2);
							BridgeWorks.Create(segment, job, site.GetLong(ZDOVars.s_creator, 0L));
							RailBridge.Create(segment, job, site.GetLong(ZDOVars.s_creator, 0L), costs);
							Complete(site, job, now, L.Key("raa_engine_028"));
						}
					}
					return;
				}
				catch (Exception ex)
				{
					Status(site, job, ex.GetBaseException().Message);
					return;
				}
			}
			Status(site, job, L.Key("raa_engine_026"));
		}
	}

	private static void Recover(ZDO site, Job job, double now)
	{
		Pending pending = job.Pending;
		Segment step = job.Segments[job.Index];
		bool exists = RailBridge.Existing(step, job.Id, job.Index) != null;
		switch (JobRules.Recover(RailBridge.TextInventory(site), pending, exists))
		{
		case Recovery.Blocked:
			job.Active = false;
			job.Status = L.Key("raa_engine_029");
			Save(site, job);
			return;
		case Recovery.Retry:
			job.Pending = null;
			job.Status = L.Key("raa_engine_030");
			Save(site, job);
			return;
		case Recovery.Debit:
			RailBridge.StoreInventory(site, pending.After);
			break;
		case Recovery.Create:
		{
			HashSet<ZDOID> own = new HashSet<ZDOID>(from z in RailBridge.All
				where z.GetString(Keys.JobTag, "") == job.Id
				select z.m_uid);
			string text = Earthworks.Prepare(step, own, Earthworks.Profile(job.Segments, job.Index));
			if (text != null)
			{
				Status(site, job, text);
				return;
			}
			BridgeWorks.Create(step, job, site.GetLong(ZDOVars.s_creator, 0L));
			RailBridge.Create(step, job, site.GetLong(ZDOVars.s_creator, 0L), pending.Costs);
			break;
		}
		}
		Complete(site, job, now, L.Key("raa_engine_031"));
	}

	private static void Complete(ZDO site, Job job, double now, string status)
	{
		double length = job.Segments[job.Index].Length;
		job.Index++;
		job.Pending = null;
		job.Next = now + Math.Max(1.0, length * (double)Plugin.SecondsPerMeter.Value);
		job.Status = ((job.Index == job.Segments.Count) ? L.Key("raa_engine_032") : status);
		if (job.Index == job.Segments.Count)
		{
			job.Active = false;
		}
		Save(site, job);
	}

	private static void Status(ZDO site, Job job, string status)
	{
		//IL_009e: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
		if (job.Status == status)
		{
			return;
		}
		job.Status = status;
		Save(site, job);
		if (job.Index < job.Segments.Count)
		{
			Segment segment = job.Segments[job.Index];
			ManualLogSource log = Plugin.Log;
			if (log != null)
			{
				object[] obj = new object[12]
				{
					"Line «", job.Line, "», section ", job.Index, " ", segment.Prefab, ", start=", null, null, null,
					null, null
				};
				Vector3 val = RailBridge.U(segment.Start);
				obj[7] = ((Vector3)(ref val)).ToString("F3");
				obj[8] = ", end=";
				val = RailBridge.U(segment.End);
				obj[9] = ((Vector3)(ref val)).ToString("F3");
				obj[10] = ": ";
				obj[11] = status;
				log.LogInfo((object)L.Render(string.Concat(obj), "English"));
			}
		}
	}
}
[Serializable]
public sealed class Cost
{
	public string Prefab;

	public int Amount;
}
[Serializable]
public sealed class Pending
{
	public int Index;

	public string Before;

	public string After;

	public List<Cost> Costs;
}
[Serializable]
public sealed class Job
{
	public const int CurrentPlanRevision = 4;

	public int Format = 1;

	public int Index;

	public int PlanRevision;

	public string Id = Guid.NewGuid().ToString("N");

	public string Line = "Линия1";

	public string Status = L.Key("raa_job_001");

	public bool Active;

	public double Next;

	public List<Segment> Segments = new List<Segment>();

	public List<Cost> Estimate = new List<Cost>();

	public Pending Pending;

	public static Job Read(string json)
	{
		if (string.IsNullOrEmpty(json))
		{
			return new Job();
		}
		if (json.Length > 131072)
		{
			throw new InvalidOperationException(L.Key("raa_job_002"));
		}
		Job job = JsonConvert.DeserializeObject<Job>(json);
		if (job == null || job.Format != 1 || job.PlanRevision < 0 || job.PlanRevision > 4 || string.IsNullOrEmpty(job.Id) || job.Segments == null || job.Segments.Count > 512 || job.Index < 0 || job.Index > job.Segments.Count || (job.Pending != null && job.Pending.Index != job.Index))
		{
			throw new InvalidOperationException(L.Key("raa_job_003"));
		}
		if (job.Estimate == null || double.IsNaN(job.Next) || double.IsInfinity(job.Next) || (job.Pending != null && (job.Index >= job.Segments.Count || job.Pending.Before == null || job.Pending.After == null || job.Pending.Costs == null)))
		{
			throw new InvalidOperationException(L.Key("raa_job_004"));
		}
		foreach (Segment segment in job.Segments)
		{
			if (segment == null || segment.Prefab == null || !segment.Origin.Finite || !segment.Start.Finite || !segment.End.Finite || !segment.Direction.Finite || segment.Length <= 0.0 || segment.Length > 200.0 || double.IsNaN(segment.Length) || double.IsNaN(segment.Yaw) || double.IsInfinity(segment.Yaw))
			{
				throw new InvalidOperationException(L.Key("raa_job_005"));
			}
		}
		foreach (Segment segment2 in job.Segments)
		{
			if (segment2.Piers != null && (segment2.Piers.Count > 64 || segment2.Piers.Exists((Pier p) => !BridgeRules.Valid(p))))
			{
				throw new InvalidOperationException(L.Key("raa_bridge_saved"));
			}
		}
		foreach (Cost item in job.Estimate)
		{
			Validate(item);
		}
		if (job.Pending != null)
		{
			foreach (Cost cost in job.Pending.Costs)
			{
				Validate(cost);
			}
		}
		return job;
	}

	private static void Validate(Cost c)
	{
		if (c == null || string.IsNullOrEmpty(c.Prefab) || c.Amount < 0 || c.Amount > 1000000)
		{
			throw new InvalidOperationException(L.Key("raa_job_006"));
		}
	}

	public string Write()
	{
		string text = JsonConvert.SerializeObject((object)this, (Formatting)0);
		if (text.Length > 131072)
		{
			throw new InvalidOperationException(L.Key("raa_job_007"));
		}
		return text;
	}
}
public enum Recovery
{
	Retry,
	Create,
	Debit,
	Commit,
	Blocked
}
public static class JobRules
{
	public static Recovery Recover(string inventory, Pending pending, bool exists)
	{
		if (pending == null)
		{
			return Recovery.Retry;
		}
		if (inventory == pending.After)
		{
			if (!exists)
			{
				return Recovery.Create;
			}
			return Recovery.Commit;
		}
		if (inventory == pending.Before)
		{
			if (!exists)
			{
				return Recovery.Retry;
			}
			return Recovery.Debit;
		}
		return Recovery.Blocked;
	}

	public static string Line(string input)
	{
		input = (input ?? "").Trim();
		if (input.Length == 0 || input.Length > 40 || input.IndexOfAny(new char[4] { '\n', '\r', '<', '>' }) >= 0)
		{
			throw new InvalidOperationException(L.Key("raa_job_008"));
		}
		return input;
	}
}
internal static class L
{
	private const char Open = '\ue000';

	private const char Close = '\ue001';

	private static readonly Dictionary<string, string> English = Read("English");

	private static readonly Dictionary<string, string> Russian = Read("Russian");

	private static readonly Dictionary<string, string> Legacy = LegacyMap();

	internal static Func<string> Language = () => "English";

	internal static string Selected
	{
		get
		{
			if (!(Language() == "Russian"))
			{
				return "English";
			}
			return "Russian";
		}
	}

	internal static string Json(string language)
	{
		using Stream stream = typeof(L).Assembly.GetManifestResourceStream("RailroadsAutomation." + language + ".json");
		using StreamReader streamReader = new StreamReader(stream ?? throw new InvalidDataException("Missing localization: " + language));
		return streamReader.ReadToEnd();
	}

	private static Dictionary<string, string> Read(string language)
	{
		return JsonConvert.DeserializeObject<Dictionary<string, string>>(Json(language));
	}

	private static Dictionary<string, string> LegacyMap()
	{
		Dictionary<string, string> dictionary = new Dictionary<string, string>(StringComparer.Ordinal);
		foreach (KeyValuePair<string, string> item in Russian)
		{
			if (!dictionary.ContainsKey(item.Value))
			{
				dictionary.Add(item.Value, item.Key);
			}
		}
		return dictionary;
	}

	internal static string Key(string key)
	{
		return "\ue000" + key + "\ue001";
	}

	internal static string Native(string message)
	{
		if (message.Length > 2 && message[0] == '\ue000' && message[message.Length - 1] == '\ue001')
		{
			return "$" + message.Substring(1, message.Length - 2);
		}
		return Render(message);
	}

	internal static string Render(string message)
	{
		return Render(message, Selected);
	}

	internal static string Render(string message, string language)
	{
		if (string.IsNullOrEmpty(message))
		{
			return message ?? "";
		}
		Dictionary<string, string> dictionary = ((language == "Russian") ? Russian : English);
		if (message.IndexOf('\ue000') < 0)
		{
			if (Legacy.TryGetValue(message, out var value))
			{
				return dictionary[value];
			}
			return message;
		}
		StringBuilder stringBuilder = new StringBuilder();
		int num = 0;
		while (num < message.Length)
		{
			int num2 = message.IndexOf('\ue000', num);
			if (num2 < 0)
			{
				stringBuilder.Append(message, num, message.Length - num);
				break;
			}
			stringBuilder.Append(message, num, num2 - num);
			int num3 = message.IndexOf('\ue001', num2 + 1);
			if (num3 < 0)
			{
				stringBuilder.Append(message, num2, message.Length - num2);
				break;
			}
			string text = message.Substring(num2 + 1, num3 - num2 - 1);
			if (!dictionary.TryGetValue(text, out var value2) && !English.TryGetValue(text, out value2))
			{
				value2 = "[" + text + "]";
			}
			stringBuilder.Append(value2);
			num = num3 + 1;
		}
		return stringBuilder.ToString();
	}
}
internal static class MarkerRules
{
	internal static bool Matches(int prefab, int start, int end, long creator, long owner, string markerLine, string selected, int waypoint = -1, string[] knownLines = null)
	{
		if (creator == owner)
		{
			if ((prefab != start && prefab != end) || !string.Equals(markerLine, selected, StringComparison.OrdinalIgnoreCase))
			{
				if (prefab == waypoint)
				{
					return WaypointRules.Belongs(markerLine, selected, knownLines);
				}
				return false;
			}
			return true;
		}
		return false;
	}

	internal static void PauseAndInvalidate(Job job, int starts, int ends, int ways = 0)
	{
		if (job.Pending != null)
		{
			throw new InvalidOperationException(L.Key("raa_markerrules_001"));
		}
		if (starts < 0 || ends < 0 || ways < 0)
		{
			throw new InvalidOperationException(L.Key("raa_markerrules_002"));
		}
		job.Active = false;
		job.PlanRevision = 0;
		job.Status = ((starts + ends + ways == 0) ? L.Key("raa_markerrules_003") : (L.Key("raa_markerrules_004") + job.Line + L.Key("raa_markerrules_005") + starts + L.Key("raa_markerrules_006") + ends + L.Key("raa_markerrules_008") + ways + L.Key("raa_markerrules_007")));
	}
}
[Flags]
public enum ObstacleFeatures
{
	None = 0,
	Tree = 1,
	Rock = 2,
	Destructible = 4,
	Pickable = 8,
	Piece = 0x10,
	Container = 0x20,
	Character = 0x40,
	Portal = 0x80
}
public enum ObstacleKind
{
	Ignore,
	Natural,
	Building,
	Container,
	Unknown
}
public static class ObstaclePolicy
{
	public static ObstacleKind Classify(string name, ObstacleFeatures f)
	{
		if ((f & ObstacleFeatures.Character) != ObstacleFeatures.None)
		{
			return ObstacleKind.Ignore;
		}
		if ((f & ObstacleFeatures.Container) != ObstacleFeatures.None)
		{
			return ObstacleKind.Container;
		}
		if ((f & ObstacleFeatures.Portal) != ObstacleFeatures.None)
		{
			return ObstacleKind.Building;
		}
		string text = new string((name ?? "").ToLowerInvariant().Where(char.IsLetterOrDigit).ToArray());
		bool flag = (f & (ObstacleFeatures.Destructible | ObstacleFeatures.Pickable)) != 0;
		bool flag2 = text == "bcawildchilipepper" || text == "bcawildtomato";
		bool flag3 = text == "pickableshrimptraprtd";
		bool flag4 = flag && (flag2 || flag3 || new string[14]
		{
			"shrub", "firtreesmall", "bush01", "bush02", "raspberrybush", "blueberrybush", "cloudberrybush", "sapling", "pickablestone", "pickablebranch",
			"pickableflint", "pickablemushroom", "pickablethistle", "pickabledandelion"
		}.Any(text.StartsWith));
		if ((f & (ObstacleFeatures.Tree | ObstacleFeatures.Rock)) != 0 || flag4)
		{
			return ObstacleKind.Natural;
		}
		if ((f & ObstacleFeatures.Piece) != ObstacleFeatures.None)
		{
			return ObstacleKind.Building;
		}
		if (flag && !text.Contains("runestone") && !text.Contains("dungeon") && new string[13]
		{
			"stub", "stump", "bush", "beech", "firtree", "pinetree", "oak", "rock", "stone", "branch",
			"thistle", "dandelion", "mushroom"
		}.Any(text.Contains))
		{
			return ObstacleKind.Natural;
		}
		if ((f & ObstacleFeatures.Destructible) == 0)
		{
			return ObstacleKind.Ignore;
		}
		return ObstacleKind.Unknown;
	}
}
[Serializable]
public sealed class OrderBook
{
	public int Format = 2;

	public string Selected = "Линия1";

	public List<Job> Jobs = new List<Job>();

	public Job Current => Jobs.First((Job j) => Same(j.Line, Selected));

	private static bool Same(string a, string b)
	{
		return string.Equals(a, b, StringComparison.OrdinalIgnoreCase);
	}

	public static OrderBook Read(string json, string fallback)
	{
		if ((json ?? "").Length > 524288)
		{
			throw new InvalidOperationException(L.Key("raa_orderbook_001"));
		}
		OrderBook orderBook;
		if (string.IsNullOrEmpty(json) || (int?)JObject.Parse(json)["Format"] == 1)
		{
			Job job = Job.Read(json);
			orderBook = new OrderBook();
			job.Line = JobRules.Line((job.Segments.Count > 0 || job.Pending != null) ? job.Line : fallback);
			orderBook.Selected = job.Line;
			orderBook.Jobs.Add(job);
		}
		else
		{
			orderBook = JsonConvert.DeserializeObject<OrderBook>(json);
		}
		if (orderBook == null || orderBook.Format != 2 || orderBook.Jobs == null || orderBook.Jobs.Count == 0 || orderBook.Jobs.Count > 32)
		{
			throw new InvalidOperationException(L.Key("raa_orderbook_002"));
		}
		orderBook.Selected = JobRules.Line(orderBook.Selected);
		HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
		foreach (Job job2 in orderBook.Jobs)
		{
			if (job2 == null)
			{
				throw new InvalidOperationException(L.Key("raa_orderbook_003"));
			}
			job2.Line = JobRules.Line(job2.Line);
			Job.Read(job2.Write());
			if (!hashSet.Add(job2.Line))
			{
				throw new InvalidOperationException(L.Key("raa_orderbook_004"));
			}
		}
		if (!hashSet.Contains(orderBook.Selected))
		{
			throw new InvalidOperationException(L.Key("raa_orderbook_005"));
		}
		if (orderBook.Jobs.Count((Job j) => j.Pending != null) > 1)
		{
			throw new InvalidOperationException(L.Key("raa_orderbook_006"));
		}
		return orderBook;
	}

	public void Select(string line)
	{
		line = JobRules.Line(line);
		Job job = Jobs.FirstOrDefault((Job j) => Same(j.Line, line));
		if (job == null)
		{
			if (Jobs.Count >= 32)
			{
				throw new InvalidOperationException(L.Key("raa_orderbook_007"));
			}
			job = new Job
			{
				Line = line
			};
			Jobs.Add(job);
		}
		Selected = job.Line;
	}

	public void Put(Job job)
	{
		int num = Jobs.FindIndex((Job j) => Same(j.Line, job.Line));
		if (num < 0)
		{
			throw new InvalidOperationException(L.Key("raa_orderbook_008"));
		}
		Jobs[num] = job;
	}

	public string Write()
	{
		string text = JsonConvert.SerializeObject((object)new { Format, Selected, Jobs }, (Formatting)0);
		if (text.Length > 524288)
		{
			throw new InvalidOperationException(L.Key("raa_orderbook_009"));
		}
		return text;
	}
}
[BepInPlugin("local.railroads.automation", "Railroads Automation", "0.3.2")]
[BepInDependency("vladasz.railroads", "0.2.2")]
[BepInDependency(/*Could not decode attribute arguments.*/)]
[NetworkCompatibility(/*Could not decode attribute arguments.*/)]
public sealed class Plugin : BaseUnityPlugin
{
	[HarmonyPatch(typeof(Container), "Interact")]
	private static class OpenSite
	{
		private static bool Prefix(Container __instance, Humanoid character, bool hold, bool alt, ref bool __result)
		{
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			Site component = ((Component)__instance).GetComponent<Site>();
			if (!Object.op_Implicit((Object)(object)component) || alt)
			{
				return true;
			}
			__result = true;
			if (hold || (Object)(object)character != (Object)(object)Player.m_localPlayer)
			{
				return false;
			}
			if (!ZNet.instance.IsServer())
			{
				((Character)Player.m_localPlayer).Message((MessageType)2, L.Render(L.Key("raa_plugin_001")), 0, (Sprite)null, false);
				return false;
			}
			if (!((Component)__instance).GetComponent<Piece>().IsCreator() || !PrivateArea.CheckAccess(((Component)__instance).transform.position, 0f, true, false))
			{
				return false;
			}
			Window.Show(component);
			return false;
		}
	}

	[HarmonyPatch(typeof(Container), "GetHoverText")]
	private static class SiteHover
	{
		private static void Postfix(Container __instance, ref string __result)
		{
			if (Object.op_Implicit((Object)(object)((Component)__instance).GetComponent<Site>()))
			{
				__result = Localization.instance.Localize(L.Render(L.Key("raa_plugin_002")));
			}
		}
	}

	[HarmonyPatch(typeof(Player), "TakeInput")]
	private static class InputBlock
	{
		private static void Postfix(ref bool __result)
		{
			if (Window.Open)
			{
				__result = false;
			}
		}
	}

	[HarmonyPatch(typeof(Menu), "IsVisible")]
	private static class MenuVisible
	{
		private static void Postfix(ref bool __result)
		{
			if (Window.Open)
			{
				__result = true;
			}
		}
	}

	[HarmonyPatch(typeof(Menu), "Update")]
	private static class MenuBlock
	{
		private static bool Prefix()
		{
			if (!Window.Open)
			{
				return !Window.Suppress;
			}
			return false;
		}
	}

	[HarmonyPatch(typeof(InventoryGui), "Update")]
	private static class InventoryBlock
	{
		private static bool Prefix()
		{
			if (!Window.Open)
			{
				return !Window.Suppress;
			}
			return false;
		}
	}

	[HarmonyPatch(typeof(GameCamera), "UpdateMouseCapture")]
	private static class CursorCapture
	{
		private static bool Prefix()
		{
			if (!Window.Open)
			{
				return true;
			}
			ZCursor.LockState = (CursorLockMode)0;
			ZCursor.Show();
			return false;
		}
	}

	internal static ConfigEntry<int> SearchNodes;

	internal static ConfigEntry<int> MaxSegments;

	internal static ConfigEntry<float> SecondsPerMeter;

	internal static ConfigEntry<bool> IgnoreSupport;

	internal static ConfigEntry<bool> AutoClear;

	internal static ConfigEntry<bool> AutoGrade;

	internal static ConfigEntry<bool> FollowTerrain;

	internal static ConfigEntry<float> BedHalfWidth;

	internal static ConfigEntry<float> MaxEarthwork;

	internal static ConfigEntry<float> ShoulderWidth;

	internal static ConfigEntry<bool> Bridges;

	internal static ConfigEntry<float> BridgeThreshold;

	internal static ConfigEntry<float> MaxPierHeight;

	private Harmony harmony;

	private ZDOMan world;

	private float next;

	private float errorAt;

	internal static ManualLogSource Log;

	private void Awake()
	{
		//IL_003a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0040: Expected O, but got Unknown
		//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ae: Expected O, but got Unknown
		//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e7: Expected O, but got Unknown
		//IL_011a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0124: Expected O, but got Unknown
		//IL_01d7: Unknown result type (might be due to invalid IL or missing references)
		//IL_01e1: Expected O, but got Unknown
		//IL_0214: Unknown result type (might be due to invalid IL or missing references)
		//IL_021e: Expected O, but got Unknown
		//IL_0251: Unknown result type (might be due to invalid IL or missing references)
		//IL_025b: Expected O, but got Unknown
		//IL_02ae: Unknown result type (might be due to invalid IL or missing references)
		//IL_02b8: Expected O, but got Unknown
		//IL_02eb: Unknown result type (might be due to invalid IL or missing references)
		//IL_02f5: Expected O, but got Unknown
		//IL_031b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0325: Expected O, but got Unknown
		Log = ((BaseUnityPlugin)this).Logger;
		L.Language = () => (Localization.instance == null) ? "English" : Localization.instance.GetSelectedLanguage();
		CustomLocalization val = new CustomLocalization(((BaseUnityPlugin)this).Info.Metadata);
		val.AddJsonFile("English", L.Json("English"));
		val.AddJsonFile("Russian", L.Json("Russian"));
		LocalizationManager.Instance.AddLocalization(val);
		SearchNodes = ((BaseUnityPlugin)this).Config.Bind<int>("Construction", "Search Nodes", 12000, new ConfigDescription("Bounded planner search per waypoint.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1000, 30000), Array.Empty<object>()));
		MaxSegments = ((BaseUnityPlugin)this).Config.Bind<int>("Construction", "Maximum Segments", 256, new ConfigDescription("Split longer railways into several orders.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(8, 512), Array.Empty<object>()));
		SecondsPerMeter = ((BaseUnityPlugin)this).Config.Bind<float>("Construction", "Seconds Per Meter", 0.5f, new ConfigDescription("Construction time, while the world is running.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 60f), Array.Empty<object>()));
		IgnoreSupport = ((BaseUnityPlugin)this).Config.Bind<bool>("Construction", "Support Automated Tracks", true, "Prevent structural collapse only for tracks created by this add-on. Damage/removal unchanged. Disabling may collapse bridges.");
		AutoClear = ((BaseUnityPlugin)this).Config.Bind<bool>("Earthworks", "Auto Clear", true, "Clear natural trees, stumps, bushes and intersecting rock parts. No harvesting rewards. Buildings/containers stop construction.");
		AutoGrade = ((BaseUnityPlugin)this).Config.Bind<bool>("Earthworks", "Auto Grade", true, "Prepare a graded bed below each track section; retain native terrain save data and water bottoms.");
		FollowTerrain = ((BaseUnityPlugin)this).Config.Bind<bool>("Earthworks", "Follow Terrain", true, "New route calculations follow land elevations using native slope sections. Recalculate old plans to use this.");
		BedHalfWidth = ((BaseUnityPlugin)this).Config.Bind<float>("Earthworks", "Bed Half Width", 2f, new ConfigDescription("Track bed half-width in metres, before shoulders.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1.5f, 4f), Array.Empty<object>()));
		ShoulderWidth = ((BaseUnityPlugin)this).Config.Bind<float>("Earthworks", "Shoulder Width", 3f, new ConfigDescription("Smooth transition from the bed to surrounding terrain in metres.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 6f), Array.Empty<object>()));
		MaxEarthwork = ((BaseUnityPlugin)this).Config.Bind<float>("Earthworks", "Maximum Cut Or Fill", 4f, new ConfigDescription("Maximum local cut/fill per vertex. Native cumulative +/-8m limit is also retained.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 7f), Array.Empty<object>()));
		Bridges = ((BaseUnityPlugin)this).Config.Bind<bool>("Bridges", "Automatic Bridges", true, "Keep a crossing above water/deep depressions and build stone piers. Recalculate plans after changing bridge settings.");
		BridgeThreshold = ((BaseUnityPlugin)this).Config.Bind<float>("Bridges", "Minimum Gap", 2f, new ConfigDescription("Use piers instead of filling a depression deeper than this many metres.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 7f), Array.Empty<object>()));
		MaxPierHeight = ((BaseUnityPlugin)this).Config.Bind<float>("Bridges", "Maximum Pier Height", 80f, new ConfigDescription("Maximum height from the seabed/ground to the track bed. Stone is charged for every native pillar block.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(4f, 128f), Array.Empty<object>()));
		try
		{
			RailBridge.Bind();
			PrefabManager.OnVanillaPrefabsAvailable += Registry.Register;
			Support.Bind();
			harmony = new Harmony("local.railroads.automation");
			harmony.PatchAll(typeof(Plugin).Assembly);
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Railroads Automation 0.3.2: stone bridges and RtDOcean scenery clearance; English and Russian localization ready.");
		}
		catch (Exception ex)
		{
			((BaseUnityPlugin)this).Logger.LogError((object)L.Render(ex.ToString(), "English"));
			((Behaviour)this).enabled = false;
		}
	}

	private void Update()
	{
		Window.Tick();
		if (ZDOMan.instance != world)
		{
			world = ZDOMan.instance;
			RailBridge.Reset();
			Window.Hide();
		}
		if (!Object.op_Implicit((Object)(object)ZNet.instance) || !Object.op_Implicit((Object)(object)ZNetScene.instance) || !Object.op_Implicit((Object)(object)ZoneSystem.instance) || WorldGenerator.instance == null || world == null || Time.unscaledTime < next)
		{
			return;
		}
		next = Time.unscaledTime + 1f;
		try
		{
			RailBridge.Ready();
			if (!ZNet.instance.IsServer())
			{
				return;
			}
			double timeSeconds = ZNet.instance.GetTimeSeconds();
			ZDO[] array = RailBridge.All.Where((ZDO z) => z.GetPrefab() == Registry.CampHash).ToArray();
			foreach (ZDO val in array)
			{
				if (val.GetOwner() == 0L && !RailBridge.Busy(val))
				{
					val.SetOwner(ZNet.GetUID());
				}
				if (val.IsOwner())
				{
					Engine.Tick(val, timeSeconds);
				}
			}
		}
		catch (Exception ex)
		{
			if (Time.unscaledTime >= errorAt)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("Construction paused: " + ex.GetBaseException().Message));
				errorAt = Time.unscaledTime + 15f;
			}
		}
	}

	private void OnDestroy()
	{
		Window.Dispose();
		PrefabManager.OnVanillaPrefabsAvailable -= Registry.Register;
		Harmony obj = harmony;
		if (obj != null)
		{
			obj.UnpatchSelf();
		}
	}
}
internal static class Support
{
	[HarmonyPatch(typeof(WearNTear), "UpdateSupport")]
	private static class Set
	{
		private static bool Prefix(WearNTear __instance)
		{
			if (!Protected(__instance))
			{
				return true;
			}
			float num = maximum(__instance);
			value.Invoke(__instance) = num;
			ZNetView component = ((Component)__instance).GetComponent<ZNetView>();
			if (component.IsOwner())
			{
				component.GetZDO().Set(ZDOVars.s_support, num);
			}
			return false;
		}
	}

	[HarmonyPatch(typeof(WearNTear), "HaveSupport")]
	private static class Have
	{
		private static void Postfix(WearNTear __instance, ref bool __result)
		{
			if (Protected(__instance))
			{
				__result = true;
			}
		}
	}

	[HarmonyPatch(typeof(WearNTear), "GetSupport")]
	private static class Get
	{
		private static void Postfix(WearNTear __instance, ref float __result)
		{
			if (Protected(__instance))
			{
				__result = maximum(__instance);
			}
		}
	}

	private static FieldRef<WearNTear, float> value;

	private static Func<WearNTear, float> maximum;

	internal static void Bind()
	{
		value = AccessTools.FieldRefAccess<WearNTear, float>("m_support");
		maximum = AccessTools.MethodDelegate<Func<WearNTear, float>>(AccessTools.Method(typeof(WearNTear), "GetMaxSupport", (Type[])null, (Type[])null), (object)null, true);
	}

	private static bool Protected(WearNTear wear)
	{
		ZNetView component = ((Component)wear).GetComponent<ZNetView>();
		if (Plugin.IgnoreSupport.Value && Object.op_Implicit((Object)(object)component) && component.IsValid())
		{
			return !string.IsNullOrEmpty(component.GetZDO().GetString(Keys.JobTag, ""));
		}
		return false;
	}
}
internal static class RailAnchors
{
	private sealed class End
	{
		internal int Line;

		internal int Side;

		internal V Point;

		internal V Outward;
	}

	private static End Endpoint(V[][] lines, int index, int side)
	{
		V[] array = lines[index];
		int num = ((side != 0) ? (array.Length - 1) : 0);
		return new End
		{
			Line = index,
			Side = side,
			Point = array[num],
			Outward = (array[num] - array[(side == 0) ? 1 : (num - 1)]).Unit
		};
	}

	private static List<End> Connections(End current, V[][] lines)
	{
		List<End> list = new List<End>();
		for (int i = 0; i < lines.Length; i++)
		{
			if (i == current.Line)
			{
				continue;
			}
			for (int j = 0; j < 2; j++)
			{
				End end = Endpoint(lines, i, j);
				if ((end.Point - current.Point).Length <= 0.3 && V.Dot(end.Outward, current.Outward) < -0.5)
				{
					list.Add(end);
				}
			}
		}
		return list;
	}

	internal static Goal Select(V requested, V heading, V[][] lines, bool[] allowed, bool start)
	{
		if (lines.Length != allowed.Length || lines.Any((V[] p) => p.Length < 2))
		{
			throw new InvalidOperationException(L.Key("raa_railanchors_001"));
		}
		End end = null;
		double num = 3.0;
		for (int num2 = 0; num2 < lines.Length; num2++)
		{
			if (!allowed[num2])
			{
				continue;
			}
			for (int num3 = 0; num3 < 2; num3++)
			{
				End end2 = Endpoint(lines, num2, num3);
				double length = (end2.Point - requested).Length;
				if (!(length >= num) && (!start || !(V.Dot(end2.Outward, heading) <= 0.2)) && (start || Connections(end2, lines).Count <= 0))
				{
					end = end2;
					num = length;
				}
			}
		}
		if (end == null)
		{
			foreach (V[] path in lines)
			{
				if (Distance(requested, path) < 1.25)
				{
					throw new InvalidOperationException(L.Key("raa_railanchors_002") + (start ? L.Key("raa_railanchors_003") : L.Key("raa_railanchors_004")) + L.Key("raa_railanchors_005"));
				}
			}
			return new Goal
			{
				Position = requested,
				Direction = heading,
				Tolerance = 1.25
			};
		}
		List<V[]> list = new List<V[]>();
		double num5 = 0.0;
		HashSet<string> hashSet = new HashSet<string>();
		if (start)
		{
			while (true)
			{
				if (!hashSet.Add(end.Line + ":" + end.Side))
				{
					throw new InvalidOperationException(L.Key("raa_railanchors_006"));
				}
				List<End> list2 = Connections(end, lines);
				if (list2.Count == 0)
				{
					break;
				}
				if (list2.Count != 1)
				{
					throw new InvalidOperationException(L.Key("raa_railanchors_007"));
				}
				End end3 = list2[0];
				V[] array = ((end3.Side == 0) ? lines[end3.Line] : lines[end3.Line].Reverse().ToArray());
				for (int num6 = 1; num6 < array.Length; num6++)
				{
					num5 += (array[num6] - array[num6 - 1]).Length;
				}
				if (num5 > 512.0 || list.Count >= 256)
				{
					throw new InvalidOperationException(L.Key("raa_railanchors_008"));
				}
				list.Add(array);
				end = Endpoint(lines, end3.Line, 1 - end3.Side);
			}
		}
		return new Goal
		{
			Position = end.Point,
			Direction = (start ? end.Outward : (-end.Outward)),
			MatchDirection = !start,
			Tolerance = 0.24,
			ExistingLength = num5,
			Passed = list.ToArray()
		};
	}

	private static double Distance(V point, V[] path)
	{
		double num = double.PositiveInfinity;
		for (int i = 0; i + 1 < path.Length; i++)
		{
			V v = path[i + 1] - path[i];
			double num2 = V.Dot(v, v);
			double num3 = ((num2 < 1E-12) ? 0.0 : Math.Max(0.0, Math.Min(1.0, V.Dot(point - path[i], v) / num2)));
			num = Math.Min(num, (point - path[i] - v * num3).Length);
		}
		return num;
	}

	internal static bool AlreadyPassed(V point, Goal start)
	{
		if (start.Passed != null)
		{
			return start.Passed.Any((V[] p) => Distance(point, p) <= 1.25);
		}
		return false;
	}
}
internal static class RailBridge
{
	internal static readonly string[] Allowed = new string[17]
	{
		"bf_track_straight_32", "bf_track_straight_16", "bf_track_straight_8", "bf_track_straight_4", "bf_track_straight", "bf_track_straight_1", "bf_track_curve_r8_22", "bf_track_curve_r8_45", "bf_track_curve_r8_90", "bf_track_curve_r16_22",
		"bf_track_curve_r16_45", "bf_track_slope_gentle", "bf_track_slope_steep", "bf_track_slope_bottom_gentle", "bf_track_slope_top_gentle", "bf_track_slope_bottom_steep", "bf_track_slope_top_steep"
	};

	private static Type railTrack;

	private static Type station;

	private static PropertyInfo lines;

	private static MethodInfo tracks;

	private static MethodInfo containerLoad;

	private static MethodInfo isGenerated;

	private static FieldInfo objects;

	private static readonly Dictionary<string, V[]> shapes = new Dictionary<string, V[]>();

	internal static readonly List<Primitive> Primitives = new List<Primitive>();

	internal static IEnumerable<ZDO> All
	{
		get
		{
			if (ZDOMan.instance != null)
			{
				return ((Dictionary<ZDOID, ZDO>)objects.GetValue(ZDOMan.instance)).Values;
			}
			return Enumerable.Empty<ZDO>();
		}
	}

	internal static List<ZDO> Tracks => (List<ZDO>)tracks.Invoke(null, null);

	internal static double BedOffset => shapes["bf_track_straight"][0].y;

	internal static V V3(Vector3 p)
	{
		//IL_0000: 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_000e: Unknown result type (might be due to invalid IL or missing references)
		return new V(p.x, p.y, p.z);
	}

	internal static Vector3 U(V p)
	{
		//IL_0015: Unknown result type (might be due to invalid IL or missing references)
		return new Vector3((float)p.x, (float)p.y, (float)p.z);
	}

	internal static void Bind()
	{
		Assembly assembly = ((object)Chainloader.PluginInfos["vladasz.railroads"].Instance).GetType().Assembly;
		railTrack = assembly.GetType("Blackforge.RailTrack", throwOnError: true);
		station = assembly.GetType("Blackforge.Station", throwOnError: true);
		lines = railTrack.GetProperty("Lines");
		tracks = assembly.GetType("Blackforge.Scan", throwOnError: true).GetProperty("Tracks").GetGetMethod();
		objects = AccessTools.Field(typeof(ZDOMan), "m_objectsByID");
		containerLoad = AccessTools.Method(typeof(Container), "Load", Type.EmptyTypes, (Type[])null);
		isGenerated = AccessTools.Method(typeof(ZoneSystem), "IsZoneGenerated", (Type[])null, (Type[])null);
		if (lines == null || tracks == null || objects == null || containerLoad == null || isGenerated == null)
		{
			throw new MissingMemberException(L.Key("raa_railbridge_001"));
		}
	}

	internal static void Ready()
	{
		if (Primitives.Count > 0)
		{
			return;
		}
		try
		{
			string[] allowed = Allowed;
			foreach (string text in allowed)
			{
				GameObject prefab = ZNetScene.instance.GetPrefab(text);
				if ((Object)(object)prefab == (Object)null)
				{
					throw new InvalidOperationException(L.Key("raa_railbridge_002") + text);
				}
				Component component = prefab.GetComponent(railTrack);
				List<Vector3[]> list = (List<Vector3[]>)lines.GetValue(component, null);
				if (list.Count != 1 || list[0].Length < 2)
				{
					throw new InvalidOperationException(L.Key("raa_railbridge_003") + text);
				}
				V[] array = list[0].Select(V3).ToArray();
				shapes[text] = array;
				double num = 0.0;
				for (int j = 1; j < array.Length; j++)
				{
					num += (array[j] - array[j - 1]).Length;
				}
				Primitives.Add(new Primitive
				{
					Prefab = text,
					Points = array,
					Length = num
				});
				Primitives.Add(new Primitive
				{
					Prefab = text,
					Points = array.Reverse().ToArray(),
					Length = num
				});
			}
		}
		catch
		{
			Reset();
			throw;
		}
	}

	internal static void Reset()
	{
		Primitives.Clear();
		shapes.Clear();
	}

	internal static List<Cost> Costs(Segment segment)
	{
		List<Cost> list = TrackCosts(segment);
		int num = BridgeRules.StoneCost(segment.Piers);
		if (num > 0)
		{
			list.Add(new Cost
			{
				Prefab = "Stone",
				Amount = num
			});
		}
		return (from c in list
			group c by c.Prefab into g
			select new Cost
			{
				Prefab = g.Key,
				Amount = g.Sum((Cost c) => c.Amount)
			}).ToList();
	}

	internal static List<Cost> TrackCosts(Segment segment)
	{
		if (!shapes.ContainsKey(segment.Prefab))
		{
			throw new InvalidOperationException(L.Key("raa_railbridge_004"));
		}
		return (from r in ZNetScene.instance.GetPrefab(segment.Prefab).GetComponent<Piece>().m_resources
			where Object.op_Implicit((Object)(object)r.m_resItem) && r.m_amount > 0
			select new Cost
			{
				Prefab = ((Object)((Component)r.m_resItem).gameObject).name,
				Amount = r.m_amount
			}).ToList();
	}

	internal static string TextInventory(ZDO site)
	{
		byte[] byteArray = site.GetByteArray(ZDOVars.s_items, (byte[])null);
		if (byteArray != null)
		{
			return Convert.ToBase64String(byteArray);
		}
		return "";
	}

	internal static Inventory Inventory(string text)
	{
		//IL_0008: Unknown result type (might be due to invalid IL or missing references)
		//IL_000e: Expected O, but got Unknown
		//IL_0020: Unknown result type (might be due to invalid IL or missing references)
		//IL_0026: Expected O, but got Unknown
		Inventory val = new Inventory("Railroads Automation", (Sprite)null, 8, 8);
		if (tex