Decompiled source of Trails v0.2.9

plugins/Trails.dll

Decompiled 2 days ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;
using UnityEngine.Rendering;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace Trails
{
	public static class Api
	{
		public static float CartFactor(Vagon cart)
		{
			if ((Object)(object)cart == (Object)null || !Rules.Enabled || !Walkers.CartFactor.TryGetValue(((Object)cart).GetInstanceID(), out var value))
			{
				return 1f;
			}
			if (!(value < 0.7f))
			{
				if (!(value > 1f))
				{
					return value;
				}
				return 1f;
			}
			return 0.7f;
		}
	}
	[HarmonyPatch]
	internal static class Effects
	{
		private const float MudSlowdown = 0.1f;

		private static bool Local(Character c)
		{
			if ((Object)(object)c != (Object)null && (Object)(object)c == (Object)(object)Player.m_localPlayer)
			{
				return Rules.Enabled;
			}
			return false;
		}

		internal static float Speed()
		{
			byte localRoad = Walkers.LocalRoad;
			if (localRoad != 0)
			{
				return 1f + (float)(Rules.RunBonus + Roads.ExtraSpeed(localRoad, Rules.RoadStep)) / 100f;
			}
			if (Walkers.LocalMuddy && Walkers.Wet > 0.05f)
			{
				return 1f - 0.1f * Walkers.Wet;
			}
			return 1f + (float)Rules.RunBonus / 100f * Walkers.LocalTrail;
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(Player), "GetRunSpeedFactor")]
		private static void Run(Player __instance, ref float __result)
		{
			if (Local((Character)(object)__instance))
			{
				__result *= Speed();
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(Player), "GetJogSpeedFactor")]
		private static void Jog(Player __instance, ref float __result)
		{
			if (Local((Character)(object)__instance))
			{
				__result *= Speed();
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(SEMan), "ModifyRunStaminaDrain")]
		private static void Stamina(SEMan __instance, ref float drain, bool minZero)
		{
			if (minZero && Local(__instance.m_character))
			{
				float num = ((Walkers.LocalRoad != 0) ? 1f : ((Walkers.LocalMuddy && Walkers.Wet > 0.05f) ? 0f : Walkers.LocalTrail));
				drain *= 1f - (float)Rules.StaminaSaving / 100f * num;
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(SEMan), "ModifyStaminaRegen")]
		private static void Regen(SEMan __instance, ref float staminaMultiplier)
		{
			if (Local(__instance.m_character) && Walkers.LocalRoad != 0)
			{
				staminaMultiplier *= 1f + Roads.Regen(Walkers.LocalRoad);
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(Character), "GetRunSpeedFactor")]
		private static void MountRun(Character __instance, ref float __result)
		{
			if (Walkers.MountFactor.TryGetValue(((Object)__instance).GetInstanceID(), out var value))
			{
				__result *= value;
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(Character), "GetJogSpeedFactor")]
		private static void MountJog(Character __instance, ref float __result)
		{
			if (Walkers.MountFactor.TryGetValue(((Object)__instance).GetInstanceID(), out var value))
			{
				__result *= value;
			}
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(Vagon), "SetMass")]
		private static void CartMass(Vagon __instance, ref float mass)
		{
			if (Walkers.CartFactor.TryGetValue(((Object)__instance).GetInstanceID(), out var value))
			{
				mass *= Mathf.Clamp(value, 0.7f, 1f);
			}
		}

		[HarmonyTranspiler]
		[HarmonyPatch(typeof(ClutterSystem), "GenerateVegPatch")]
		private static IEnumerable<CodeInstruction> Lay(IEnumerable<CodeInstruction> code)
		{
			MethodInfo cleared = AccessTools.Method(typeof(Heightmap), "IsCleared", new Type[1] { typeof(Vector3) }, (Type[])null);
			MethodInfo ours = AccessTools.Method(typeof(Effects), "GrassCleared", (Type[])null, (Type[])null);
			int n = 0;
			foreach (CodeInstruction item in code)
			{
				if ((item.opcode == OpCodes.Callvirt || item.opcode == OpCodes.Call) && object.Equals(item.operand, cleared))
				{
					n++;
					yield return CodeInstructionExtensions.MoveBlocksFrom(CodeInstructionExtensions.MoveLabelsFrom(new CodeInstruction(OpCodes.Call, (object)ours), item), item);
				}
				else
				{
					yield return item;
				}
			}
			if (n == 0)
			{
				Plugin.Warn("ClutterSystem.GenerateVegPatch has no IsCleared call (the game changed?): no grass thinning");
			}
		}

		private static bool GrassCleared(Heightmap hmap, Vector3 point)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			bool flag = hmap.IsCleared(point);
			if (flag || TrailRegion.Loaded.Count == 0 || !Plugin.Thinning.Value || !Rules.Enabled)
			{
				return flag;
			}
			try
			{
				float num = Stages.Thin(TrailRegion.WearAt(point));
				return num > 0f && Pattern(point) < num;
			}
			catch (Exception ex)
			{
				Plugin.Debug("thinning: " + ex.Message);
				return false;
			}
		}

		private static float Pattern(Vector3 p)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			int num = (Mathf.RoundToInt(p.x * 8f) * 73856093) ^ (Mathf.RoundToInt(p.z * 8f) * 19349663);
			int num2 = (num ^ (num >> 13)) * 1540483477;
			return (float)((num2 ^ (num2 >> 15)) & 0xFFFF) / 65536f;
		}
	}
	internal static class Gravel
	{
		private sealed class Patch
		{
			internal Mesh Mesh;

			internal int Version;

			internal float RemakeAt;

			internal Vector3 Centre;
		}

		private const float Reach = 40f;

		private const float RemakeEvery = 30f;

		private const int PerCell = 10;

		private static readonly Dictionary<long, Patch> patches = new Dictionary<long, Patch>();

		private static readonly List<long> gone = new List<long>();

		private static Material material;

		private static bool looked;

		private static int layer = -1;

		private static readonly List<Vector3> vertices = new List<Vector3>();

		private static readonly List<Vector3> normals = new List<Vector3>();

		private static readonly List<Vector2> uvs = new List<Vector2>();

		private static readonly List<int> triangles = new List<int>();

		internal static void Clear()
		{
			foreach (Patch value in patches.Values)
			{
				if ((Object)(object)value.Mesh != (Object)null)
				{
					Object.Destroy((Object)(object)value.Mesh);
				}
			}
			patches.Clear();
		}

		internal static void Update()
		{
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0164: Unknown result type (might be due to invalid IL or missing references)
			//IL_0169: Unknown result type (might be due to invalid IL or missing references)
			//IL_016a: Unknown result type (might be due to invalid IL or missing references)
			//IL_016f: 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_0200: Unknown result type (might be due to invalid IL or missing references)
			//IL_0201: Unknown result type (might be due to invalid IL or missing references)
			//IL_0206: Unknown result type (might be due to invalid IL or missing references)
			//IL_021d: Unknown result type (might be due to invalid IL or missing references)
			Camera mainCamera = Utils.GetMainCamera();
			if (!Plugin.GravelPebbles.Value || (Object)(object)mainCamera == (Object)null || TrailRegion.Loaded.Count == 0)
			{
				if (patches.Count > 0)
				{
					Clear();
				}
				return;
			}
			if (!looked)
			{
				Look();
			}
			if ((Object)(object)material == (Object)null)
			{
				return;
			}
			Vector3 position = ((Component)mainCamera).transform.position;
			float time = Time.time;
			int num = 0;
			gone.Clear();
			foreach (long key2 in patches.Keys)
			{
				if (!TrailRegion.Loaded.ContainsKey(key2))
				{
					gone.Add(key2);
				}
			}
			foreach (long item in gone)
			{
				if ((Object)(object)patches[item].Mesh != (Object)null)
				{
					Object.Destroy((Object)(object)patches[item].Mesh);
				}
				patches.Remove(item);
			}
			foreach (KeyValuePair<long, TrailRegion> item2 in TrailRegion.Loaded)
			{
				TrailRegion value = item2.Value;
				if ((Object)(object)value == (Object)null)
				{
					continue;
				}
				Vector3 val = ((Component)value).transform.position - position;
				if (((Vector3)(ref val)).sqrMagnitude > 5184f)
				{
					continue;
				}
				if (!patches.TryGetValue(item2.Key, out var value2))
				{
					Dictionary<long, Patch> dictionary = patches;
					long key = item2.Key;
					Patch obj = new Patch
					{
						Version = -1
					};
					value2 = obj;
					dictionary[key] = obj;
				}
				if ((value2.Version != value.Version || time >= value2.RemakeAt) && num < 2)
				{
					Make(item2.Key, value, value2, time);
					num++;
				}
				if (!((Object)(object)value2.Mesh == (Object)null))
				{
					val = value2.Centre - position;
					if (!(((Vector3)(ref val)).sqrMagnitude > 4096f))
					{
						Graphics.DrawMesh(value2.Mesh, Matrix4x4.identity, material, layer, (Camera)null, 0, (MaterialPropertyBlock)null, false, true);
					}
				}
			}
		}

		private static void Look()
		{
			looked = true;
			layer = LayerMask.NameToLayer("terrain");
			if (layer < 0)
			{
				layer = 0;
			}
			GameObject val = (((Object)(object)ObjectDB.instance != (Object)null) ? ObjectDB.instance.GetItemPrefab("Stone") : null);
			MeshRenderer val2 = (((Object)(object)val != (Object)null) ? val.GetComponentInChildren<MeshRenderer>(true) : null);
			if ((Object)(object)val2 != (Object)null && (Object)(object)((Renderer)val2).sharedMaterial != (Object)null)
			{
				material = ((Renderer)val2).sharedMaterial;
			}
			if ((Object)(object)material == (Object)null)
			{
				looked = false;
				Plugin.Debug("gravel: no stone material yet");
			}
		}

		private static void Make(long key, TrailRegion region, Patch patch, float now)
		{
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0300: Unknown result type (might be due to invalid IL or missing references)
			//IL_0305: Unknown result type (might be due to invalid IL or missing references)
			//IL_0315: Expected O, but got Unknown
			//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ec: 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_00f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_0118: Unknown result type (might be due to invalid IL or missing references)
			//IL_0111: Unknown result type (might be due to invalid IL or missing references)
			//IL_038e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0391: Unknown result type (might be due to invalid IL or missing references)
			//IL_0396: 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_0126: Unknown result type (might be due to invalid IL or missing references)
			//IL_012b: Unknown result type (might be due to invalid IL or missing references)
			//IL_012d: 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_0131: Unknown result type (might be due to invalid IL or missing references)
			//IL_0136: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b9: 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_01d3: 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_01da: Unknown result type (might be due to invalid IL or missing references)
			//IL_01df: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_0203: 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_0214: Unknown result type (might be due to invalid IL or missing references)
			//IL_0219: Unknown result type (might be due to invalid IL or missing references)
			//IL_021e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0220: Unknown result type (might be due to invalid IL or missing references)
			//IL_0222: Unknown result type (might be due to invalid IL or missing references)
			//IL_0229: Unknown result type (might be due to invalid IL or missing references)
			//IL_0244: Unknown result type (might be due to invalid IL or missing references)
			//IL_0249: Unknown result type (might be due to invalid IL or missing references)
			//IL_024b: 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)
			//IL_0254: Unknown result type (might be due to invalid IL or missing references)
			//IL_025b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0260: Unknown result type (might be due to invalid IL or missing references)
			//IL_0262: Unknown result type (might be due to invalid IL or missing references)
			//IL_0264: Unknown result type (might be due to invalid IL or missing references)
			//IL_026b: Unknown result type (might be due to invalid IL or missing references)
			//IL_028b: Unknown result type (might be due to invalid IL or missing references)
			//IL_028c: Unknown result type (might be due to invalid IL or missing references)
			//IL_028e: 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)
			patch.Version = region.Version;
			patch.RemakeAt = now + 30f;
			vertices.Clear();
			normals.Clear();
			uvs.Clear();
			triangles.Clear();
			bool sphere = Surface.Sphere;
			bool flag = false;
			Vector3 val = Vector3.zero;
			int num = 0;
			RaycastHit val3 = default(RaycastHit);
			foreach (KeyValuePair<ushort, RegionData.Cell> cell in region.Data.Cells)
			{
				if (cell.Value.Road != 1)
				{
					continue;
				}
				long num2 = Grid.FromLocal(key, cell.Key);
				if (!Surface.Ground(num2, out var point))
				{
					flag = true;
					continue;
				}
				Vector3 val2 = (sphere ? ((Vector3)(ref point)).normalized : Vector3.up);
				if (Physics.Raycast(point + val2 * 1.5f, -val2, ref val3, 3f, 1 << layer, (QueryTriggerInteraction)1))
				{
					point = ((RaycastHit)(ref val3)).point;
					val2 = ((RaycastHit)(ref val3)).normal;
				}
				Vector3 val4 = Vector3.Cross(val2, (Mathf.Abs(val2.z) < 0.9f) ? Vector3.forward : Vector3.right);
				Vector3 normalized = ((Vector3)(ref val4)).normalized;
				Vector3 val5 = Vector3.Cross(normalized, val2);
				uint seed = (uint)(num2.GetHashCode() * -1640531535 + (int)(num2 >> 32));
				for (int i = 0; i < 10; i++)
				{
					float num3 = Next(ref seed) - 0.5f;
					float num4 = Next(ref seed) - 0.5f;
					float num5 = Mathf.Lerp(0.035f, 0.085f, Next(ref seed) * Next(ref seed));
					float num6 = Mathf.Lerp(0.45f, 0.75f, Next(ref seed));
					Quaternion val6 = Quaternion.AngleAxis(Next(ref seed) * 360f, val2) * Quaternion.AngleAxis((Next(ref seed) - 0.5f) * 30f, normalized);
					Pebble(point + normalized * num3 + val5 * num4 - val2 * num5 * num6 * 0.35f, val6 * normalized * num5 * Mathf.Lerp(0.8f, 1.3f, Next(ref seed)), val6 * val2 * num5 * num6, val6 * val5 * num5, Next(ref seed));
				}
				val += point;
				num++;
			}
			if (flag)
			{
				patch.RemakeAt = now + 3f;
			}
			if (vertices.Count == 0)
			{
				if ((Object)(object)patch.Mesh != (Object)null)
				{
					Object.Destroy((Object)(object)patch.Mesh);
					patch.Mesh = null;
				}
				return;
			}
			if ((Object)(object)patch.Mesh == (Object)null)
			{
				patch.Mesh = new Mesh
				{
					name = "Trails gravel"
				};
			}
			patch.Mesh.Clear();
			patch.Mesh.indexFormat = (IndexFormat)(vertices.Count > 65000);
			patch.Mesh.SetVertices(vertices);
			patch.Mesh.SetNormals(normals);
			patch.Mesh.SetUVs(0, uvs);
			patch.Mesh.SetTriangles(triangles, 0);
			patch.Mesh.RecalculateBounds();
			patch.Centre = val / (float)num;
		}

		private static void Pebble(Vector3 at, Vector3 x, Vector3 y, Vector3 z, float u)
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			int count = vertices.Count;
			Vector3[] array = (Vector3[])(object)new Vector3[6]
			{
				x,
				-x,
				y,
				-y,
				z,
				-z
			};
			for (int i = 0; i < 6; i++)
			{
				vertices.Add(at + array[i]);
				normals.Add(((Vector3)(ref array[i])).normalized);
				uvs.Add(new Vector2(u * 0.8f + (float)(i & 1) * 0.15f, 0.2f + (float)(i >> 1) * 0.25f));
			}
			int[] array2 = new int[24]
			{
				0, 2, 4, 4, 2, 1, 1, 2, 5, 5,
				2, 0, 4, 3, 0, 1, 3, 4, 5, 3,
				1, 0, 3, 5
			};
			foreach (int num in array2)
			{
				triangles.Add(count + num);
			}
		}

		private static float Next(ref uint seed)
		{
			seed ^= seed << 13;
			seed ^= seed >> 17;
			seed ^= seed << 5;
			return (float)(seed & 0xFFFFFF) / 16777216f;
		}
	}
	internal static class Host
	{
		private sealed class Region
		{
			internal ZDO Zdo;

			internal RegionData Data;

			internal bool Dirty;

			internal bool Unpainted = true;

			internal bool SoilKnown;

			internal Soil Soil;

			internal Biome Biome;

			internal float JobUntil;
		}

		internal const string StepsRpc = "chistix_trails_steps";

		internal const string PaintedRpc = "chistix_trails_painted";

		internal const string RoadRpc = "chistix_trails_road";

		private const float FlushEvery = 5f;

		private const float DecayEvery = 60f;

		private const float JobTimeout = 20f;

		private const float PainterReach = 80f;

		private const int JobCells = 96;

		private const int GraceDays = 1;

		private const int RoadsideAge = 3;

		private const int RoadsideMax = 4;

		private const float RoadsideChance = 1f / 30f;

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

		private static readonly Random random = new Random();

		private static bool indexed;

		private static float nextFlush;

		private static float nextDecay;

		private static float nextJobs;

		private static double lastDecayTime = -1.0;

		private static readonly Dictionary<Biome, string[]> roadside = new Dictionary<Biome, string[]>
		{
			{
				(Biome)1,
				new string[3] { "Pickable_Dandelion", "Pickable_Dandelion", "Bush01" }
			},
			{
				(Biome)8,
				new string[3] { "Pickable_Thistle", "Bush02_en", "Bush01" }
			},
			{
				(Biome)16,
				new string[1] { "Bush01_heath" }
			}
		};

		private static readonly List<long> empty = new List<long>();

		private static readonly List<KeyValuePair<ushort, byte>> job = new List<KeyValuePair<ushort, byte>>();

		internal static int RegionCount
		{
			get
			{
				Index();
				return regions.Count;
			}
		}

		private static double DayLength => ((Object)(object)EnvMan.instance != (Object)null && EnvMan.instance.m_dayLengthSec > 0) ? EnvMan.instance.m_dayLengthSec : 1800;

		private static double Now
		{
			get
			{
				if (!((Object)(object)ZNet.instance != (Object)null))
				{
					return 0.0;
				}
				return ZNet.instance.GetTimeSeconds();
			}
		}

		private static ushort Today => (ushort)(long)(Now / DayLength);

		internal static void Register(ZRoutedRpc rpc)
		{
			rpc.Register<ZPackage>("chistix_trails_steps", (Action<long, ZPackage>)delegate(long sender, ZPackage pkg)
			{
				if (Rules.Server)
				{
					Steps(pkg);
				}
			});
			rpc.Register<ZPackage>("chistix_trails_painted", (Action<long, ZPackage>)delegate(long sender, ZPackage pkg)
			{
				if (Rules.Server)
				{
					Painted(pkg);
				}
			});
			rpc.Register<ZPackage>("chistix_trails_road", (Action<long, ZPackage>)delegate(long sender, ZPackage pkg)
			{
				if (Rules.Server)
				{
					Road(pkg);
				}
			});
		}

		internal static void Clear()
		{
			regions.Clear();
			indexed = false;
			lastDecayTime = -1.0;
		}

		private static void Index()
		{
			if (indexed || ZDOMan.instance == null)
			{
				return;
			}
			indexed = true;
			int num = 0;
			foreach (ZDO value in ZDOMan.instance.m_objectsByID.Values)
			{
				if (value.GetPrefab() == Prefabs.RegionHash)
				{
					long key = value.GetLong(Prefabs.KeyHash, 0L);
					if (regions.ContainsKey(key))
					{
						num++;
						continue;
					}
					regions[key] = new Region
					{
						Zdo = value,
						Data = RegionData.Read(value.GetByteArray(Prefabs.CellsHash, (byte[])null))
					};
				}
			}
			Plugin.Log($"trails: {regions.Count} regions in the world" + ((num > 0) ? $", {num} duplicates ignored" : ""));
		}

		private static Region Get(long key, Vector3 near, bool create)
		{
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			Index();
			if (regions.TryGetValue(key, out var value) && value.Zdo != null && value.Zdo.IsValid())
			{
				return value;
			}
			if (!create)
			{
				return null;
			}
			ZDO val = ZDOMan.instance.CreateNewZDO(Surface.RegionPoint(key, near), Prefabs.RegionHash);
			val.Persistent = true;
			val.SetType((ObjectType)0);
			val.SetPrefab(Prefabs.RegionHash);
			val.Set(Prefabs.KeyHash, key);
			val.Set(Prefabs.BornHash, (int)Today, false);
			value = new Region
			{
				Zdo = val,
				Data = new RegionData()
			};
			regions[key] = value;
			return value;
		}

		private static void Know(Region r)
		{
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: 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)
			if (!r.SoilKnown && r.Zdo != null)
			{
				r.SoilKnown = true;
				r.Biome = Surface.Biome(r.Zdo.GetPosition());
				r.Soil = Surface.SoilOf(r.Biome);
			}
		}

		private static void Steps(ZPackage pkg)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				Vector3 anchor = pkg.ReadVector3();
				int num = pkg.ReadInt();
				List<KeyValuePair<long, float>> list = new List<KeyValuePair<long, float>>(Mathf.Min(num, 4096));
				for (int i = 0; i < num; i++)
				{
					list.Add(new KeyValuePair<long, float>(pkg.ReadLong(), pkg.ReadSingle()));
				}
				Add(anchor, list);
			}
			catch (Exception ex)
			{
				Plugin.Warn("steps: " + ex.Message);
			}
		}

		internal static void Add(Vector3 anchor, IEnumerable<KeyValuePair<long, float>> cells)
		{
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			if (!Rules.Enabled || ZDOMan.instance == null)
			{
				return;
			}
			float num = Plugin.WearScales[Mathf.Clamp((int)Plugin.WearSpeed.Value, 0, Plugin.WearScales.Length - 1)];
			ushort today = Today;
			Region region = null;
			long num2 = long.MinValue;
			foreach (KeyValuePair<long, float> cell in cells)
			{
				if (cell.Value > 0f && !(cell.Value > 20f))
				{
					long num3 = Grid.RegionOf(cell.Key);
					if (num3 != num2)
					{
						num2 = num3;
						region = Get(num3, anchor, create: true);
					}
					region.Data.Add(Grid.Local(cell.Key), cell.Value * num, today);
					region.Dirty = (region.Unpainted = true);
				}
			}
		}

		private static void Road(ZPackage pkg)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				SetRoad(pkg.ReadVector3(), pkg.ReadSingle(), pkg.ReadByte());
			}
			catch (Exception ex)
			{
				Plugin.Warn("road: " + ex.Message);
			}
		}

		internal static int SetRoad(Vector3 world, float radius, byte level)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			if (ZDOMan.instance == null || !Surface.ToGrid(world, out var x, out var y, out var z))
			{
				return 0;
			}
			List<KeyValuePair<long, float>> list = new List<KeyValuePair<long, float>>();
			Grid.Brush(x, y, z, Mathf.Max(0f, radius - 0.9f), Surface.Flat, list);
			int num = 0;
			Region region = null;
			long num2 = long.MinValue;
			foreach (KeyValuePair<long, float> item in list)
			{
				long num3 = Grid.RegionOf(item.Key);
				if (num3 != num2)
				{
					num2 = num3;
					region = Get(num3, world, level != 0);
				}
				if (region != null)
				{
					ushort num4 = Grid.Local(item.Key);
					region.Data.Cells.TryGetValue(num4, out var value);
					byte b = (byte)((level != 0) ? ((byte)Mathf.Max((int)value.Road, (int)level)) : 0);
					if (b != value.Road || !region.Data.Cells.ContainsKey(num4))
					{
						region.Data.SetRoad(num4, b);
						region.Dirty = true;
						num++;
					}
				}
			}
			return num;
		}

		internal static int Stamp(Vector3 world, float radius, float amount)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
			if (!Surface.ToGrid(world, out var x, out var y, out var z))
			{
				return 0;
			}
			List<KeyValuePair<long, float>> list = new List<KeyValuePair<long, float>>();
			Grid.Brush(x, y, z, radius, Surface.Flat, list);
			List<KeyValuePair<long, float>> list2 = new List<KeyValuePair<long, float>>();
			foreach (KeyValuePair<long, float> item in list)
			{
				list2.Add(new KeyValuePair<long, float>(item.Key, Mathf.Min(20f, amount * Mathf.Clamp01(item.Value * 1.5f))));
			}
			for (float num = amount; num > 0f; num -= 20f)
			{
				float num2 = Mathf.Min(20f, num) / Mathf.Min(20f, amount);
				List<KeyValuePair<long, float>> list3 = new List<KeyValuePair<long, float>>();
				foreach (KeyValuePair<long, float> item2 in list2)
				{
					list3.Add(new KeyValuePair<long, float>(item2.Key, item2.Value * num2));
				}
				Add(world, list3);
			}
			return list2.Count;
		}

		internal static int Scale(Vector3 world, float radius, float factor, bool roads = false)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			if (!Surface.ToGrid(world, out var x, out var y, out var z))
			{
				return 0;
			}
			Index();
			int num = 0;
			foreach (KeyValuePair<long, Region> region in regions)
			{
				foreach (ushort item in new List<ushort>(region.Value.Data.Cells.Keys))
				{
					if (!(Grid.Distance(Grid.FromLocal(region.Key, item), x, Surface.Flat ? 0.0 : y, z) > (double)radius))
					{
						RegionData.Cell value = region.Value.Data.Cells[item];
						value.Wear *= factor;
						if (roads)
						{
							value.Road = 0;
						}
						region.Value.Data.Cells[item] = value;
						region.Value.Dirty = (region.Value.Unpainted = true);
						num++;
					}
				}
			}
			return num;
		}

		private static void Painted(ZPackage pkg)
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				long key = pkg.ReadLong();
				int num = pkg.ReadInt();
				Region region = Get(key, Vector3.zero, create: false);
				for (int i = 0; i < num; i++)
				{
					ushort key2 = pkg.ReadUShort();
					byte painted = pkg.ReadByte();
					if (region != null && region.Data.Cells.TryGetValue(key2, out var value))
					{
						value.Painted = painted;
						region.Data.Cells[key2] = value;
						region.Dirty = true;
					}
				}
				if (region != null)
				{
					region.JobUntil = 0f;
					region.Unpainted = true;
				}
			}
			catch (Exception ex)
			{
				Plugin.Warn("painted: " + ex.Message);
			}
		}

		internal static void PaintedHere(long key, List<KeyValuePair<ushort, byte>> done)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Expected O, but got Unknown
			ZPackage val = new ZPackage();
			val.Write(key);
			val.Write(done.Count);
			foreach (KeyValuePair<ushort, byte> item in done)
			{
				val.Write(item.Key);
				val.Write(item.Value);
			}
			val.SetPos(0);
			Painted(val);
		}

		internal static void Update()
		{
			if (Rules.Server && ZDOMan.instance != null && !((Object)(object)ZNet.instance == (Object)null))
			{
				float time = Time.time;
				if (time >= nextDecay)
				{
					nextDecay = time + 60f;
					Decay();
					Roadside();
				}
				if (time >= nextFlush)
				{
					nextFlush = time + 5f;
					Flush();
				}
				if (time >= nextJobs)
				{
					nextJobs = time + 1f;
					Jobs();
				}
			}
		}

		private static bool Snowing(Vector3 point)
		{
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			Player localPlayer = Player.m_localPlayer;
			if (!((Object)(object)localPlayer == (Object)null) && !((Object)(object)EnvMan.instance == (Object)null))
			{
				Vector3 val = ((Component)localPlayer).transform.position - point;
				if (!(((Vector3)(ref val)).sqrMagnitude > 90000f))
				{
					EnvSetup currentEnvironment = EnvMan.instance.GetCurrentEnvironment();
					if (currentEnvironment != null && currentEnvironment.m_name != null)
					{
						return currentEnvironment.m_name.IndexOf("snow", StringComparison.OrdinalIgnoreCase) >= 0;
					}
					return false;
				}
			}
			return false;
		}

		private static void Decay()
		{
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			double now = Now;
			if (lastDecayTime < 0.0)
			{
				lastDecayTime = now;
				return;
			}
			double num = (now - lastDecayTime) / DayLength;
			lastDecayTime = now;
			double num2 = Plugin.HalfLives[Mathf.Clamp((int)Plugin.Regrowth.Value, 0, Plugin.HalfLives.Length - 1)];
			if (num2 <= 0.0 || num <= 0.0)
			{
				return;
			}
			Index();
			ushort today = Today;
			foreach (Region value in regions.Values)
			{
				Know(value);
				double num3 = Soils.HalfLifeFactor(value.Soil, value.Soil == Soil.Snow && value.Zdo != null && Snowing(value.Zdo.GetPosition()));
				if (value.Data.Decay(today, num, num2 * num3, 1))
				{
					value.Dirty = (value.Unpainted = true);
				}
			}
		}

		private static void Roadside()
		{
			//IL_0080: 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_0185: Unknown result type (might be due to invalid IL or missing references)
			//IL_018c: Expected O, but got Unknown
			if (!Plugin.Roadside.Value || !Rules.Enabled)
			{
				return;
			}
			ushort today = Today;
			int num = 0;
			foreach (KeyValuePair<long, Region> region in regions)
			{
				Region value = region.Value;
				if (value.Zdo == null || !value.Zdo.IsValid() || random.NextDouble() > 0.03333333507180214)
				{
					continue;
				}
				Know(value);
				if (!roadside.TryGetValue(value.Biome, out var value2) || value.Zdo.GetInt(Prefabs.FlowersHash, 0) >= 4)
				{
					continue;
				}
				if (value.Zdo.GetOwner() != ZDOMan.GetSessionID())
				{
					value.Zdo.SetOwner(ZDOMan.GetSessionID());
				}
				if (value.Zdo.GetInt(Prefabs.BornHash, -1) < 0)
				{
					value.Zdo.Set(Prefabs.BornHash, (int)today, false);
				}
				if ((ushort)(today - (ushort)value.Zdo.GetInt(Prefabs.BornHash, (int)today)) < 3)
				{
					continue;
				}
				long num2 = Edge(region.Key, value.Data);
				if (num2 == 0L)
				{
					continue;
				}
				long num3 = Painter(value.Zdo.GetPosition());
				if (num3 != 0L)
				{
					int stableHashCode = StringExtensionMethods.GetStableHashCode(value2[random.Next(value2.Length)]);
					value.Zdo.Set(Prefabs.FlowersHash, value.Zdo.GetInt(Prefabs.FlowersHash, 0) + 1, false);
					ZPackage val = new ZPackage();
					val.Write(num2);
					val.Write(stableHashCode);
					if (num3 == ZDOMan.GetSessionID())
					{
						val.SetPos(0);
						Trails.Painter.Grow(num3, val);
					}
					else
					{
						ZRoutedRpc.instance.InvokeRoutedRPC(num3, "chistix_trails_grow", new object[1] { val });
					}
					if (++num >= 4)
					{
						break;
					}
				}
			}
		}

		private static long Edge(long region, RegionData data)
		{
			List<ushort> list = new List<ushort>();
			foreach (KeyValuePair<ushort, RegionData.Cell> cell in data.Cells)
			{
				if (cell.Value.Wear >= Stages.PaintSteps[3] && cell.Value.Road == 0)
				{
					list.Add(cell.Key);
				}
			}
			if (list.Count == 0)
			{
				return 0L;
			}
			for (int i = 0; i < 12; i++)
			{
				Grid.Unpack(Grid.FromLocal(region, list[random.Next(list.Count)]), out var x, out var y, out var z);
				int num = random.Next(-3, 4);
				int num2 = random.Next(-3, 4);
				if (Math.Abs(num) >= 2 || Math.Abs(num2) >= 2)
				{
					long num3 = Grid.Key(x + num, y, z + num2);
					long num4 = Grid.RegionOf(num3);
					Region value;
					RegionData regionData = ((num4 == region) ? data : (regions.TryGetValue(num4, out value) ? value.Data : null));
					if (regionData == null || !regionData.Cells.TryGetValue(Grid.Local(num3), out var value2) || (!(value2.Wear >= 4f) && value2.Road == 0))
					{
						return num3;
					}
				}
			}
			return 0L;
		}

		private static void Flush()
		{
			empty.Clear();
			long sessionID = ZDOMan.GetSessionID();
			foreach (KeyValuePair<long, Region> region in regions)
			{
				Region value = region.Value;
				if (value.Dirty && value.Zdo != null && value.Zdo.IsValid())
				{
					value.Dirty = false;
					if (value.Zdo.GetOwner() != sessionID)
					{
						value.Zdo.SetOwner(sessionID);
					}
					value.Data.Prune();
					if (value.Data.Cells.Count == 0)
					{
						empty.Add(region.Key);
					}
					else
					{
						value.Zdo.Set(Prefabs.CellsHash, value.Data.Write());
					}
				}
			}
			foreach (long item in empty)
			{
				ZDOMan.instance.DestroyZDO(regions[item].Zdo);
				regions.Remove(item);
			}
		}

		private static void Jobs()
		{
			//IL_013e: Unknown result type (might be due to invalid IL or missing references)
			//IL_015f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0166: Expected O, but got Unknown
			float time = Time.time;
			int num = 0;
			foreach (KeyValuePair<long, Region> region in regions)
			{
				Region value = region.Value;
				if (!value.Unpainted || time < value.JobUntil || value.Zdo == null || !value.Zdo.IsValid())
				{
					continue;
				}
				Know(value);
				int max = Soils.MaxPaint(value.Soil);
				job.Clear();
				foreach (KeyValuePair<ushort, RegionData.Cell> cell in value.Data.Cells)
				{
					int num2 = Stages.Paint(cell.Value.Wear, cell.Value.Painted, max, cell.Value.Road != 0);
					if (num2 != cell.Value.Painted)
					{
						job.Add(new KeyValuePair<ushort, byte>(cell.Key, (byte)num2));
					}
					if (job.Count >= 96)
					{
						break;
					}
				}
				if (job.Count == 0)
				{
					value.Unpainted = false;
					continue;
				}
				long num3 = Painter(value.Zdo.GetPosition());
				if (num3 == 0L)
				{
					continue;
				}
				value.JobUntil = time + 20f;
				ZPackage val = new ZPackage();
				val.Write(region.Key);
				val.Write(job.Count);
				foreach (KeyValuePair<ushort, byte> item in job)
				{
					val.Write(item.Key);
					val.Write(value.Data.Cells[item.Key].Painted);
					val.Write(item.Value);
				}
				if (num3 == ZDOMan.GetSessionID())
				{
					val.SetPos(0);
					Trails.Painter.Accept(num3, val);
				}
				else
				{
					ZRoutedRpc.instance.InvokeRoutedRPC(num3, "chistix_trails_paint", new object[1] { val });
				}
				if (++num < 8)
				{
					continue;
				}
				break;
			}
		}

		private static long Painter(Vector3 point)
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			float num = 6400f;
			long result = 0L;
			Player localPlayer = Player.m_localPlayer;
			Vector3 val;
			if ((Object)(object)localPlayer != (Object)null)
			{
				val = ((Component)localPlayer).transform.position - point;
				if (((Vector3)(ref val)).sqrMagnitude < num)
				{
					return ZDOMan.GetSessionID();
				}
			}
			foreach (ZNetPeer peer in ZNet.instance.GetPeers())
			{
				if (peer != null && peer.IsReady())
				{
					val = peer.m_refPos - point;
					float sqrMagnitude = ((Vector3)(ref val)).sqrMagnitude;
					if (sqrMagnitude < num)
					{
						num = sqrMagnitude;
						result = peer.m_uid;
					}
				}
			}
			return result;
		}

		internal static string Report(Vector3 world)
		{
			Index();
			int num = 0;
			int num2 = 0;
			int num3 = 0;
			foreach (Region value in regions.Values)
			{
				foreach (RegionData.Cell value2 in value.Data.Cells.Values)
				{
					num++;
					if (value2.Painted > 0)
					{
						num2++;
					}
					if (value2.Road > 0)
					{
						num3++;
					}
				}
			}
			return $"regions {regions.Count}, cells {num}, painted {num2}, road cells {num3}";
		}
	}
	internal static class Painter
	{
		private struct Job
		{
			internal long Host;

			internal long Region;

			internal ushort Local;

			internal byte From;

			internal byte To;
		}

		internal const string PaintRpc = "chistix_trails_paint";

		private const int PerFrame = 6;

		private static readonly Queue<Job> queue = new Queue<Job>();

		private static readonly Dictionary<long, List<KeyValuePair<ushort, byte>>> done = new Dictionary<long, List<KeyValuePair<ushort, byte>>>();

		private static float nextReport;

		internal static int Painted;

		internal const string GrowRpc = "chistix_trails_grow";

		private static readonly Queue<KeyValuePair<long, int>> sowing = new Queue<KeyValuePair<long, int>>();

		private static int blockMask;

		internal static int Sown;

		internal static void Register(ZRoutedRpc rpc)
		{
			rpc.Register<ZPackage>("chistix_trails_paint", (Action<long, ZPackage>)Accept);
			rpc.Register<ZPackage>("chistix_trails_grow", (Action<long, ZPackage>)Grow);
		}

		internal static void Clear()
		{
			queue.Clear();
			done.Clear();
			sowing.Clear();
		}

		internal static void Grow(long sender, ZPackage pkg)
		{
			try
			{
				sowing.Enqueue(new KeyValuePair<long, int>(pkg.ReadLong(), pkg.ReadInt()));
			}
			catch (Exception ex)
			{
				Plugin.Warn("grow job: " + ex.Message);
			}
		}

		private static void Sow(long cell, int hash)
		{
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a1: 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_00ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00db: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = (((Object)(object)ZNetScene.instance != (Object)null) ? ZNetScene.instance.GetPrefab(hash) : null);
			if (!((Object)(object)val == (Object)null) && Surface.Ground(cell, out var point))
			{
				Vector3 val2 = (Surface.Sphere ? ((Vector3)(ref point)).normalized : Vector3.up);
				if (blockMask == 0)
				{
					blockMask = LayerMask.GetMask(new string[7] { "piece", "piece_nonsolid", "static_solid", "Default", "Default_small", "item", "vehicle" });
				}
				if (!Physics.CheckSphere(point + val2 * 0.6f, 0.7f, blockMask, (QueryTriggerInteraction)1))
				{
					Object.Instantiate<GameObject>(val, point, Quaternion.AngleAxis(Random.Range(0f, 360f), val2) * Quaternion.FromToRotation(Vector3.up, val2));
					Sown++;
				}
			}
		}

		internal static void Accept(long sender, ZPackage pkg)
		{
			try
			{
				long region = pkg.ReadLong();
				int num = pkg.ReadInt();
				for (int i = 0; i < num; i++)
				{
					queue.Enqueue(new Job
					{
						Host = sender,
						Region = region,
						Local = pkg.ReadUShort(),
						From = pkg.ReadByte(),
						To = pkg.ReadByte()
					});
				}
			}
			catch (Exception ex)
			{
				Plugin.Warn("paint job: " + ex.Message);
			}
		}

		internal static void Update()
		{
			if (sowing.Count > 0)
			{
				KeyValuePair<long, int> keyValuePair = sowing.Dequeue();
				try
				{
					Sow(keyValuePair.Key, keyValuePair.Value);
				}
				catch (Exception ex)
				{
					Plugin.Warn("sow: " + ex.Message);
				}
			}
			for (int i = 0; i < 6; i++)
			{
				if (queue.Count <= 0)
				{
					break;
				}
				Job job = queue.Dequeue();
				try
				{
					if (Paint(job, out var now))
					{
						Done(job.Region, job.Local, now);
					}
				}
				catch (Exception ex2)
				{
					Plugin.Warn("paint: " + ex2.Message);
				}
			}
			if (done.Count > 0 && (queue.Count == 0 || Time.time >= nextReport))
			{
				nextReport = Time.time + 2f;
				Report();
			}
		}

		private static void Done(long region, ushort local, byte stage)
		{
			if (!done.TryGetValue(region, out var value))
			{
				value = (done[region] = new List<KeyValuePair<ushort, byte>>());
			}
			value.Add(new KeyValuePair<ushort, byte>(local, stage));
		}

		private static void Report()
		{
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Expected O, but got Unknown
			foreach (KeyValuePair<long, List<KeyValuePair<ushort, byte>>> item in done)
			{
				if (Rules.Server)
				{
					Host.PaintedHere(item.Key, item.Value);
				}
				else
				{
					if (ZRoutedRpc.instance == null)
					{
						continue;
					}
					ZPackage val = new ZPackage();
					val.Write(item.Key);
					val.Write(item.Value.Count);
					foreach (KeyValuePair<ushort, byte> item2 in item.Value)
					{
						val.Write(item2.Key);
						val.Write(item2.Value);
					}
					ZRoutedRpc.instance.InvokeRoutedRPC("chistix_trails_painted", new object[1] { val });
				}
			}
			done.Clear();
		}

		private static bool Paint(Job job, out byte now)
		{
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: 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)
			now = job.From;
			if (!Surface.Ground(Grid.FromLocal(job.Region, job.Local), out var point))
			{
				return false;
			}
			if (job.To == job.From)
			{
				return true;
			}
			Heightmap val = Heightmap.FindHeightmap(point);
			if ((Object)(object)val != (Object)null)
			{
				Color paintMask = val.GetPaintMask(point);
				bool flag = job.To > job.From;
				if (flag && (paintMask.g > 0.3f || paintMask.b > 0.3f))
				{
					now = job.To;
					return true;
				}
				if (!flag && (paintMask.g > 0.15f || paintMask.b > 0.15f || paintMask.r > (float)(int)job.From / 4f + 0.3f))
				{
					now = 0;
					return true;
				}
			}
			TerrainOp val2 = Prefabs.Paint(job.From, job.To);
			if ((Object)(object)val2 == (Object)null)
			{
				return true;
			}
			if ((Object)(object)ObjectDB.instance != (Object)null)
			{
				Prefabs.RegisterObjects(ObjectDB.instance);
			}
			Object.Instantiate<GameObject>(((Component)val2).gameObject, point, Quaternion.identity);
			Painted++;
			now = job.To;
			return true;
		}
	}
	internal static class Pens
	{
		private static Func<Vector3, bool> inPen;

		private static bool looked;

		internal static bool Inside(Vector3 point)
		{
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			if (!looked)
			{
				looked = true;
				Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
				for (int i = 0; i < assemblies.Length; i++)
				{
					MethodInfo methodInfo = assemblies[i].GetType("Leash.Api", throwOnError: false)?.GetMethod("IsInPen", BindingFlags.Static | BindingFlags.Public, null, new Type[1] { typeof(Vector3) }, null);
					if (!(methodInfo == null) && !(methodInfo.ReturnType != typeof(bool)))
					{
						inPen = (Func<Vector3, bool>)Delegate.CreateDelegate(typeof(Func<Vector3, bool>), methodInfo);
						Plugin.Log("Leash found: no trails inside its pens");
						break;
					}
				}
			}
			if (inPen == null)
			{
				return false;
			}
			try
			{
				return inPen(point);
			}
			catch (Exception ex)
			{
				Plugin.Warn("Leash pens: " + ex.Message + ", pens are ignored from now on");
				inPen = null;
				return false;
			}
		}
	}
	internal enum WearRate
	{
		Slow,
		Normal,
		Fast,
		VeryFast
	}
	internal enum Growth
	{
		Off,
		Slow,
		Normal,
		Fast
	}
	[BepInPlugin("chistix.trails", "Trails", "0.2.9")]
	public sealed class Plugin : BaseUnityPlugin
	{
		public const string Guid = "chistix.trails";

		public const string Version = "0.2.9";

		private static ManualLogSource log;

		internal static readonly float[] WearScales = new float[4] { 0.5f, 1f, 2f, 4f };

		internal static readonly double[] HalfLives = new double[4] { 0.0, 20.0, 8.0, 3.0 };

		internal static readonly int[] RunBonuses = new int[6] { 0, 5, 10, 15, 20, 25 };

		internal static readonly int[] StaminaSavings = new int[6] { 0, 10, 15, 20, 30, 40 };

		internal static readonly int[] RoadBonuses = new int[4] { 0, 5, 10, 15 };

		internal static ConfigEntry<bool> Enabled;

		internal static ConfigEntry<bool> Tamed;

		internal static ConfigEntry<bool> Wild;

		internal static ConfigEntry<bool> Carts;

		internal static ConfigEntry<bool> CropTrampling;

		internal static ConfigEntry<bool> Mud;

		internal static ConfigEntry<bool> Roadside;

		internal static ConfigEntry<bool> Thinning;

		internal static ConfigEntry<bool> GravelPebbles;

		internal static ConfigEntry<bool> DebugLog;

		internal static ConfigEntry<WearRate> WearSpeed;

		internal static ConfigEntry<Growth> Regrowth;

		internal static ConfigEntry<int> RunBonus;

		internal static ConfigEntry<int> StaminaSaving;

		internal static ConfigEntry<int> RoadBonus;

		internal static bool Russian
		{
			get
			{
				if (Localization.instance != null)
				{
					return Localization.instance.GetSelectedLanguage() == "Russian";
				}
				return false;
			}
		}

		private void Awake()
		{
			//IL_0314: Unknown result type (might be due to invalid IL or missing references)
			log = ((BaseUnityPlugin)this).Logger;
			int num = 100;
			Enabled = Bind("General", "Enabled", value: true, "Worn trails on.", "Протоптанные тропы включены.", num--);
			WearSpeed = Bind("General", "WearSpeed", WearRate.Normal, "How fast a trail wears in: Normal - the grass goes after some 16 crossings, the trail is finished after 45; Slow half as fast, Fast twice, VeryFast four times.", "Как быстро вытаптывается тропа: Normal - трава пропадает примерно после 16 проходов, тропа готова после 45; Slow вдвое медленнее, Fast вдвое быстрее, VeryFast вчетверо.", num--);
			Regrowth = Bind("General", "Regrowth", Growth.Normal, "How fast an unwalked trail grows back (it keeps for a game day first): its wear halves every 20 (Slow), 8 (Normal) or 3 (Fast) game days; Off - never.", "Как быстро зарастает нехоженая тропа (первые игровые сутки не меняется): износ вдвое за 20 (Slow), 8 (Normal) или 3 (Fast) игровых дня; Off - никогда.", num--);
			RunBonus = Bind("General", "RunBonus", 10, "% faster running and jogging on a finished trail (less on one wearing in).", "На сколько процентов быстрее бег и обычный ход по готовой тропе (по недотоптанной - меньше).", num--, (AcceptableValueBase)(object)new AcceptableValueList<int>(RunBonuses));
			StaminaSaving = Bind("General", "StaminaSaving", 15, "% less stamina for running on a finished trail.", "На сколько процентов меньше выносливости уходит на бег по готовой тропе.", num--, (AcceptableValueBase)(object)new AcceptableValueList<int>(StaminaSavings));
			RoadBonus = Bind("General", "RoadBonus", 5, "Roads: % faster than a trail per step (gravel one, stone two). On a road stamina also comes back faster (+25 % gravel, +50 % stone), carts are lighter, mounts faster; roads never grow back. Gravel: the hoe's \"Gravel road\" (1 stone); stone: the game's \"Paved road\" on top.", "Дороги: на сколько процентов быстрее тропы каждая ступень (гравий - одна, камень - две). На дороге ещё и выносливость восстанавливается быстрее (+25 % гравий, +50 % камень), телега легче, ездовой зверь быстрее; дорога не зарастает. Гравий - «Гравийная дорога» у мотыги (1 камень), камень - игровая «Мощёная дорога» поверх.", num--, (AcceptableValueBase)(object)new AcceptableValueList<int>(RoadBonuses));
			Carts = Bind("General", "Carts", value: true, "Cart wheels wear two ruts that grow into a wide road; a cart is lighter on ruts and roads.", "Колёса телег протаптывают две колеи, из которых выходит широкая дорога; на колее и дороге телега легче.", num--);
			Mud = Bind("General", "Mud", value: true, "Rain turns earth trails to mud: while the ground is wet they are slower (down to -10 %), not faster. Mountains, plains, ash and roads stay firm.", "Дождь размывает земляные тропы в грязь: пока сыро, по ним медленнее (до −10 %), а не быстрее. Горы, равнины, пепел и дороги не размокают.", num--);
			CropTrampling = Bind("General", "CropTrampling", value: true, "Crops get trampled: a step on a crop sets its growth back a tenth, the fifth step in a game day kills it. Sneaking does no harm.", "Грядки мнутся: шаг по посадке задерживает её рост на десятую часть, пятый шаг за игровые сутки губит её. Крадущимся шагом растения не мнутся.", num--);
			Roadside = Bind("General", "Roadside", value: true, "Flowers and bushes grow along old trails over time (meadows, Black Forest, plains).", "Вдоль старых троп со временем вырастают цветы и кусты (луга, Чёрный лес, равнины).", num--);
			Tamed = Bind("Creatures", "TamedWear", value: false, "Tamed animals wear trails too (big ones wider). Off: only players and carts do.", "Приручённые животные тоже протаптывают тропы (крупные - шире). Выключено: топчут только игроки и телеги.", num--);
			Wild = Bind("Creatures", "WildWear", value: false, "Wild animals wear trails too.", "Дикие животные тоже протаптывают тропы.", num--);
			GravelPebbles = Bind("Visual", "GravelPebbles", value: true, "Small pebbles drawn over gravel roads near you (one light mesh per 32 m area, no shadows). Yours only.", "Мелкие камешки на гравийных дорогах рядом с вами (один лёгкий меш на участок 32 м, без теней). Только у вас.", num--, null, local: true);
			Thinning = Bind("Visual", "GrassThinning", value: true, "The grass of a trail thins little by little before it goes (flat worlds). Yours only.", "Трава на тропе редеет понемногу, прежде чем пропасть (плоский мир). Только у вас.", num--, null, local: true);
			DebugLog = Bind("Advanced", "DebugLog", value: false, "Details in the log.", "Подробности в журнал.", num--, null, local: true);
			ConfigEntryBase[] array = (ConfigEntryBase[])(object)new ConfigEntryBase[11]
			{
				(ConfigEntryBase)Enabled,
				(ConfigEntryBase)WearSpeed,
				(ConfigEntryBase)Regrowth,
				(ConfigEntryBase)RunBonus,
				(ConfigEntryBase)StaminaSaving,
				(ConfigEntryBase)Tamed,
				(ConfigEntryBase)Wild,
				(ConfigEntryBase)Carts,
				(ConfigEntryBase)RoadBonus,
				(ConfigEntryBase)CropTrampling,
				(ConfigEntryBase)Mud
			};
			foreach (ConfigEntryBase entry in array)
			{
				((BaseUnityPlugin)this).Config.SettingChanged += delegate(object s, SettingChangedEventArgs e)
				{
					if (e.ChangedSetting == entry)
					{
						Rules.Changed();
					}
				};
			}
			Prefabs.Make();
			new Harmony("chistix.trails").PatchAll(typeof(Plugin).Assembly);
			Log("Trails 0.2.9");
		}

		private void Update()
		{
			try
			{
				Rules.Update();
				Walkers.Update();
				Painter.Update();
				Host.Update();
				Gravel.Update();
			}
			catch (Exception ex)
			{
				Warn("update: " + ex);
			}
		}

		private ConfigEntry<T> Bind<T>(string section, string key, T value, string en, string ru, int order, AcceptableValueBase values = null, bool local = false)
		{
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Expected O, but got Unknown
			return ((BaseUnityPlugin)this).Config.Bind<T>(section, key, value, new ConfigDescription(en + (local ? "" : " In multiplayer the host's value applies.") + " / " + ru + (local ? "" : " В мультиплеере действует значение хоста."), values, new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Order = order
				}
			}));
		}

		internal static string T(string ru, string en)
		{
			if (!Russian)
			{
				return en;
			}
			return ru;
		}

		internal static void Log(string text)
		{
			ManualLogSource obj = log;
			if (obj != null)
			{
				obj.LogInfo((object)text);
			}
		}

		internal static void Warn(string text)
		{
			ManualLogSource obj = log;
			if (obj != null)
			{
				obj.LogWarning((object)text);
			}
		}

		internal static void Debug(string text)
		{
			if (DebugLog != null && DebugLog.Value)
			{
				ManualLogSource obj = log;
				if (obj != null)
				{
					obj.LogInfo((object)text);
				}
			}
		}
	}
	[HarmonyPatch]
	internal static class Network
	{
		[Serializable]
		[CompilerGenerated]
		private sealed class <>c
		{
			public static readonly <>c <>9 = new <>c();

			public static ConsoleEvent <>9__2_0;

			internal void <Command>b__2_0(ConsoleEventArgs args)
			{
				//IL_0026: Unknown result type (might be due to invalid IL or missing references)
				//IL_002b: Unknown result type (might be due to invalid IL or missing references)
				//IL_0186: 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_0160: Unknown result type (might be due to invalid IL or missing references)
				Player localPlayer = Player.m_localPlayer;
				if ((Object)(object)localPlayer == (Object)null)
				{
					args.Context.AddString("trails: no player");
					return;
				}
				Vector3 position = ((Component)localPlayer).transform.position;
				if (args.Length >= 2 && (args[1] == "stamp" || args[1] == "clear"))
				{
					if (!Rules.Server || !args.Context.IsCheatsEnabled())
					{
						args.Context.AddString("trails: host with devcommands only");
						return;
					}
					float result;
					float result2;
					float radius = ((args.Length >= 4 && float.TryParse(args[3], NumberStyles.Float, CultureInfo.InvariantCulture, out result)) ? result : ((args[1] == "clear" && args.Length >= 3 && float.TryParse(args[2], NumberStyles.Float, CultureInfo.InvariantCulture, out result2)) ? result2 : 1.5f));
					if (args[1] == "clear")
					{
						args.Context.AddString($"trails: {Host.Scale(position, radius, 0f, roads: true)} cells cleared (roads too)");
						return;
					}
					float result3;
					float num = ((args.Length >= 3 && float.TryParse(args[2], NumberStyles.Float, CultureInfo.InvariantCulture, out result3)) ? result3 : 20f);
					args.Context.AddString($"trails: {Host.Stamp(position, radius, num)} cells +{num}");
				}
				else
				{
					args.Context.AddString(Status(position));
				}
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(ZNet), "Awake")]
		private static void Start()
		{
			Host.Clear();
			Walkers.Clear();
			Painter.Clear();
			Rules.Reset();
			Gravel.Clear();
			ZRoutedRpc instance = ZRoutedRpc.instance;
			if (instance != null)
			{
				Rules.Register(instance);
				Host.Register(instance);
				Painter.Register(instance);
			}
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(ZNet), "OnDestroy")]
		private static void Stop()
		{
			Host.Clear();
			Walkers.Clear();
			Painter.Clear();
			Rules.Reset();
			Gravel.Clear();
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(Terminal), "InitTerminal")]
		private static void Command()
		{
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Expected O, but got Unknown
			object obj = <>c.<>9__2_0;
			if (obj == null)
			{
				ConsoleEvent val = delegate(ConsoleEventArgs args)
				{
					//IL_0026: Unknown result type (might be due to invalid IL or missing references)
					//IL_002b: Unknown result type (might be due to invalid IL or missing references)
					//IL_0186: 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_0160: Unknown result type (might be due to invalid IL or missing references)
					Player localPlayer = Player.m_localPlayer;
					if ((Object)(object)localPlayer == (Object)null)
					{
						args.Context.AddString("trails: no player");
					}
					else
					{
						Vector3 position = ((Component)localPlayer).transform.position;
						if (args.Length >= 2 && (args[1] == "stamp" || args[1] == "clear"))
						{
							if (!Rules.Server || !args.Context.IsCheatsEnabled())
							{
								args.Context.AddString("trails: host with devcommands only");
							}
							else
							{
								float result;
								float result2;
								float radius = ((args.Length >= 4 && float.TryParse(args[3], NumberStyles.Float, CultureInfo.InvariantCulture, out result)) ? result : ((args[1] == "clear" && args.Length >= 3 && float.TryParse(args[2], NumberStyles.Float, CultureInfo.InvariantCulture, out result2)) ? result2 : 1.5f));
								if (args[1] == "clear")
								{
									args.Context.AddString($"trails: {Host.Scale(position, radius, 0f, roads: true)} cells cleared (roads too)");
								}
								else
								{
									float result3;
									float num = ((args.Length >= 3 && float.TryParse(args[2], NumberStyles.Float, CultureInfo.InvariantCulture, out result3)) ? result3 : 20f);
									args.Context.AddString($"trails: {Host.Stamp(position, radius, num)} cells +{num}");
								}
							}
						}
						else
						{
							args.Context.AddString(Status(position));
						}
					}
				};
				<>c.<>9__2_0 = val;
				obj = (object)val;
			}
			new ConsoleCommand("trails", "[stamp N [radius] | clear [radius]] — Trails: wear here, or (host, cheats) wear the ground", (ConsoleEvent)obj, false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
		}

		internal static string Status(Vector3 pos)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0158: Unknown result type (might be due to invalid IL or missing references)
			float num = TrailRegion.WearAt(pos);
			return "trails 0.2.9: " + (Rules.Enabled ? "on" : (Rules.Received ? "off" : "waiting for the host")) + ", " + (Surface.Sphere ? "planet" : "flat") + ", " + string.Format("wear here {0:0.0} (thin {1:0.00}, step {2}, run {3:0.00}), on trail {4:0.00}, road {5}, wet {6:0.00}{7}, speed x{8:0.00}, ", num, Stages.Thin(num), Stages.Paint(num, 0), Stages.Speed(num), Walkers.LocalTrail, Walkers.LocalRoad, Walkers.Wet, Walkers.LocalMuddy ? " (mud)" : "", Effects.Speed()) + $"trampled {Walkers.Trampled}, sown {Painter.Sown}, " + $"loaded regions {TrailRegion.Loaded.Count}, sent {Walkers.Sent}, painted {Painter.Painted}" + (Rules.Server ? (", host: " + Host.Report(pos)) : "");
		}
	}
	internal sealed class ConfigurationManagerAttributes
	{
		public int? Order;
	}
	internal static class Prefabs
	{
		internal const string RegionName = "chistix_trails_region";

		internal static readonly int RegionHash = StringExtensionMethods.GetStableHashCode("chistix_trails_region");

		internal static readonly int KeyHash = StringExtensionMethods.GetStableHashCode("chistix_trails_key");

		internal static readonly int CellsHash = StringExtensionMethods.GetStableHashCode("chistix_trails_cells");

		internal static readonly int BornHash = StringExtensionMethods.GetStableHashCode("chistix_trails_born");

		internal static readonly int FlowersHash = StringExtensionMethods.GetStableHashCode("chistix_trails_flowers");

		private static GameObject holder;

		private static GameObject region;

		private static readonly Dictionary<int, TerrainOp> paints = new Dictionary<int, TerrainOp>();

		internal static Transform Holder
		{
			get
			{
				Make();
				return holder.transform;
			}
		}

		internal static void Make()
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Expected O, but got Unknown
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Expected O, but got Unknown
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: 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_00ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0103: Unknown result type (might be due to invalid IL or missing references)
			//IL_010e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0120: Expected O, but got Unknown
			if ((Object)(object)holder != (Object)null)
			{
				return;
			}
			holder = new GameObject("Trails prefabs");
			holder.SetActive(false);
			Object.DontDestroyOnLoad((Object)(object)holder);
			region = new GameObject("chistix_trails_region");
			region.transform.SetParent(holder.transform, false);
			ZNetView obj = region.AddComponent<ZNetView>();
			obj.m_persistent = true;
			obj.m_type = (ObjectType)0;
			region.AddComponent<TrailRegion>();
			for (int i = 0; i <= 4; i++)
			{
				for (int j = 0; j <= 4; j++)
				{
					if (i != j)
					{
						GameObject val = new GameObject(PaintName(i, j));
						val.transform.SetParent(holder.transform, false);
						TerrainOp val2 = val.AddComponent<TerrainOp>();
						val2.m_settings = new Settings
						{
							m_level = false,
							m_raise = false,
							m_smooth = false,
							m_halfOffset = false,
							m_paintCleared = true,
							m_paintHeightCheck = false,
							m_paintType = (PaintType)((j <= i) ? 3 : 0),
							m_paintRadius = 1.5f,
							m_paintExp = 1f,
							m_paintStrength = Stages.PaintStrength(i, j)
						};
						paints[Key(i, j)] = val2;
					}
				}
			}
		}

		private static int Key(int from, int to)
		{
			return from * 16 + to;
		}

		private static string PaintName(int from, int to)
		{
			return $"chistix_trails_paint_{((to > from) ? 'u' : 'd')}{from}{to}";
		}

		internal static TerrainOp Paint(int from, int to)
		{
			if (!paints.TryGetValue(Key(from, to), out var value))
			{
				return null;
			}
			return value;
		}

		internal static void RegisterScene(ZNetScene scene)
		{
			if (!((Object)(object)scene == (Object)null) && !((Object)(object)region == (Object)null) && !scene.m_namedPrefabs.ContainsKey(RegionHash))
			{
				scene.m_namedPrefabs[RegionHash] = region;
				scene.m_prefabs.Add(region);
			}
		}

		internal static void RegisterObjects(ObjectDB db)
		{
			if ((Object)(object)db == (Object)null)
			{
				return;
			}
			foreach (TerrainOp value in paints.Values)
			{
				int stableHashCode = StringExtensionMethods.GetStableHashCode(((Object)((Component)value).gameObject).name);
				if (!db.m_terrainOpsByHash.ContainsKey(stableHashCode))
				{
					db.m_terrainOpsByHash[stableHashCode] = value;
				}
			}
		}
	}
	[HarmonyPatch(typeof(ZNetScene), "Awake")]
	internal static class RegisterRegionPrefab
	{
		private static void Postfix(ZNetScene __instance)
		{
			try
			{
				Prefabs.RegisterScene(__instance);
			}
			catch (Exception ex)
			{
				Plugin.Warn("region prefab: " + ex.Message);
			}
		}
	}
	[HarmonyPatch(typeof(ObjectDB), "Awake")]
	internal static class RegisterPaintOps
	{
		private static void Postfix(ObjectDB __instance)
		{
			try
			{
				Prefabs.RegisterObjects(__instance);
			}
			catch (Exception ex)
			{
				Plugin.Warn("paint ops: " + ex.Message);
			}
		}
	}
	internal sealed class TrailRegion : MonoBehaviour
	{
		internal static readonly Dictionary<long, TrailRegion> Loaded = new Dictionary<long, TrailRegion>();

		private ZNetView view;

		private long key;

		private bool registered;

		private uint revision = uint.MaxValue;

		internal RegionData Data = new RegionData();

		internal int Version;

		private float nextCheck;

		private float grassAt = -1f;

		private int thinning;

		private float lastGrass = -100f;

		private static readonly List<KeyValuePair<long, float>> brush = new List<KeyValuePair<long, float>>();

		private void Awake()
		{
			view = ((Component)this).GetComponent<ZNetView>();
			if (!((Object)(object)view == (Object)null) && view.IsValid())
			{
				key = view.GetZDO().GetLong(Prefabs.KeyHash, 0L);
				Loaded[key] = this;
				registered = true;
				Refresh();
			}
		}

		private void OnDestroy()
		{
			if (registered && Loaded.TryGetValue(key, out var value) && (Object)(object)value == (Object)(object)this)
			{
				Loaded.Remove(key);
			}
		}

		private void Update()
		{
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			float time = Time.time;
			if (grassAt >= 0f && time >= grassAt)
			{
				grassAt = -1f;
				if ((Object)(object)ClutterSystem.instance != (Object)null && Plugin.Thinning.Value)
				{
					ClutterSystem.instance.ResetGrass(((Component)this).transform.position, 24f);
				}
			}
			if (time < nextCheck)
			{
				return;
			}
			nextCheck = time + 1f;
			try
			{
				Refresh();
			}
			catch (Exception ex)
			{
				Plugin.Debug("region: " + ex.Message);
			}
		}

		private void Refresh()
		{
			ZDO val = (((Object)(object)view != (Object)null && view.IsValid()) ? view.GetZDO() : null);
			if (val == null || val.DataRevision == revision)
			{
				return;
			}
			revision = val.DataRevision;
			Data = RegionData.Read(val.GetByteArray(Prefabs.CellsHash, (byte[])null));
			Version++;
			int num = 0;
			foreach (KeyValuePair<ushort, RegionData.Cell> cell in Data.Cells)
			{
				num = num * 31 + cell.Key * 7 + (int)(Stages.Thin(cell.Value.Wear) * 8f);
			}
			if (num != thinning)
			{
				thinning = num;
				if (grassAt < 0f)
				{
					grassAt = Mathf.Max(Time.time, lastGrass + 10f);
				}
				lastGrass = grassAt;
			}
		}

		internal static float WearAt(Vector3 world)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			byte road;
			return Sample(world, out road);
		}

		internal static float Sample(Vector3 world, out byte road)
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			road = 0;
			if (Loaded.Count == 0 || !Surface.ToGrid(world, out var x, out var y, out var z))
			{
				return 0f;
			}
			bool flat = Surface.Flat;
			Grid.Brush(x, y, z, 0.3, flat, brush);
			float num = 0f;
			float num2 = 0f;
			long num3 = long.MinValue;
			TrailRegion value = null;
			foreach (KeyValuePair<long, float> item in brush)
			{
				long num4 = Grid.RegionOf(item.Key);
				if (num4 != num3)
				{
					num3 = num4;
					Loaded.TryGetValue(num4, out value);
				}
				num2 += item.Value;
				if ((Object)(object)value != (Object)null && value.Data.Cells.TryGetValue(Grid.Local(item.Key), out var value2))
				{
					num += value2.Wear * item.Value;
					if (item.Value > 0.5f && value2.Road > road)
					{
						road = value2.Road;
					}
				}
			}
			if (!(num2 > 0f))
			{
				return 0f;
			}
			return num / num2;
		}
	}
	[HarmonyPatch]
	internal static class RoadPieces
	{
		internal const string GravelName = "chistix_trails_gravel";

		private static GameObject gravel;

		private static readonly Action<Localization, string, string> AddWord = AccessTools.MethodDelegate<Action<Localization, string, string>>(AccessTools.Method(typeof(Localization), "AddWord", (Type[])null, (Type[])null), (object)null, true);

		[HarmonyPostfix]
		[HarmonyPatch(typeof(ObjectDB), "Awake")]
		private static void Items(ObjectDB __instance)
		{
			try
			{
				Make(__instance);
			}
			catch (Exception ex)
			{
				Plugin.Warn("gravel road: " + ex.Message);
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(ZNetScene), "Awake")]
		private static void Scene(ZNetScene __instance)
		{
			if (!((Object)(object)gravel == (Object)null) && !((Object)(object)__instance == (Object)null))
			{
				int stableHashCode = StringExtensionMethods.GetStableHashCode("chistix_trails_gravel");
				if (!__instance.m_namedPrefabs.ContainsKey(stableHashCode))
				{
					__instance.m_namedPrefabs[stableHashCode] = gravel;
					__instance.m_prefabs.Add(gravel);
				}
			}
		}

		private static void Make(ObjectDB db)
		{
			//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_010c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0119: 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_0133: Unknown result type (might be due to invalid IL or missing references)
			//IL_0140: Unknown result type (might be due to invalid IL or missing references)
			//IL_014d: Unknown result type (might be due to invalid IL or missing references)
			//IL_015a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0167: Unknown result type (might be due to invalid IL or missing references)
			//IL_0174: Unknown result type (might be due to invalid IL or missing references)
			//IL_0181: Unknown result type (might be due to invalid IL or missing references)
			//IL_0188: 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)
			//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_01a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bc: 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_01d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_0202: Expected O, but got Unknown
			//IL_022a: Unknown result type (might be due to invalid IL or missing references)
			//IL_022f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0236: Unknown result type (might be due to invalid IL or missing references)
			//IL_023d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0245: Expected O, but got Unknown
			GameObject itemPrefab = db.GetItemPrefab("Hoe");
			PieceTable val = ((itemPrefab != null) ? itemPrefab.GetComponent<ItemDrop>() : null)?.m_itemData?.m_shared?.m_buildPieces;
			if ((Object)(object)val == (Object)null)
			{
				return;
			}
			Words();
			if ((Object)(object)gravel == (Object)null)
			{
				GameObject val2 = val.m_pieces.Find(delegate(GameObject p)
				{
					//IL_0026: Unknown result type (might be due to invalid IL or missing references)
					//IL_002c: Invalid comparison between Unknown and I4
					if ((Object)(object)p != (Object)null)
					{
						TerrainOp component3 = p.GetComponent<TerrainOp>();
						if (component3 != null && component3.m_settings.m_paintCleared)
						{
							return (int)component3.m_settings.m_paintType == 2;
						}
					}
					return false;
				});
				GameObject itemPrefab2 = db.GetItemPrefab("Stone");
				ItemDrop val3 = ((itemPrefab2 != null) ? itemPrefab2.GetComponent<ItemDrop>() : null);
				if ((Object)(object)val2 == (Object)null || (Object)(object)val3 == (Object)null)
				{
					Plugin.Warn("gravel road: the hoe has no paving piece (the game changed?)");
					return;
				}
				gravel = Object.Instantiate<GameObject>(val2, Prefabs.Holder, false);
				((Object)gravel).name = "chistix_trails_gravel";
				TerrainOp component = gravel.GetComponent<TerrainOp>();
				Settings settings = component.m_settings;
				component.m_settings = new Settings
				{
					m_levelOffset = settings.m_levelOffset,
					m_level = settings.m_level,
					m_levelRadius = settings.m_levelRadius,
					m_square = settings.m_square,
					m_raise = settings.m_raise,
					m_raiseRadius = settings.m_raiseRadius,
					m_raisePower = settings.m_raisePower,
					m_raiseDelta = settings.m_raiseDelta,
					m_smooth = settings.m_smooth,
					m_smoothRadius = settings.m_smoothRadius,
					m_smoothPower = settings.m_smoothPower,
					m_paintCleared = true,
					m_paintHeightCheck = settings.m_paintHeightCheck,
					m_paintType = (PaintType)2,
					m_paintRadius = settings.m_paintRadius,
					m_paintStrength = settings.m_paintStrength * 0.35f,
					m_paintExp = settings.m_paintExp,
					m_paintCurve = settings.m_paintCurve,
					m_rotation = settings.m_rotation,
					m_sides = settings.m_sides,
					m_halfOffset = settings.m_halfOffset
				};
				Piece component2 = gravel.GetComponent<Piece>();
				component2.m_name = "$chistix_trails_gravel";
				component2.m_description = "$chistix_trails_gravel_desc";
				component2.m_resources = (Requirement[])(object)new Requirement[1]
				{
					new Requirement
					{
						m_resItem = val3,
						m_amount = 1,
						m_recover = false
					}
				};
			}
			if (!val.m_pieces.Contains(gravel))
			{
				int num = val.m_pieces.FindIndex(delegate(GameObject p)
				{
					//IL_0019: Unknown result type (might be due to invalid IL or missing references)
					//IL_001f: Invalid comparison between Unknown and I4
					if ((Object)(object)p != (Object)null)
					{
						TerrainOp component3 = p.GetComponent<TerrainOp>();
						if (component3 != null)
						{
							return (int)component3.m_settings.m_paintType == 2;
						}
					}
					return false;
				});
				val.m_pieces.Insert((num >= 0) ? num : val.m_pieces.Count, gravel);
			}
			int stableHashCode = StringExtensionMethods.GetStableHashCode("chistix_trails_gravel");
			if (!db.m_terrainOpsByHash.ContainsKey(stableHashCode))
			{
				db.m_terrainOpsByHash[stableHashCode] = gravel.GetComponent<TerrainOp>();
			}
			if ((Object)(object)ZNetScene.instance != (Object)null)
			{
				Scene(ZNetScene.instance);
			}
		}

		private static void Words()
		{
			Localization instance = Localization.instance;
			if (instance != null)
			{
				bool flag = instance.GetSelectedLanguage() == "Russian";
				AddWord(instance, "chistix_trails_gravel", flag ? "Гравийная дорога" : "Gravel road");
				AddWord(instance, "chistix_trails_gravel_desc", flag ? "Засыпать тропу гравием: дорога быстрее тропы и не зарастает. Сверху можно вымостить камнем (мощёная дорога) — ещё быстрее." : "Lay gravel on a trail: a road, faster than the trail, that never grows back. Pave it with stone on top (paved road) to make it faster still.");
			}
		}

		internal static int Level(Piece piece)
		{
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Invalid comparison between Unknown and I4
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Invalid comparison between Unknown and I4
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Invalid comparison between Unknown and I4
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			TerrainOp val = (((Object)(object)piece != (Object)null) ? ((Component)piece).GetComponent<TerrainOp>() : null);
			if ((Object)(object)val == (Object)null || !val.m_settings.m_paintCleared)
			{
				return -1;
			}
			Settings settings = val.m_settings;
			if ((int)settings.m_paintType == 2)
			{
				if (!(Utils.GetPrefabName(((Component)piece).gameObject) == "chistix_trails_gravel"))
				{
					return 2;
				}
				return 1;
			}
			if (Surface.Sphere && settings.m_level && (int)settings.m_paintType == 0)
			{
				return -1;
			}
			if ((int)settings.m_paintType == 4 || (int)settings.m_paintType == 5)
			{
				return -1;
			}
			return 0;
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(Player), "PlacePiece")]
		private static void Placed(Player __instance, Piece piece, Vector3 pos)
		{
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Expected O, but got Unknown
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if ((Object)(object)__instance != (Object)(object)Player.m_localPlayer || !Rules.Enabled)
				{
					return;
				}
				int num = Level(piece);
				if (num < 0)
				{
					return;
				}
				float paintRadius = ((Component)piece).GetComponent<TerrainOp>().m_settings.m_paintRadius;
				if (Rules.Server)
				{
					Host.SetRoad(pos, paintRadius, (byte)num);
					return;
				}
				ZPackage val = new ZPackage();
				val.Write(pos);
				val.Write(paintRadius);
				val.Write((byte)num);
				ZRoutedRpc instance = ZRoutedRpc.instance;
				if (instance != null)
				{
					instance.InvokeRoutedRPC("chistix_trails_road", new object[1] { val });
				}
			}
			catch (Exception ex)
			{
				Plugin.Warn("road stroke: " + ex.Message);
			}
		}
	}
	internal static class Rules
	{
		internal const string HelloRpc = "chistix_trails_hello";

		internal const string RulesRpc = "chistix_trails_rules";

		private const int Version = 4;

		private static bool received;

		private static bool enabled;

		private static bool tamed;

		private static bool wild;

		private static bool carts;

		private static bool crops;

		private static bool mud;

		private static int runBonus;

		private static int staminaSaving;

		private static int roadStep;

		private static float nextHello;

		private static int hellos;

		internal static bool Server
		{
			get
			{
				if ((Object)(object)ZNet.instance != (Object)null)
				{
					return ZNet.instance.IsServer();
				}
				return false;
			}
		}

		internal static bool Enabled
		{
			get
			{
				if (!Server)
				{
					if (received)
					{
						return enabled;
					}
					return false;
				}
				return Plugin.Enabled.Value;
			}
		}

		internal static int RunBonus
		{
			get
			{
				if (!Server)
				{
					return runBonus;
				}
				return Plugin.RunBonus.Value;
			}
		}

		internal static int StaminaSaving
		{
			get
			{
				if (!Server)
				{
					return staminaSaving;
				}
				return Plugin.StaminaSaving.Value;
			}
		}

		internal static int RoadStep
		{
			get
			{
				if (!Server)
				{
					return roadStep;
				}
				return Plugin.RoadBonus.Value;
			}
		}

		internal static bool Tamed
		{
			get
			{
				if (!Server)
				{
					return tamed;
				}
				return Plugin.Tamed.Value;
			}
		}

		internal static bool Wild
		{
			get
			{
				if (!Server)
				{
					return wild;
				}
				return Plugin.Wild.Value;
			}
		}

		internal static bool Carts
		{
			get
			{
				if (!Server)
				{
					return carts;
				}
				return Plugin.Carts.Value;
			}
		}

		internal static bool Crops
		{
			get
			{
				if (!Server)
				{
					return crops;
				}
				return Plugin.CropTrampling.Value;
			}
		}

		internal static bool Mud
		{
			get
			{
				if (!Server)
				{
					return mud;
				}
				return Plugin.Mud.Value;
			}
		}

		internal static bool Received
		{
			get
			{
				if (!Server)
				{
					return received;
				}
				return true;
			}
		}

		internal static void Register(ZRoutedRpc rpc)
		{
			rpc.Register<ZPackage>("chistix_trails_hello", (Action<long, ZPackage>)delegate(long sender, ZPackage pkg)
			{
				if (Server)
				{
					Send(sender);
				}
			});
			rpc.Register<ZPackage>("chistix_trails_rules", (Action<long, ZPackage>)delegate(long sender, ZPackage pkg)
			{
				if (!Server)
				{
					Read(pkg);
				}
			});
		}

		internal static void Reset()
		{
			received = false;
			nextHello = 0f;
			hellos = 0;
		}

		internal static void Update()
		{
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Expected O, but got Unknown
			if (!Server && !received && ZRoutedRpc.instance != null && !((Object)(object)Player.m_localPlayer == (Object)null) && !(Time.time < nextHello) && hellos < 6)
			{
				nextHello = Time.time + 10f;
				if (++hellos == 6)
				{
					Plugin.Log("the host does not answer: no Trails there, trails are off");
				}
				ZRoutedRpc.instance.InvokeRoutedRPC("chistix_trails_hello", new object[1] { (object)new ZPackage() });
			}
		}

		internal static void Changed()
		{
			if (Server && ZRoutedRpc.instance != null && ZNet.instance.GetPeers().Count > 0)
			{
				Send(0L);
			}
		}

		private static void Send(long target)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Expected O, but got Unknown
			ZPackage val = new ZPackage();
			val.Write(4);
			val.Write(Plugin.Enabled.Value);
			val.Write(Plugin.RunBonus.Value);
			val.Write(Plugin.StaminaSaving.Value);
			val.Write(Plugin.Tamed.Value);
			val.Write(Plugin.Carts.Value);
			val.Write(Plugin.RoadBonus.Value);
			val.Write(Plugin.CropTrampling.Value);
			val.Write(Plugin.Mud.Value);
			val.Write(Plugin.Wild.Value);
			ZRoutedRpc.instance.InvokeRoutedRPC(target, "chistix_trails_rules", new object[1] { val });
		}

		private static void Read(ZPackage pkg)
		{
			try
			{
				if (pkg.ReadInt() != 4)
				{
					Plugin.Warn("the host's Trails is of another version: trails are off here");
					received = true;
					enabled = false;
					return;
				}
				enabled = pkg.ReadBool();
				runBonus = Mathf.Clamp(pkg.ReadInt(), 0, 100);
				staminaSaving = Mathf.Clamp(pkg.ReadInt(), 0, 100);
				tamed = pkg.ReadBool();
				carts = pkg.ReadBool();
				roadStep = Mathf.Clamp(pkg.ReadInt(), 0, 50);
				crops = pkg.ReadBool();
				mud = pkg.ReadBool();
				wild = pkg.ReadBool();
				if (!received)
				{
					Plugin.Log(string.Format("host rules: trails {0}, run +{1}% (+{2}% a road step), stamina -{3}%, tamed {4}, wild {5}, carts {6}, crops {7}, mud {8}", enabled ? "on" : "off", runBonus, roadStep, staminaSaving, tamed, wild, carts, crops, mud));
				}
				received = true;
			}
			catch (Exception ex)
			{
				Plugin.Warn("host rules: " + ex.Message);
			}
		}
	}
	internal static class Surface
	{
		private static bool looked;

		private static PropertyInfo active;

		private static PropertyInfo radius;

		private static Func<Vector3, bool> onSurface;

		private static Func<Vector3, bool> groundReady;

		private static MethodInfo groundPoint;

		private static Func<Vector3, Biome> planetBiome;

		private static int terrainLayer = -1;

		private static int sphereFrame = -1;

		private static bool sphere;

		internal static bool Sphere
		{
			get
			{
				int frameCount = Time.frameCount;
				if (frameCount == sphereFrame)
				{
					return sphere;
				}
				Look();
				try
				{
					sphere = active != null && (bool)active.GetValue(null);
				}
				catch
				{
					sphere = false;
				}
				sphereFrame = frameCount;
				return sphere;
			}
		}

		private static float Radius
		{
			get
			{
				try
				{
					return (float)radius.GetValue(null);
				}
				catch
				{
					return 0f;
				}
			}
		}

		internal static int TerrainLayer
		{
			get
			{
				if (terrainLayer < 0)
				{
					terrainLayer = LayerMask.NameToLayer("terrain");
				}
				return terrainLayer;
			}
		}

		internal static bool Flat => !Sphere;

		private static void Look()
		{
			if (looked)
			{
				return;
			}
			looked = true;
			try
			{
				Type type = (from a in AppDomain.CurrentDomain.GetAssemblies()
					select a.GetType("PlanetPrototype.PlanetApi", throwOnError: false)).FirstOrDefault((Type t) => t != null);
				if (type == null)
				{
					return;
				}
				BindingFlags bindingAttr = BindingFlags.Static | BindingFlags.Public;
				PropertyInfo property = type.GetProperty("ApiVersion", bindingAttr);
				if (property == null || (int)property.GetValue(null) < 2)
				{
					Plugin.Warn("PlanetApi is older than v2: trails use the flat grid");
					return;
				}
				active = type.GetProperty("Active", bindingAttr);
				radius = type.GetProperty("Radius", bindingAttr);
				onSurface = Bind(type.GetMethod("OnSurface", bindingAttr, null, new Type[1] { typeof(Vector3) }, null));
				groundReady = Bind(type.GetMethod("GroundReady", bindingAttr, null, new Type[1] { typeof(Vector3) }, null));
				groundPoint = type.GetMethod("GroundPoint", bindingAttr);
				MethodInfo method = type.GetMethod("Biome", bindingAttr, null, new Type[1] { typeof(Vector3) }, null);
				if (method != null && method.ReturnType == typeof(Biome))
				{
					planetBiome = (Func<Vector3, Biome>)Delegate.CreateDelegate(typeof(Func<Vector3, Biome>), method);
				}
				if (active == null || radius == null || onSurface == null || groundPoint == null)
				{
					active = null;
					Plugin.Warn("PlanetApi lacks what trails need: flat grid");
				}
				else
				{
					Plugin.Log("PlanetApi found: trails follow the planet's surface");
				}
			}
			catch (Exception ex)
			{
				Plugin.Warn("PlanetApi: " + ex.Message);
				active = null;
			}
		}

		private static Func<Vector3, bool> Bind(MethodInfo m)
		{
			if (!(m == null))
			{
				return (Func<Vector3, bool>)Delegate.CreateDelegate(typeof(Func<Vector3, bool>), m);
			}
			return null;
		}

		internal static bool ToGrid(Vector3 world, out double x, out double y, out double z)
		{
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			x = (y = (z = 0.0));
			if (Sphere)
			{
				float num = Radius;
				if (num <= 0f || ((Vector3)(ref world)).sqrMagnitude < 1f || !onSurface(world))
				{
					return false;
				}
				Vector3 val = ((Vector3)(ref world)).normalized * num;
				x = val.x;
				y = val.y;
				z = val.z;
				return true;
			}
			if (world.y > 3000f)
			{
				return false;
			}
			x = world.x;
			z = world.z;
			return true;
		}

		internal static bool Ground(long cell, out Vector3 point)
		{
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			Grid.Unpack(cell, out var x, out var y, out var z);
			if (Sphere)
			{
				Vector3 val = default(Vector3);
				((Vector3)(ref val))..ctor((float)x, (float)y, (float)z);
				point = val;
				if (groundReady != null && !groundReady(val))
				{
					return false;
				}
				try
				{
					object[] array = new object[3] { val, null, null };
					if (!(bool)groundPoint.Invoke(null, array))
					{
						return false;
					}
					point = (Vector3)array[1];
					return true;
				}
				catch
				{
					return false;
				}
			}
			point = new Vector3((float)x, 0f, (float)z);
			Heightmap val2 = Heightmap.FindHeightmap(point);
			float y2 = default(float);
			if ((Object)(object)val2 == (Object)null || !val2.GetWorldHeight(point, ref y2))
			{
				return false;
			}
			point.y = y2;
			return true;
		}

		internal static Vector3 RegionPoint(long region, Vector3 near)
		{
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			bool flag = Sphere;
			Grid.Centre(region, out var x, out var y, out var z, !flag);
			if (flag)
			{
				Vector3 val = default(Vector3);
				((Vector3)(ref val))..ctor((float)x, (float)y, (float)z);
				if (!(((Vector3)(ref val)).sqrMagnitude < 1f))
				{
					return ((Vector3)(ref val)).normalized * Mathf.Max(((Vector3)(ref near)).magnitude, 1f);
				}
				return near;
			}
			return new Vector3((float)x, near.y, (float)z);
		}

		internal static Biome Biome(Vector3 world)
		{
			//IL_002d: 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_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			if (Sphere && planetBiome != null)
			{
				try
				{
					return planetBiome(world);
				}
				catch
				{
				}
			}
			if (WorldGenerator.instance == null)
			{
				return (Biome)0;
			}
			return WorldGenerator.instance.GetBiome(world);
		}

		internal static Soil SoilOf(Biome b)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0003: Invalid comparison between Unknown and I4
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Invalid comparison between Unknown and I4
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Invalid comparison between Unknown and I4
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Invalid comparison between Unknown and I4
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Invalid comparison between Unknown and I4
			if ((int)b <= 16)
			{
				if ((int)b == 4)
				{
					goto IL_001a;
				}
				if ((int)b == 16)
				{
					goto IL_001e;
				}
			}
			else
			{
				if ((int)b == 32)
				{
					goto IL_001e;
				}
				if ((int)b == 64)
				{
					goto IL_001a;
				}
			}
			return Soil.Earth;
			IL_001e:
			return Soil.Sand;
			IL_001a:
			return Soil.Snow;
		}

		internal static bool OnTerrain(Character c)
		{
			if ((Object)(object)c == (Object)null || c.IsDead() || !c.IsOnGround() || c.InWater() || c.IsSwimming() || c.InInterior())
			{
				return false;
			}
			Collider lastGroundCollider = c.GetLastGroundCollider();
			if ((Object)(object)lastGroundCollider != (Object)null)
			{
				return ((Component)lastGroundCollider).gameObject.layer == TerrainLayer;
			}
			return false;
		}
	}
	internal static class Walkers
	{
		private const float SampleEvery = 0.2f;

		private const float SendEvery = 4f;

		private const int MaxPerMessage = 3000;

		private const float WetIn = 300f;

		private const float DryIn = 900f;

		private static readonly Dictionary<int, Tracker> trackers = new Dictionary<int, Tracker>();

		private static readonly HashSet<int> seen = new HashSet<int>();

		private static readonly List<int> lost = new List<int>();

		private static readonly Dictionary<long, float> pending = new Dictionary<long, float>();

		private static Vector3 anchor;

		private static float nextSample;

		private static float nextSend;

		private static float lastSample;

		private static int terrainMask;

		internal static float LocalTrail;

		internal static float LocalWear;

		internal static float Wet;

		internal static byte LocalRoad;

		internal static bool LocalMuddy;

		internal static int Sent;

		internal static int Trampled;

		internal static readonly Dictionary<int, float> MountFactor = new Dictionary<int, float>();

		internal static readonly Dictionary<int, float> CartFactor = new Dictionary<int, float>();

		private static readonly Dictionary<ZDOID, Vector3> crops = new Dictionary<ZDOID, Vector3>();

		private static readonly Collider[] near = (Collider[])(object)new Collider[16];

		private static readonly List<ZDOID> left = new List<ZDOID>();

		private static readonly int TrampleHash = StringExtensionMethods.GetStableHashCode("chistix_trails_trample");

		private static readonly int TrampleDayHash = StringExtensionMethods.GetStableHashCode("chistix_trails_trample_day");

		private static readonly List<KeyValuePair<long, float>> batch = new List<KeyValuePair<long,