Decompiled source of ValheimNavigator v0.1.1

ValheimNavigator.dll

Decompiled an hour ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Splatform;
using TMPro;
using Unity.Collections;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Events;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("ValheimNavigator")]
[assembly: AssemblyDescription("Search and filters for the world on the game's large map")]
[assembly: AssemblyFileVersion("0.1.1.0")]
[assembly: InternalsVisibleTo("ValheimNavigatorCheck")]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyVersion("0.1.1.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace ValheimNavigator
{
	internal static class Abstraction
	{
		private sealed class Region
		{
			internal int Type;

			internal int Rep;

			internal int Count;

			internal double SumX;

			internal double SumZ;

			internal readonly Dictionary<int, float> Links = new Dictionary<int, float>();
		}

		internal const float Cell = 24f;

		internal const float Origin = -10512f;

		internal const float Sample = 6f;

		internal const int SectorCells = 8;

		internal const int Margin = 4;

		internal static readonly int N = 877;

		private static readonly int[] Dx = new int[8] { 1, 0, -1, 0, 1, -1, -1, 1 };

		private static readonly int[] Dz = new int[8] { 0, 1, 0, -1, 1, 1, -1, -1 };

		private static float[] _height;

		private static byte[] _biome;

		private static Color32[] _mask;

		private static int _side;

		private static float _pixel;

		private static float _water;

		private static float _maxSlope;

		private static int[] _regionOf;

		private static List<Region> _regions;

		private static List<int>[] _cellsOf;

		private static volatile int _state;

		private static WorldGenerator _world;

		internal static string Problem;

		internal const int LandmarkCount = 4;

		private const float MinSailBias = 0.6f;

		private const float WadePenalty = 1.3f;

		private const float SnapMeters = 17f;

		private static ushort[][] _footMarks;

		private static ushort[][] _sailMarks;

		private static byte[] _rateClass;

		private static float _walkRate;

		private static float _wadeRate;

		private static float _swimRate;

		private static float _sailRate;

		private static volatile bool _landmarksBuilding;

		private static string _landmarkKey;

		private static float _nextKeyCheck;

		internal static float LandmarkSailSpeed;

		private static bool _landmarksFailed;

		private static int[] _queue;

		private static int[] _slot;

		private static float[] _g;

		private static int[] _parent;

		private static int[] _seen;

		private static int[] _closed;

		private static int _stamp;

		private static readonly List<(float f, int r)> _heap = new List<(float, int)>();

		internal static bool Ready => _state == 2;

		internal static bool LandmarksReady
		{
			get
			{
				if (_footMarks != null)
				{
					return _sailMarks != null;
				}
				return false;
			}
		}

		internal static string LandmarkState
		{
			get
			{
				if (!LandmarksReady)
				{
					if (!_landmarksBuilding)
					{
						if (!_landmarksFailed)
						{
							return "off";
						}
						return "failed";
					}
					return "building";
				}
				return "on";
			}
		}

		internal static void Tick(Minimap map)
		{
			if (!((Object)(object)map == (Object)null) && WorldGenerator.instance != null)
			{
				float num = Mathf.Tan(Mathf.Max(Settings.MaxSlope.Value, (Settings.JumpMeters.Value > 0f) ? Settings.JumpSlope.Value : 0f) * ((float)Math.PI / 180f));
				if (_state != 1 && (_world != WorldGenerator.instance || Math.Abs(num - _maxSlope) > 0.0001f))
				{
					_state = 0;
					_footMarks = (_sailMarks = null);
				}
				if (_state == 2 && !_landmarksBuilding && !_landmarksFailed && Time.unscaledTime >= _nextKeyCheck && (_nextKeyCheck = Time.unscaledTime + 2f) > 0f && _landmarkKey != LandmarkKey())
				{
					_footMarks = (_sailMarks = null);
					_landmarksBuilding = true;
					Thread thread = new Thread(BuildLandmarks);
					thread.IsBackground = true;
					thread.Priority = ThreadPriority.BelowNormal;
					thread.Name = "ValheimNavigator route landmarks";
					thread.Start();
				}
				if (_state == 0 && DetailOverlay.EnsureCache(map))
				{
					_world = WorldGenerator.instance;
					_maxSlope = num;
					_height = DetailOverlay.HeightData;
					_biome = DetailOverlay.BiomeData;
					_mask = DetailOverlay.MaskData;
					_side = DetailOverlay.DataSide;
					_pixel = map.m_pixelSize;
					_water = (((Object)(object)ZoneSystem.instance != (Object)null) ? ZoneSystem.instance.m_waterLevel : 30f);
					_state = 1;
					_landmarksBuilding = true;
					Thread thread2 = new Thread(Build);
					thread2.IsBackground = true;
					thread2.Priority = ThreadPriority.BelowNormal;
					thread2.Name = "ValheimNavigator route abstraction";
					thread2.Start();
				}
			}
		}

		private static float X(int c)
		{
			return -10512f + (float)(c % N) * 24f;
		}

		private static float Z(int c)
		{
			return -10512f + (float)(c / N) * 24f;
		}

		internal static int CellAt(float x, float z)
		{
			int num = (int)Math.Round((x - -10512f) / 24f);
			int num2 = (int)Math.Round((z - -10512f) / 24f);
			if (num >= 0 && num2 >= 0 && num < N && num2 < N)
			{
				return num2 * N + num;
			}
			return -1;
		}

		private static int Neighbour(int c, int d)
		{
			int num = c % N + Dx[d];
			int num2 = c / N + Dz[d];
			if (num >= 0 && num2 >= 0 && num < N && num2 < N)
			{
				return num2 * N + num;
			}
			return -1;
		}

		private static float Height(float x, float z, out int texel)
		{
			float num = x / _pixel + (float)_side / 2f;
			float num2 = z / _pixel + (float)_side / 2f;
			int num3 = Math.Max(0, Math.Min(_side - 2, (int)Math.Floor(num)));
			int num4 = Math.Max(0, Math.Min(_side - 2, (int)Math.Floor(num2)));
			float num5 = Math.Max(0f, Math.Min(1f, num - (float)num3));
			float num6 = Math.Max(0f, Math.Min(1f, num2 - (float)num4));
			int num7 = num4 * _side + num3;
			texel = Math.Max(0, Math.Min(_side * _side - 1, (int)(num2 + 0.5f) * _side + (int)(num + 0.5f)));
			return (_height[num7] * (1f - num5) + _height[num7 + 1] * num5) * (1f - num6) + (_height[num7 + _side] * (1f - num5) + _height[num7 + _side + 1] * num5) * num6;
		}

		private static bool Lava(int texel)
		{
			if (_biome[texel] == 5)
			{
				return _mask[texel].b > 25;
			}
			return false;
		}

		private static int Rank(int texel, float x, float z, float h)
		{
			switch (_biome[texel])
			{
			case 0:
				return 1;
			case 1:
				return 2;
			case 2:
				return 3;
			case 3:
				return 5;
			case 4:
				return 6;
			case 5:
				return 7;
			case 6:
				if (!(h < _water))
				{
					if (!(x * x + (z + 4000f) * (z + 4000f) > 144000000f))
					{
						return 4;
					}
					return 8;
				}
				return 0;
			default:
				return 0;
			}
		}

		private static int TypeOf(int c)
		{
			float num = X(c);
			float num2 = Z(c);
			if (num * num + num2 * num2 > 110250000f)
			{
				return -1;
			}
			int texel;
			float num3 = Height(num, num2, out texel);
			if (num3 < _water)
			{
				if (!Lava(texel))
				{
					if (!(_water - num3 >= 2f))
					{
						return 9;
					}
					return 10;
				}
				return 11;
			}
			if (Lava(texel))
			{
				return -1;
			}
			return Rank(texel, num, num2, num3);
		}

		private static bool Passable(int a, int b)
		{
			float num = X(a);
			float num2 = Z(a);
			float num3 = X(b);
			float num4 = Z(b);
			float num5 = (float)Math.Sqrt((num3 - num) * (num3 - num) + (num4 - num2) * (num4 - num2));
			int num6 = (int)Math.Ceiling(num5 / 6f);
			float num7 = num5 / (float)num6;
			int texel;
			float num8 = Height(num, num2, out texel);
			if (num8 >= _water && Lava(texel))
			{
				return false;
			}
			for (int i = 1; i <= num6; i++)
			{
				float num9 = (float)i / (float)num6;
				float num10 = Height(num + (num3 - num) * num9, num2 + (num4 - num2) * num9, out texel);
				bool flag = num8 < _water;
				bool flag2 = num10 < _water;
				if (!flag2 && Lava(texel))
				{
					return false;
				}
				if (!(flag && flag2) && Math.Abs((flag2 ? _water : num10) - (flag ? _water : num8)) / num7 > _maxSlope)
				{
					return false;
				}
				num8 = num10;
			}
			return true;
		}

		private static void Build()
		{
			try
			{
				Stopwatch stopwatch = Stopwatch.StartNew();
				int[] array = new int[N * N];
				for (int i = 0; i < N * N; i++)
				{
					array[i] = TypeOf(i);
				}
				int[] array2 = new int[N * N];
				for (int j = 0; j < array2.Length; j++)
				{
					array2[j] = -1;
				}
				List<Region> list = new List<Region>();
				Queue<int> queue = new Queue<int>();
				for (int k = 0; k < N * N; k++)
				{
					if (array[k] < 0 || array2[k] >= 0)
					{
						continue;
					}
					int num = k % N / 8;
					int num2 = k / N / 8;
					int count = list.Count;
					Region region = new Region
					{
						Type = array[k]
					};
					list.Add(region);
					array2[k] = count;
					queue.Enqueue(k);
					while (queue.Count > 0)
					{
						int num3 = queue.Dequeue();
						region.Count++;
						region.SumX += X(num3);
						region.SumZ += Z(num3);
						for (int l = 0; l < 8; l++)
						{
							int num4 = Neighbour(num3, l);
							if (num4 >= 0 && array2[num4] < 0 && array[num4] == region.Type && num4 % N / 8 == num && num4 / N / 8 == num2 && Passable(num3, num4))
							{
								array2[num4] = count;
								queue.Enqueue(num4);
							}
						}
					}
				}
				float[] array3 = new float[list.Count];
				for (int m = 0; m < array3.Length; m++)
				{
					array3[m] = float.MaxValue;
				}
				List<int>[] array4 = new List<int>[list.Count];
				for (int n = 0; n < array4.Length; n++)
				{
					array4[n] = new List<int>();
				}
				for (int num5 = 0; num5 < N * N; num5++)
				{
					int num6 = array2[num5];
					if (num6 >= 0)
					{
						array4[num6].Add(num5);
						Region region2 = list[num6];
						float num7 = X(num5) - (float)(region2.SumX / (double)region2.Count);
						float num8 = Z(num5) - (float)(region2.SumZ / (double)region2.Count);
						float num9 = num7 * num7 + num8 * num8;
						if (num9 < array3[num6])
						{
							array3[num6] = num9;
							region2.Rep = num5;
						}
					}
				}
				for (int num10 = 0; num10 < N * N; num10++)
				{
					int num11 = array2[num10];
					if (num11 < 0)
					{
						continue;
					}
					for (int num12 = 0; num12 < 8; num12++)
					{
						int num13 = Neighbour(num10, num12);
						if (num13 >= 0)
						{
							int num14 = array2[num13];
							if (num14 >= 0 && num14 != num11 && !list[num11].Links.ContainsKey(num14) && Passable(num10, num13))
							{
								float value = Distance(list[num11].Rep, list[num14].Rep);
								list[num11].Links[num14] = value;
								list[num14].Links[num11] = value;
							}
						}
					}
				}
				_regionOf = array2;
				_regions = list;
				_cellsOf = array4;
				_state = 2;
				Problem = null;
				if (Plugin.LogCounters.Value)
				{
					Plugin.Log.LogInfo((object)("route abstraction ready, " + list.Count + " regions, " + stopwatch.Elapsed.TotalMilliseconds.ToString("F0") + " ms on a background thread"));
				}
				BuildLandmarks();
			}
			catch (Exception ex)
			{
				Problem = ex.Message;
				_state = 3;
				_landmarksBuilding = false;
				Plugin.Log.LogWarning((object)("route abstraction failed: " + ex));
			}
		}

		private static void Stage(Stopwatch clock, string what)
		{
			if (Plugin.LogCounters.Value)
			{
				Plugin.Log.LogInfo((object)("route landmarks: " + what + ", " + clock.Elapsed.TotalMilliseconds.ToString("F0") + " ms"));
			}
		}

		private static float FastestShip()
		{
			float val = 9.5f;
			string[] array = (Settings.ShipSpeedList.Value ?? "").Split(new char[2] { ',', ';' }, StringSplitOptions.RemoveEmptyEntries);
			for (int i = 0; i < array.Length; i++)
			{
				string[] array2 = array[i].Split(new char[1] { '=' });
				if (array2.Length != 2)
				{
					continue;
				}
				string[] array3 = array2[1].Split(new char[1] { '/' });
				for (int j = 0; j < array3.Length; j++)
				{
					if (float.TryParse(array3[j], NumberStyles.Float, CultureInfo.InvariantCulture, out var result))
					{
						val = Math.Max(val, result);
					}
				}
			}
			return Math.Max(val, Settings.SailSpeed.Value);
		}

		private static string LandmarkKey()
		{
			return Settings.WalkSpeed.Value + "|" + Settings.WadeDepth.Value + "|" + Settings.SwimSpeed.Value + "|" + Settings.SwimRisk.Value + "|" + FastestShip();
		}

		private static void BuildLandmarks()
		{
			try
			{
				Stopwatch stopwatch = Stopwatch.StartNew();
				WorldGenerator world = _world;
				string landmarkKey = LandmarkKey();
				Stage(stopwatch, "building");
				float value = Settings.WadeDepth.Value;
				_walkRate = 1f / Settings.WalkSpeed.Value;
				_wadeRate = 1.3f / Settings.WalkSpeed.Value;
				_swimRate = Settings.SwimRisk.Value / Settings.SwimSpeed.Value;
				float num = FastestShip() / 0.6f;
				_sailRate = 1f / num;
				int[] array = new int[4] { 0, 1, 4, 5 };
				float[] array2 = new float[N * N * 4];
				float[] array3 = new float[N * N * 4];
				byte[] array4 = new byte[N * N];
				for (int i = 0; i < N * N; i++)
				{
					float num2 = X(i);
					float num3 = Z(i);
					int texel;
					float num4 = Height(num2, num3, out texel);
					float num5 = _water - num4;
					array4[i] = (byte)((num5 >= value + 1f) ? 2u : ((num5 >= 1f) ? 1u : 0u));
					for (int j = 0; j < 4; j++)
					{
						int d = array[j];
						int num6 = Neighbour(i, d);
						float num7 = X(Math.Max(0, num6));
						float num8 = Z(Math.Max(0, num6));
						if (num6 < 0 || num2 * num2 + num3 * num3 > 110250000f || num7 * num7 + num8 * num8 > 110250000f)
						{
							array2[i * 4 + j] = (array3[i * 4 + j] = float.PositiveInfinity);
							continue;
						}
						float num9 = (float)Math.Sqrt((num7 - num2) * (num7 - num2) + (num8 - num3) * (num8 - num3));
						int num10 = (int)Math.Ceiling(num9 / 6f);
						float num11 = num9 / (float)num10;
						float num12 = num4;
						float num13 = 0f;
						float num14 = 0f;
						for (int k = 1; k <= num10; k++)
						{
							float num15 = (float)k / (float)num10;
							float num16 = Height(num2 + (num7 - num2) * num15, num3 + (num8 - num3) * num15, out texel);
							float num17 = _water - Math.Max(num12, num16);
							float num18 = ((num17 >= value + 1f) ? _swimRate : ((num17 >= 1f) ? _wadeRate : _walkRate));
							num13 += num11 * num18;
							num14 += ((num12 < _water + 1f && num16 < _water + 1f) ? (num11 * Math.Min(num18, _sailRate)) : (num11 * num18));
							num12 = num16;
						}
						array2[i * 4 + j] = num13;
						array3[i * 4 + j] = num14;
					}
				}
				byte[] array5 = new byte[N * N];
				for (int l = 0; l < N * N; l++)
				{
					byte b = array4[l];
					for (int m = 0; m < 8; m++)
					{
						int num19 = Neighbour(l, m);
						if (num19 >= 0 && array4[num19] > b)
						{
							b = array4[num19];
						}
					}
					array5[l] = b;
				}
				Stage(stopwatch, "edges ready");
				ushort[][] footMarks = Place(array2);
				array2 = null;
				Stage(stopwatch, "foot tables ready");
				ushort[][] sailMarks = Place(array3);
				Stage(stopwatch, "sailing tables ready");
				if (world != _world)
				{
					Stage(stopwatch, "world changed, tables dropped");
					return;
				}
				_rateClass = array5;
				LandmarkSailSpeed = num;
				_landmarkKey = landmarkKey;
				_footMarks = footMarks;
				_sailMarks = sailMarks;
				_queue = (_slot = null);
				if (Plugin.LogCounters.Value)
				{
					Plugin.Log.LogInfo((object)("route landmarks ready, " + 4 + " on foot and " + 4 + " sailing, " + stopwatch.Elapsed.TotalMilliseconds.ToString("F0") + " ms on a background thread"));
				}
			}
			catch (Exception ex)
			{
				_footMarks = (_sailMarks = null);
				Plugin.Log.LogWarning((object)("route landmarks failed, not tried again this session: " + ex));
				_queue = (_slot = null);
				_landmarksFailed = true;
			}
			finally
			{
				_landmarksBuilding = false;
			}
		}

		private static float EdgeTime(float[] edges, int c, int d, int j)
		{
			return d switch
			{
				0 => edges[c * 4], 
				1 => edges[c * 4 + 1], 
				4 => edges[c * 4 + 2], 
				5 => edges[c * 4 + 3], 
				2 => edges[j * 4], 
				3 => edges[j * 4 + 1], 
				6 => edges[j * 4 + 2], 
				_ => edges[j * 4 + 3], 
			};
		}

		private static float[] Dijkstra(float[] edges, int source)
		{
			float[] dist = new float[N * N];
			for (int i = 0; i < dist.Length; i++)
			{
				dist[i] = float.PositiveInfinity;
			}
			if (_queue == null)
			{
				_queue = new int[N * N];
				_slot = new int[N * N];
			}
			int[] queue = _queue;
			int[] slot = _slot;
			for (int j = 0; j < slot.Length; j++)
			{
				slot[j] = -1;
			}
			int num = 0;
			dist[source] = 0f;
			queue[0] = source;
			slot[source] = 0;
			num = 1;
			while (num > 0)
			{
				int num2 = queue[0];
				slot[num2] = -2;
				num--;
				if (num > 0)
				{
					int num3 = queue[num];
					float num4 = dist[num3];
					int num5 = 0;
					while (true)
					{
						int num6 = 2 * num5 + 1;
						if (num6 >= num)
						{
							break;
						}
						if (num6 + 1 < num && dist[queue[num6 + 1]] < dist[queue[num6]])
						{
							num6++;
						}
						if (num4 <= dist[queue[num6]])
						{
							break;
						}
						queue[num5] = queue[num6];
						slot[queue[num5]] = num5;
						num5 = num6;
					}
					queue[num5] = num3;
					slot[num3] = num5;
				}
				float num7 = dist[num2];
				for (int k = 0; k < 8; k++)
				{
					int num8 = Neighbour(num2, k);
					if (num8 < 0 || slot[num8] == -2)
					{
						continue;
					}
					float num9 = EdgeTime(edges, num2, k, num8);
					if (!float.IsInfinity(num9) && !(num7 + num9 >= dist[num8]))
					{
						dist[num8] = num7 + num9;
						if (slot[num8] < 0)
						{
							queue[num] = num8;
							slot[num8] = num;
							num++;
						}
						Up(slot[num8]);
					}
				}
			}
			return dist;
			void Up(int num11)
			{
				int num10 = queue[num11];
				float num12 = dist[num10];
				while (num11 > 0)
				{
					int num13 = (num11 - 1) / 2;
					if (dist[queue[num13]] <= num12)
					{
						break;
					}
					queue[num11] = queue[num13];
					slot[queue[num11]] = num11;
					num11 = num13;
				}
				queue[num11] = num10;
				slot[num10] = num11;
			}
		}

		private static ushort[][] Place(float[] edges)
		{
			ushort[][] array = new ushort[4][];
			float[] array2 = Dijkstra(edges, CellAt(0f, 0f));
			for (int i = 0; i < 4; i++)
			{
				int num = -1;
				float num2 = 0f;
				for (int j = 0; j < array2.Length; j++)
				{
					if (!float.IsInfinity(array2[j]) && array2[j] > num2)
					{
						num2 = array2[j];
						num = j;
					}
				}
				if (num < 0)
				{
					Array.Resize(ref array, i);
					break;
				}
				float[] array3 = Dijkstra(edges, num);
				ushort[] array4 = new ushort[N * N];
				for (int k = 0; k < N * N; k++)
				{
					array4[k] = (float.IsInfinity(array3[k]) ? ushort.MaxValue : ((ushort)Math.Min(65534.0, Math.Floor(array3[k]))));
					array2[k] = Math.Min(array2[k], array3[k]);
				}
				array[i] = array4;
			}
			return array;
		}

		internal static float LandmarkBound(float fx, float fz, float tx, float tz, bool sail)
		{
			ushort[][] array = (sail ? _sailMarks : _footMarks);
			byte[] rateClass = _rateClass;
			if (array == null || rateClass == null)
			{
				return 0f;
			}
			int num = CellAt(fx, fz);
			int num2 = CellAt(tx, tz);
			if (num < 0 || num2 < 0)
			{
				return 0f;
			}
			int num3 = 0;
			ushort[][] array2 = array;
			foreach (ushort[] obj in array2)
			{
				int num4 = obj[num];
				int num5 = obj[num2];
				if (num4 != 65535 && num5 != 65535)
				{
					num3 = Math.Max(num3, Math.Abs(num4 - num5));
				}
			}
			return Math.Max(0f, (float)num3 - 1f - 17f * (SlackRate(rateClass[num]) + SlackRate(rateClass[num2])));
		}

		private static float SlackRate(byte rate)
		{
			return rate switch
			{
				1 => _wadeRate, 
				2 => _swimRate, 
				_ => _walkRate, 
			};
		}

		private static float Distance(int a, int b)
		{
			float num = X(b) - X(a);
			float num2 = Z(b) - Z(a);
			return (float)Math.Sqrt(num * num + num2 * num2);
		}

		private static float Rate(NavOptions o, int type, bool sail)
		{
			switch (type)
			{
			case 11:
				if (!sail)
				{
					return o.SwimRisk / o.SwimSpeed * o.BoilingPenalty;
				}
				if (!o.HeatProof)
				{
					return float.PositiveInfinity;
				}
				return o.SailBias / o.SailSpeed;
			case 10:
				if (!sail)
				{
					return o.SwimRisk / o.SwimSpeed;
				}
				return o.SailBias / o.SailSpeed;
			case 9:
				return o.WadePenalty / o.WalkSpeed;
			default:
				if (type > o.DangerRank)
				{
					if (!o.DangerBlock)
					{
						return o.DangerPenalty / o.WalkSpeed;
					}
					return float.PositiveInfinity;
				}
				return 1f / o.WalkSpeed;
			}
		}

		private static bool Wet(int type)
		{
			return type >= 9;
		}

		private static bool Sailable(NavOptions o, int type)
		{
			switch (type)
			{
			case 11:
				return o.HeatProof;
			default:
				return false;
			case 9:
			case 10:
				return true;
			}
		}

		private static List<int> Path(NavOptions o, int from, int to, bool boats, Dictionary<int, List<int>> portals, byte startMode = 0, bool free = false)
		{
			int num = ((from >= 0) ? _regionOf[from] : (-1));
			int num2 = ((to >= 0) ? _regionOf[to] : (-1));
			if (num < 0 || num2 < 0)
			{
				return null;
			}
			int num3 = -1;
			if (boats && o.HasShip)
			{
				int num4 = CellAt(o.ShipX, o.ShipZ);
				num3 = ((num4 >= 0) ? _regionOf[num4] : (-1));
			}
			float fastest = (boats ? Math.Max(o.WalkSpeed, o.Fastest) : o.WalkSpeed);
			int num5 = _regions.Count * 3;
			if (_g == null || _g.Length < num5)
			{
				_g = new float[num5];
				_parent = new int[num5];
				_seen = new int[num5];
				_closed = new int[num5];
			}
			int num6 = ++_stamp;
			_heap.Clear();
			float gx = X(to);
			float gz = Z(to);
			int num7 = num * 3 + ((boats && !free) ? startMode : 0);
			_g[num7] = 0f;
			_parent[num7] = -1;
			_seen[num7] = num6;
			Push(0f, num7);
			int num8 = -1;
			while (_heap.Count > 0)
			{
				int item = Pop().r;
				if (_closed[item] == num6)
				{
					continue;
				}
				_closed[item] = num6;
				int num9 = item / 3;
				int num10 = item % 3;
				int type = _regions[num9].Type;
				if (num9 == num2 && num10 != 1)
				{
					num8 = item;
					break;
				}
				foreach (KeyValuePair<int, float> link in _regions[num9].Links)
				{
					int key = link.Key;
					int type2 = _regions[key].Type;
					float num11 = link.Value * 0.5f;
					if (free)
					{
						Visit(item, key * 3, num11 * (Rate(o, type, boats) + Rate(o, type2, boats)), num6, gx, gz, fastest);
						continue;
					}
					if (num10 != 1)
					{
						Visit(item, key * 3 + num10, num11 * (Rate(o, type, sail: false) + Rate(o, type2, sail: false)), num6, gx, gz, fastest);
						bool flag = (num10 == 0 && o.BuildBoat) || (num10 == 2 && o.MoreBoats);
						if (boats && flag && !Wet(type) && Sailable(o, type2))
						{
							Visit(item, key * 3 + 1, num11 * (Rate(o, type, sail: false) + o.SailBias / o.SailSpeed) + o.BuildSeconds, num6, gx, gz, fastest);
						}
						continue;
					}
					if (Sailable(o, type) && Sailable(o, type2))
					{
						Visit(item, key * 3 + 1, num11 * 2f * o.SailBias / o.SailSpeed, num6, gx, gz, fastest);
					}
					if (!Wet(type2))
					{
						Visit(item, key * 3 + 2, num11 * (o.SailBias / o.SailSpeed + Rate(o, type2, sail: false)) + o.SwitchSeconds, num6, gx, gz, fastest);
					}
				}
				if (num10 == 0 && num9 == num3 && !free)
				{
					Visit(item, num9 * 3 + 1, o.SwitchSeconds, num6, gx, gz, fastest);
				}
				if (num10 == 1 || portals == null || !portals.TryGetValue(num9, out var value))
				{
					continue;
				}
				foreach (int item2 in value)
				{
					Visit(item, item2 * 3 + num10, o.PortalSeconds, num6, gx, gz, fastest);
				}
			}
			if (num8 < 0)
			{
				return null;
			}
			List<int> list = new List<int>();
			for (int num12 = num8; num12 >= 0; num12 = _parent[num12])
			{
				list.Add(num12 / 3);
			}
			return list;
		}

		private static void Visit(int r, int q, float cost, int stamp, float gx, float gz, float fastest)
		{
			if (!float.IsInfinity(cost) && _closed[q] != stamp)
			{
				float num = _g[r] + cost;
				if (_seen[q] != stamp || !(num >= _g[q]))
				{
					_g[q] = num;
					_parent[q] = r;
					_seen[q] = stamp;
					int rep = _regions[q / 3].Rep;
					float num2 = X(rep) - gx;
					float num3 = Z(rep) - gz;
					Push(num + (float)Math.Sqrt(num2 * num2 + num3 * num3) / fastest, q);
				}
			}
		}

		private static void Push(float f, int r)
		{
			_heap.Add((f, r));
			int num = _heap.Count - 1;
			while (num > 0)
			{
				int num2 = (num - 1) / 2;
				if (!(_heap[num2].f <= _heap[num].f))
				{
					List<(float, int)> heap = _heap;
					int index = num2;
					List<(float f, int r)> heap2 = _heap;
					int index2 = num;
					(float, int) value = _heap[num];
					(float, int) value2 = _heap[num2];
					heap[index] = value;
					heap2[index2] = value2;
					num = num2;
					continue;
				}
				break;
			}
		}

		private static (float f, int r) Pop()
		{
			(float, int) result = _heap[0];
			(float, int) value = _heap[_heap.Count - 1];
			_heap.RemoveAt(_heap.Count - 1);
			if (_heap.Count > 0)
			{
				_heap[0] = value;
				int num = 0;
				while (true)
				{
					int num2 = 2 * num + 1;
					int num3 = num2 + 1;
					int num4 = num;
					if (num2 < _heap.Count && _heap[num2].f < _heap[num4].f)
					{
						num4 = num2;
					}
					if (num3 < _heap.Count && _heap[num3].f < _heap[num4].f)
					{
						num4 = num3;
					}
					if (num4 == num)
					{
						break;
					}
					List<(float, int)> heap = _heap;
					int index = num4;
					List<(float f, int r)> heap2 = _heap;
					int index2 = num;
					(float, int) value2 = _heap[num];
					(float, int) value3 = _heap[num4];
					heap[index] = value2;
					heap2[index2] = value3;
					num = num4;
				}
			}
			return result;
		}

		internal static bool Snap(ref float x, ref float z)
		{
			if (_state != 2)
			{
				return true;
			}
			int num = CellAt(x, z);
			if (num < 0)
			{
				return false;
			}
			if (_regionOf[num] >= 0)
			{
				return true;
			}
			int num2 = num % N;
			int num3 = num / N;
			int num4 = -1;
			float num5 = float.MaxValue;
			for (int i = -8; i <= 8; i++)
			{
				for (int j = -8; j <= 8; j++)
				{
					int num6 = num2 + j;
					int num7 = num3 + i;
					if (num6 >= 0 && num7 >= 0 && num6 < N && num7 < N)
					{
						int num8 = num7 * N + num6;
						float num9 = j * j + i * i;
						if (_regionOf[num8] >= 0 && num9 < num5)
						{
							num5 = num9;
							num4 = num8;
						}
					}
				}
			}
			if (num4 < 0)
			{
				return false;
			}
			x = X(num4);
			z = Z(num4);
			return true;
		}

		internal static bool[] Corridor(NavOptions o, float sx, float sz, float gx, float gz, byte startMode, out int regionCount, out bool noWay)
		{
			regionCount = 0;
			noWay = false;
			if (_state != 2)
			{
				return null;
			}
			int num = CellAt(sx, sz);
			int num2 = CellAt(gx, gz);
			Dictionary<int, List<int>> dictionary = null;
			foreach (float[] portal in o.Portals)
			{
				int num3 = CellAt(portal[0], portal[1]);
				int num4 = CellAt(portal[2], portal[3]);
				if (num3 >= 0 && num4 >= 0 && _regionOf[num3] >= 0 && _regionOf[num4] >= 0)
				{
					if (dictionary == null)
					{
						dictionary = new Dictionary<int, List<int>>();
					}
					if (!dictionary.TryGetValue(_regionOf[num3], out var value))
					{
						value = (dictionary[_regionOf[num3]] = new List<int>());
					}
					if (!dictionary.TryGetValue(_regionOf[num4], out var value2))
					{
						value2 = (dictionary[_regionOf[num4]] = new List<int>());
					}
					value.Add(_regionOf[num4]);
					value2.Add(_regionOf[num3]);
				}
			}
			HashSet<int> hashSet = new HashSet<int>();
			if (startMode == 0)
			{
				List<int> list3 = Path(o, num, num2, boats: false, dictionary, 0);
				if (list3 != null)
				{
					hashSet.UnionWith(list3);
				}
			}
			if (o.Boats)
			{
				List<int> list4 = Path(o, num, num2, boats: true, dictionary, startMode);
				if (list4 != null)
				{
					hashSet.UnionWith(list4);
				}
				List<int> list5 = Path(o, num, num2, boats: true, dictionary, startMode, free: true);
				if (list5 != null)
				{
					hashSet.UnionWith(list5);
				}
			}
			if (hashSet.Count == 0)
			{
				noWay = num >= 0 && num2 >= 0 && _regionOf[num] >= 0 && _regionOf[num2] >= 0;
				return null;
			}
			regionCount = hashSet.Count;
			bool[] array = new bool[N * N];
			foreach (int item in hashSet)
			{
				foreach (int item2 in _cellsOf[item])
				{
					int num5 = item2 % N;
					int num6 = item2 / N;
					for (int i = -4; i <= 4; i++)
					{
						for (int j = -4; j <= 4; j++)
						{
							int num7 = num5 + j;
							int num8 = num6 + i;
							if (num7 >= 0 && num8 >= 0 && num7 < N && num8 < N)
							{
								array[num8 * N + num7] = true;
							}
						}
					}
				}
			}
			foreach (float[] portal2 in o.Portals)
			{
				MarkAround(array, portal2[0], portal2[1]);
				MarkAround(array, portal2[2], portal2[3]);
			}
			return array;
		}

		internal static void Allow(bool[] allowed, float x, float z, float meters)
		{
			int num = CellAt(x, z);
			if (num < 0)
			{
				return;
			}
			int num2 = num % N;
			int num3 = num / N;
			int num4 = (int)Math.Ceiling(meters / 24f);
			for (int i = -num4; i <= num4; i++)
			{
				for (int j = -num4; j <= num4; j++)
				{
					int num5 = num2 + j;
					int num6 = num3 + i;
					if (num5 >= 0 && num6 >= 0 && num5 < N && num6 < N && j * j + i * i <= num4 * num4)
					{
						allowed[num6 * N + num5] = true;
					}
				}
			}
		}

		private static void MarkAround(bool[] allowed, float x, float z)
		{
			int num = CellAt(x, z);
			if (num < 0)
			{
				return;
			}
			int num2 = num % N;
			int num3 = num / N;
			for (int i = -4; i <= 4; i++)
			{
				for (int j = -4; j <= 4; j++)
				{
					int num4 = num2 + j;
					int num5 = num3 + i;
					if (num4 >= 0 && num5 >= 0 && num4 < N && num5 < N)
					{
						allowed[num5 * N + num4] = true;
					}
				}
			}
		}

		internal static bool Inside(bool[] allowed, float x, float z)
		{
			int num = CellAt(x, z);
			if (num >= 0)
			{
				return allowed[num];
			}
			return false;
		}
	}
	internal static class Categories
	{
		internal sealed class Rule
		{
			internal readonly string Pattern;

			internal readonly string Category;

			internal readonly string Name;

			internal readonly Regex Expression;

			internal Rule(string pattern, string category, string name)
			{
				Pattern = pattern;
				Category = category;
				Name = name;
				Expression = new Regex("^(?:" + pattern + ")$", RegexOptions.ECMAScript);
			}
		}

		internal static readonly string[] Order = new string[15]
		{
			"spawn", "bosses", "traders", "quests", "dungeons", "caves", "spawners", "settlements", "dvergr", "materials",
			"runestones", "rare", "camps", "ruins", "other"
		};

		internal static readonly Dictionary<string, string> Titles = new Dictionary<string, string>
		{
			{ "spawn", "Spawn" },
			{ "bosses", "Bosses" },
			{ "traders", "Traders" },
			{ "quests", "Quest sites" },
			{ "dungeons", "Dungeons" },
			{ "caves", "Caves" },
			{ "spawners", "Spawners" },
			{ "settlements", "Settlements" },
			{ "dvergr", "Dvergr" },
			{ "materials", "Materials" },
			{ "runestones", "Runestones" },
			{ "rare", "Rare finds" },
			{ "camps", "Camps" },
			{ "ruins", "Ruins" },
			{ "other", "Other" }
		};

		internal static readonly Rule[] Rules = new Rule[87]
		{
			new Rule("StartTemple", "spawn", "Sacrificial stones"),
			new Rule("Eikthyrnir", "bosses", "Eikthyr"),
			new Rule("GDKing", "bosses", "The Elder"),
			new Rule("Bonemass", "bosses", "Bonemass"),
			new Rule("Dragonqueen", "bosses", "Moder"),
			new Rule("GoblinKing", "bosses", "Yagluth"),
			new Rule("Mistlands_DvergrBossEntrance1", "bosses", "The Queen"),
			new Rule("FaderLocation", "bosses", "Fader"),
			new Rule("DN_Bossroom", "bosses", "Deep North boss"),
			new Rule("Vendor_BlackForest", "traders", "Haldor"),
			new Rule("Hildir_camp", "traders", "Hildir"),
			new Rule("BogWitch_Camp", "traders", "Bog Witch"),
			new Rule("Hildir_crypt", "quests", "Smouldering Tomb"),
			new Rule("Hildir_cave", "quests", "Howling Cavern"),
			new Rule("Hildir_plainsfortress", "quests", "Sealed Tower"),
			new Rule("Crypt\\d", "dungeons", "Burial Chambers"),
			new Rule("SunkenCrypt\\d", "dungeons", "Sunken Crypt"),
			new Rule("MountainCave\\d+", "dungeons", "Frost Cave"),
			new Rule("Mistlands_DvergrTownEntrance\\d", "dungeons", "Infested Mine"),
			new Rule("CharredFortress", "dungeons", "Charred Fortress"),
			new Rule("TrollCave\\d+", "caves", "Troll Cave"),
			new Rule("BearCave", "caves", "Bear Cave"),
			new Rule("MorgenHole\\d", "caves", "Morgen Hole"),
			new Rule("TheHole\\d+", "caves", "The Hole"),
			new Rule("Greydwarf_camp\\d", "spawners", "Greydwarf nest camp"),
			new Rule("Grave1", "spawners", "Swamp grave"),
			new Rule("CharredStone_Spawner", "spawners", "Charred spawner"),
			new Rule("VoltureNest", "spawners", "Volture nest"),
			new Rule("FireHole", "spawners", "Surtling fire hole"),
			new Rule("GoblinCamp\\d", "settlements", "Fuling village"),
			new Rule("WoodVillage\\d", "settlements", "Draugr village"),
			new Rule("StoneTower\\d", "settlements", "Fuling tower"),
			new Rule("NorthVillage", "settlements", "Northern village"),
			new Rule("MorkBorg", "settlements", "Mork Borg"),
			new Rule("LumberCamp", "settlements", "Lumber camp"),
			new Rule("DN_hut\\d+", "settlements", "Northern hut"),
			new Rule("Mistlands_Harbour\\d", "dvergr", "Dvergr harbour"),
			new Rule("Mistlands_Lighthouse\\d_new", "dvergr", "Dvergr lighthouse"),
			new Rule("Mistlands_Excavation\\d", "dvergr", "Dvergr excavation"),
			new Rule("Mistlands_GuardTower\\d_new", "dvergr", "Dvergr guard tower"),
			new Rule("Mistlands_RoadPost\\d", "dvergr", "Dvergr road post"),
			new Rule("CharredTowerRuins\\d_dvergr", "dvergr", "Dvergr outpost"),
			new Rule("TarPit\\d(_\\d)?", "materials", "Tar pit"),
			new Rule("InfestedTree\\d+", "materials", "Guck tree"),
			new Rule("DrakeNest\\d+", "materials", "Drake nest"),
			new Rule("Mistlands_Giant\\d", "materials", "Giant remains"),
			new Rule("LeviathanLava", "materials", "Lava leviathan"),
			new Rule("ShipWreck\\d+(_DN)?", "materials", "Shipwreck"),
			new Rule("FrozenShip\\d+_DN", "materials", "Frozen ship"),
			new Rule("Runestone_(\\w+)", "runestones", "Runestone"),
			new Rule("DrakeLorestone", "runestones", "Drake lore stone"),
			new Rule("AncientUpgradeStation", "rare", "Ancient upgrade station"),
			new Rule("PlaceofMystery\\d", "rare", "Place of mystery"),
			new Rule("BigRockClearing", "rare", "Big rock clearing"),
			new Rule("StoneTowerRuins05_leet", "rare", "Stone tower ruin (rare)"),
			new Rule("CombatRuin01", "rare", "Combat ruin"),
			new Rule("WoodHouse\\d+", "camps", "Abandoned house"),
			new Rule("WoodFarm\\d", "camps", "Abandoned farm"),
			new Rule("SwampHut\\d(_\\d)?", "camps", "Swamp hut"),
			new Rule("GoblinHut\\d+", "camps", "Goblin hut"),
			new Rule("AbandonedLogCabin\\d+", "camps", "Log cabin"),
			new Rule("SwampWell\\d", "camps", "Swamp well"),
			new Rule("MountainWell\\d", "camps", "Mountain well"),
			new Rule("Ruin\\d", "ruins", "Ruin"),
			new Rule("StoneHouse\\d", "ruins", "Stone house ruin"),
			new Rule("StoneTowerRuins\\d+(_sunk)?", "ruins", "Stone tower ruin"),
			new Rule("SwampRuin\\d", "ruins", "Swamp ruin"),
			new Rule("MountainGrave\\d+", "ruins", "Mountain grave"),
			new Rule("Mistlands_GuardTower\\d_ruined_new\\d?", "ruins", "Ruined guard tower"),
			new Rule("Mistlands_Viaduct\\d", "ruins", "Viaduct"),
			new Rule("Mistlands_Statue\\d", "ruins", "Giant statue"),
			new Rule("Mistlands_StatueGroup\\d", "ruins", "Statue group"),
			new Rule("Mistlands_Swords\\d", "ruins", "Giant swords"),
			new Rule("Mistlands_RockSpire\\d", "ruins", "Rock spire"),
			new Rule("AshlandRuins", "ruins", "Ashlands ruin"),
			new Rule("CharredRuins\\d", "ruins", "Charred ruin"),
			new Rule("CharredTowerRuins\\d", "ruins", "Charred tower ruin"),
			new Rule("FortressRuins", "ruins", "Fortress ruin"),
			new Rule("SulfurArch", "ruins", "Sulfur arch"),
			new Rule("NorthMemorialPlace", "ruins", "Memorial"),
			new Rule("DN_gammeltrollFrac\\d+", "ruins", "Fallen troll"),
			new Rule("IcePond\\d", "ruins", "Ice pond"),
			new Rule("Dolmen\\d+", "ruins", "Dolmen"),
			new Rule("StoneHenge\\d", "ruins", "Standing stones"),
			new Rule("StoneCircle", "ruins", "Stone circle"),
			new Rule("Waymarker\\d+", "ruins", "Waymarker"),
			new Rule("ShipSetting\\d+", "ruins", "Stone ship")
		};

		private static readonly Dictionary<string, KeyValuePair<string, string>> Cache = new Dictionary<string, KeyValuePair<string, string>>();

		internal static string Split(string text)
		{
			return Regex.Replace(text, "(?<!^)(?=[A-Z])", " ");
		}

		internal static void Classify(string prefab, out string category, out string name)
		{
			if (Cache.TryGetValue(prefab, out var value))
			{
				category = value.Key;
				name = value.Value;
				return;
			}
			category = "other";
			name = Split(prefab.Replace("_", " "));
			Rule[] rules = Rules;
			foreach (Rule rule in rules)
			{
				Match match = rule.Expression.Match(prefab);
				if (match.Success)
				{
					category = rule.Category;
					name = rule.Name;
					if (category == "runestones" && match.Groups.Count > 1 && match.Groups[1].Value.Length > 0)
					{
						name = "Runestone (" + Split(match.Groups[1].Value) + ")";
					}
					break;
				}
			}
			Cache[prefab] = new KeyValuePair<string, string>(category, name);
		}
	}
	internal static class Commands
	{
		private const string Usage = "navigator count | types [text] | nearest <type> | done | undo | reset <type> | sync | importlegacy";

		private const int ListLimit = 40;

		private static bool _registered;

		internal static void Register()
		{
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Expected O, but got Unknown
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			if (!_registered)
			{
				_registered = true;
				new ConsoleCommand("navigator", "navigator count | types [text] | nearest <type> | done | undo | reset <type> | sync | importlegacy", new ConsoleEvent(Handle), false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
			}
		}

		private static void Handle(ConsoleEventArgs args)
		{
			Terminal context = args.Context;
			if (!Plugin.Enabled.Value)
			{
				context.AddString("Valheim Navigator is off in its config.");
				return;
			}
			string text = ((args.Length > 1) ? args[1].ToLowerInvariant() : "");
			string query = ((args.Length > 2) ? string.Join(" ", args.Args.Skip(2)) : "");
			if (text == "sync")
			{
				WorldSync.Retry();
				context.AddString(WorldSync.Status);
				return;
			}
			if (!Session.BuildNow())
			{
				context.AddString(Session.Problem ?? "Valheim Navigator has no location list yet, load a world first.");
				return;
			}
			switch (text)
			{
			case "count":
				Count(context);
				break;
			case "types":
				Types(context, query);
				break;
			case "nearest":
				Pick(context, query);
				break;
			case "done":
				Done(context);
				break;
			case "undo":
				Undo(context);
				break;
			case "reset":
				Reset(context, query);
				break;
			case "importlegacy":
				context.AddString("Imported " + Session.ImportLegacy() + " matching completion marks. Original files were kept.");
				break;
			default:
				context.AddString("navigator count | types [text] | nearest <type> | done | undo | reset <type> | sync | importlegacy");
				break;
			}
		}

		private static void Count(Terminal say)
		{
			List<Entry> entries = Session.Entries;
			say.AddString($"Navigator: {entries.Count} locations, {entries.Count((Entry e) => e.Placed)} placed, {Session.Kinds.Count} kinds, indexed in {Session.BuildMilliseconds:F1} ms, worst slice {Session.WorstSliceMilliseconds:F2} ms");
			string[] order = Categories.Order;
			foreach (string category in order)
			{
				int num2 = entries.Count((Entry e) => e.Category == category);
				if (num2 > 0)
				{
					say.AddString($"   {Categories.Titles[category]} {num2}");
				}
			}
			List<string> unclassified = Session.Unclassified;
			if (unclassified.Count > 0)
			{
				say.AddString("   no category yet for " + string.Join(", ", unclassified.Take(10)) + ((unclassified.Count > 10) ? (" and " + (unclassified.Count - 10) + " more") : ""));
			}
		}

		private static List<KindData> Matches(string query)
		{
			return Search.Match(Session.Kinds.Values, query);
		}

		private static void Types(Terminal say, string query)
		{
			List<KindData> list = Matches(query);
			if (list.Count == 0)
			{
				say.AddString("Navigator: no type matches '" + query + "'.");
				return;
			}
			foreach (KindData item in list.Take(40))
			{
				say.AddString($"   {Categories.Titles[item.Category]}: {item.Kind}, {item.Done} of {item.Items.Count} done");
			}
			if (list.Count > 40)
			{
				say.AddString($"   {list.Count - 40} more, add words to narrow it down");
			}
		}

		private static KindData One(Terminal say, string query)
		{
			if (string.IsNullOrWhiteSpace(query))
			{
				say.AddString("navigator count | types [text] | nearest <type> | done | undo | reset <type> | sync | importlegacy");
				return null;
			}
			List<KindData> list = Matches(query);
			if (list.Count == 0)
			{
				say.AddString("Navigator: no type matches '" + query + "'. Try navigator types.");
				return null;
			}
			if (list.Count > 1)
			{
				say.AddString("Navigator: '" + query + "' matches " + list.Count + " types, add words or use one of");
				foreach (KindData item in list.Take(8))
				{
					say.AddString("   " + item.Kind);
				}
				return null;
			}
			return list[0];
		}

		private static void Pick(Terminal say, string query)
		{
			KindData kindData = One(say, query);
			if (kindData != null)
			{
				Show(say, kindData);
			}
		}

		private static void Show(Terminal say, KindData kind)
		{
			if (!Session.PlayerPosition().HasValue)
			{
				say.AddString("Valheim Navigator needs your character in the world.");
				return;
			}
			Found found = Session.Find(kind);
			if (found == null)
			{
				say.AddString("Navigator: every " + kind.Kind + " is done, navigator reset " + kind.Kind + " starts again.");
				return;
			}
			Entry entry = found.Entry;
			string text = (entry.Possible ? $", possible site, 1 of {entry.Candidates}" : "");
			say.AddString($"Navigator: nearest {kind.Kind} not done is {found.Distance:F0} m {found.Direction}, x {entry.X:F0} z {entry.Z:F0}, {Categories.Split(entry.Biome)}{text}. {found.Left} left, {found.Done} done.");
			if (Plugin.LogCounters.Value)
			{
				Plugin.Log.LogInfo((object)$"nearest {kind.Kind} {found.Distance:F0} m {found.Direction} at {entry.X:F0} {entry.Z:F0}, {found.Left} left, {found.Done} done");
			}
		}

		private static void Done(Terminal say)
		{
			Found last = Session.Last;
			if (last == null)
			{
				say.AddString("Navigator: run navigator nearest <type> first.");
				return;
			}
			Entry entry = last.Entry;
			say.AddString($"Navigator: {entry.Kind} at x {entry.X:F0} z {entry.Z:F0} marked done.");
			Session.MarkDoneAndNext();
			Show(say, entry.Group);
		}

		private static void Undo(Terminal say)
		{
			Entry lastDone = Session.LastDone;
			if (!Session.Undo())
			{
				say.AddString("Navigator: nothing to undo in this session.");
			}
			else
			{
				say.AddString($"Navigator: {lastDone.Kind} at x {lastDone.X:F0} z {lastDone.Z:F0} is not done again.");
			}
		}

		private static void Reset(Terminal say, string query)
		{
			KindData kindData = One(say, query);
			if (kindData != null)
			{
				int num = Session.Reset(kindData);
				say.AddString($"Navigator: {kindData.Kind} done list cleared, {num} locations.");
			}
		}
	}
	internal static class CursorReadout
	{
		private static TMP_Text _text;

		private static float _next;

		internal static void Frame(Minimap map, RectTransform rect, Camera camera)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: 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_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b3: Expected O, but got Unknown
			//IL_01c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c4: Expected O, but got Unknown
			//IL_00c4: 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_00d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f1: Expected O, but got Unknown
			//IL_00f2: 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_0116: Unknown result type (might be due to invalid IL or missing references)
			//IL_014a: Unknown result type (might be due to invalid IL or missing references)
			//IL_01db: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0212: Unknown result type (might be due to invalid IL or missing references)
			//IL_0217: Unknown result type (might be due to invalid IL or missing references)
			//IL_021f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0226: Unknown result type (might be due to invalid IL or missing references)
			//IL_022b: 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_023b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0257: Unknown result type (might be due to invalid IL or missing references)
			//IL_025e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0263: Unknown result type (might be due to invalid IL or missing references)
			//IL_026e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0273: Unknown result type (might be due to invalid IL or missing references)
			//IL_0309: Unknown result type (might be due to invalid IL or missing references)
			//IL_0310: Unknown result type (might be due to invalid IL or missing references)
			//IL_0318: Unknown result type (might be due to invalid IL or missing references)
			//IL_031f: Unknown result type (might be due to invalid IL or missing references)
			//IL_035e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0422: Unknown result type (might be due to invalid IL or missing references)
			//IL_0429: Unknown result type (might be due to invalid IL or missing references)
			//IL_03b5: Unknown result type (might be due to invalid IL or missing references)
			Vector2 zero = Vector2.zero;
			if (Settings.CursorTerrain.Value && !Panel.PointerOver && RectTransformUtility.ScreenPointToLocalPointInRectangle(rect, Vector2.op_Implicit(Input.mousePosition), camera, ref zero))
			{
				Rect rect2 = rect.rect;
				if (((Rect)(ref rect2)).Contains(zero))
				{
					if ((Object)(object)_text == (Object)null)
					{
						GameObject val = new GameObject("ValheimNavigator_Cursor", new Type[3]
						{
							typeof(RectTransform),
							typeof(CanvasRenderer),
							typeof(Image)
						});
						RectTransform val2 = (RectTransform)map.m_largeRoot.transform;
						rect2 = val2.rect;
						Ui.Place(val, (Transform)(object)val2, 14f, ((Rect)(ref rect2)).height - 80f, 640f, 30f);
						((Graphic)val.GetComponent<Image>()).color = new Color(0.06f, 0.05f, 0.04f, 0.72f);
						((Graphic)val.GetComponent<Image>()).raycastTarget = false;
						_text = Ui.Label(val.transform, "Readout", 8f, 0f, 624f, 30f, 16f, (TextAlignmentOptions)4097, Ui.Ink);
						if (Plugin.LogCounters.Value)
						{
							Plugin.Log.LogInfo((object)"map cursor terrain readout created");
						}
					}
					((Component)_text.transform.parent).gameObject.SetActive(true);
					((Component)_text).gameObject.SetActive(true);
					RectTransform val3 = (RectTransform)_text.transform.parent;
					float num = 14f + Panel.RightEdge;
					if (Mathf.Abs(val3.anchoredPosition.x - num) > 0.5f)
					{
						val3.anchoredPosition = new Vector2(num, val3.anchoredPosition.y);
					}
					if (Time.unscaledTime < _next)
					{
						return;
					}
					_next = Time.unscaledTime + 0.1f;
					Rect uvRect = map.m_mapImageLarge.uvRect;
					float x = ((Rect)(ref uvRect)).x;
					float x2 = zero.x;
					rect2 = rect.rect;
					float num2 = x2 - ((Rect)(ref rect2)).xMin;
					rect2 = rect.rect;
					float num3 = x + num2 / ((Rect)(ref rect2)).width * ((Rect)(ref uvRect)).width;
					float y = ((Rect)(ref uvRect)).y;
					float y2 = zero.y;
					rect2 = rect.rect;
					float num4 = y2 - ((Rect)(ref rect2)).yMin;
					rect2 = rect.rect;
					float num5 = y + num4 / ((Rect)(ref rect2)).height * ((Rect)(ref uvRect)).height;
					float num6 = (float)map.m_textureSize * map.m_pixelSize;
					Vector3 val4 = default(Vector3);
					((Vector3)(ref val4))..ctor((num3 - 0.5f) * num6, 0f, (num5 - 0.5f) * num6);
					string text = "x " + val4.x.ToString("F0") + ", z " + val4.z.ToString("F0");
					Texture2D val5 = MapAccess.Height.Invoke(map);
					Texture2D val6 = MapAccess.Fog.Invoke(map);
					if (val4.x * val4.x + val4.z * val4.z > 110250000f)
					{
						text += "    Outside the world";
					}
					else if (!Settings.ShowUnexplored.Value && (Object)(object)val6 != (Object)null && val6.GetPixelBilinear(num3, num5).r > 0.5f)
					{
						text += "    Unexplored";
					}
					else if ((Object)(object)val5 != (Object)null)
					{
						float num7 = (((Object)(object)ZoneSystem.instance != (Object)null) ? ZoneSystem.instance.m_waterLevel : 30f);
						float r = val5.GetPixelBilinear(num3, num5).r;
						text += ((r >= num7) ? ("    Ground " + (r - num7).ToString("F1") + " m above sea") : ("    Water depth " + (num7 - r).ToString("F1") + " m"));
					}
					if (Settings.SpawnSafe.Value)
					{
						int num8 = SpawnAreas.StateAt(val4.x, val4.z);
						if (num8 > 0)
						{
							text = text + "    " + SpawnAreas.Describe(num8);
						}
					}
					_text.text = text;
					return;
				}
			}
			if ((Object)(object)_text != (Object)null)
			{
				((Component)_text.transform.parent).gameObject.SetActive(false);
			}
		}
	}
	internal static class DetailOverlay
	{
		private sealed class Job
		{
			internal Rect Uv;

			internal Vector2 Size;

			internal string Key;

			internal int W;

			internal int H;

			internal int View;

			internal float Interval;

			internal float Mpp;

			internal float Pixel;

			internal float Water;

			internal float Danger;

			internal float Warning;

			internal float MaxSlope;

			internal bool Contours;

			internal bool Mist;

			internal bool Steep;

			internal bool Shallows;

			internal bool Heat;

			internal bool Fogged;

			internal int[] Cols;

			internal float[] ColWeights;

			internal float[] Wx;

			internal float[] Fx;

			internal Color32[] Pixels;

			internal volatile bool Done;

			internal volatile bool Cancel;

			internal double Milliseconds;
		}

		private const int MaxPixels = 600000;

		private const float SettleSeconds = 0.15f;

		private const float MinorWidth = 0.9f;

		private const float MajorWidth = 1.4f;

		private const float MinorAlpha = 0.55f;

		private const float MajorAlpha = 0.8f;

		private const float LightLength = 1.9899749f;

		private const float FlatDot = 0.70352644f;

		private static readonly Color ContourColor = new Color(0.13f, 0.1f, 0.07f, 1f);

		private static readonly Color32[] GameBiome = (Color32[])(object)new Color32[7]
		{
			new Color32((byte)146, (byte)167, (byte)92, byte.MaxValue),
			new Color32((byte)107, (byte)116, (byte)63, byte.MaxValue),
			new Color32((byte)163, (byte)114, (byte)88, byte.MaxValue),
			new Color32((byte)231, (byte)171, (byte)120, byte.MaxValue),
			new Color32((byte)51, (byte)51, (byte)51, byte.MaxValue),
			new Color32((byte)123, (byte)32, (byte)32, byte.MaxValue),
			new Color32(byte.MaxValue, byte.MaxValue, byte.MaxValue, byte.MaxValue)
		};

		private static readonly Color[] Land = (Color[])(object)new Color[8]
		{
			C(138, 160, 84),
			C(84, 99, 55),
			C(118, 96, 70),
			C(214, 176, 112),
			C(88, 84, 100),
			C(100, 42, 36),
			C(226, 230, 234),
			C(150, 150, 150)
		};

		private static readonly Color[] Flat = (Color[])(object)new Color[8]
		{
			C(146, 167, 92),
			C(92, 104, 56),
			C(163, 114, 88),
			C(231, 171, 120),
			C(78, 76, 88),
			C(123, 32, 32),
			C(236, 238, 240),
			C(150, 150, 150)
		};

		private static readonly Color North = C(212, 226, 240);

		private static readonly Color Sand = C(206, 192, 146);

		private static readonly Color Lava = C(255, 122, 44);

		private static RectTransform _clip;

		private static RawImage _image;

		private static Texture2D _texture;

		private static Texture2D _cacheSource;

		private static float[] _height;

		private static byte[] _biome;

		private static Color32[] _mask;

		private static int _side;

		private static int _cacheRow;

		private static bool _cached;

		private static Rect _shownUv;

		private static Rect _lastUv;

		private static Vector2 _shownSize;

		private static Vector2 _lastSize;

		private static string _shownKey;

		private static bool _dirty = true;

		private static bool _warned;

		private static float _moved;

		private static float _interval = 20f;

		internal static bool Covering;

		internal static int Renders;

		private static Job _job;

		private const int RecentViews = 6;

		private static readonly List<Job> Recent = new List<Job>();

		private static byte[] _fog;

		private static int _fogRow;

		private static bool _fogFull;

		private static Texture2D _fogSource;

		private static int _fogFrame = -1;

		private static bool _fogFormatNoted;

		internal static float Interval => _interval;

		internal static bool DataReady => _cached;

		internal static float[] HeightData => _height;

		internal static byte[] BiomeData => _biome;

		internal static Color32[] MaskData => _mask;

		internal static int DataSide => _side;

		internal static byte[] FogData => _fog;

		internal static bool FogReady => _fogFull;

		private static Color C(int r, int g, int b)
		{
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			return new Color((float)r / 255f, (float)g / 255f, (float)b / 255f, 1f);
		}

		internal static void Dirty()
		{
			_dirty = true;
			Recent.Clear();
		}

		private static void Hide()
		{
			Covering = false;
			if ((Object)(object)_clip != (Object)null && ((Component)_clip).gameObject.activeSelf)
			{
				((Component)_clip).gameObject.SetActive(false);
			}
		}

		internal static void Frame(Minimap map)
		{
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: 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_00a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: 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_00bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_00af: 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_00fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0107: Unknown result type (might be due to invalid IL or missing references)
			//IL_0108: Unknown result type (might be due to invalid IL or missing references)
			//IL_0140: Unknown result type (might be due to invalid IL or missing references)
			//IL_0141: 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_0160: Unknown result type (might be due to invalid IL or missing references)
			//IL_016d: Unknown result type (might be due to invalid IL or missing references)
			//IL_017b: Unknown result type (might be due to invalid IL or missing references)
			//IL_017c: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				RawImage val = (((Object)(object)map != (Object)null) ? map.m_mapImageLarge : null);
				if ((Object)(object)val == (Object)null || !Settings.Detail.Value || !TerrainOverlay.Active)
				{
					Hide();
					return;
				}
				if ((Object)(object)_clip == (Object)null)
				{
					Create(val);
				}
				if (!((Component)_clip).gameObject.activeSelf)
				{
					((Component)_clip).gameObject.SetActive(true);
				}
				Rect uvRect = val.uvRect;
				if (!EnsureCache(map))
				{
					Place(uvRect);
					return;
				}
				Rect rect = _clip.rect;
				Vector2 size = ((Rect)(ref rect)).size;
				string text = TerrainOverlay.Key();
				if (uvRect != _lastUv || size != _lastSize)
				{
					_lastUv = uvRect;
					_lastSize = size;
					_moved = Time.unscaledTime;
				}
				bool num = Time.unscaledTime - _moved >= 0.15f;
				bool flag = _dirty || text != _shownKey || uvRect != _shownUv || size != _shownSize;
				if (!Settings.ShowUnexplored.Value)
				{
					RefreshFog(MapAccess.Fog.Invoke(map));
				}
				bool flag2 = _job != null && Matches(_job, uvRect, size, text) && !_dirty;
				if (num && flag && !flag2 && size.x >= 8f && size.y >= 8f)
				{
					Start(map, uvRect, size, text);
				}
				Collect();
				Place(uvRect);
			}
			catch (Exception ex)
			{
				Covering = false;
				if (!_warned)
				{
					Plugin.Log.LogWarning((object)("detailed map view unavailable: " + ex));
				}
				_warned = true;
			}
		}

		private static void Create(RawImage image)
		{
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Expected O, but got Unknown
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Expected O, but got Unknown
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cf: 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_00dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00de: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e4: 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)
			GameObject val = new GameObject("ValheimNavigator_Detail", new Type[2]
			{
				typeof(RectTransform),
				typeof(RectMask2D)
			});
			val.transform.SetParent(((Component)image).transform, false);
			_clip = (RectTransform)val.transform;
			Ui.Stretch(_clip, 0f);
			val.transform.SetSiblingIndex(Math.Min(1, ((Component)image).transform.childCount - 1));
			GameObject val2 = new GameObject("Picture", new Type[3]
			{
				typeof(RectTransform),
				typeof(CanvasRenderer),
				typeof(RawImage)
			});
			val2.transform.SetParent(val.transform, false);
			RectTransform val3 = (RectTransform)val2.transform;
			Vector2 val4 = (val3.pivot = Vector2.zero);
			Vector2 anchorMin = (val3.anchorMax = val4);
			val3.anchorMin = anchorMin;
			_image = val2.GetComponent<RawImage>();
			((Graphic)_image).raycastTarget = false;
			((Behaviour)_image).enabled = false;
			if ((Object)(object)_texture != (Object)null)
			{
				_image.texture = (Texture)(object)_texture;
			}
			else
			{
				_shownUv = default(Rect);
			}
			if (Plugin.LogCounters.Value)
			{
				Plugin.Log.LogInfo((object)"detailed map view layer built");
			}
		}

		private static void Place(Rect uv)
		{
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f6: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_texture == (Object)null || ((Rect)(ref _shownUv)).width <= 0f || ((Rect)(ref uv)).width <= 0f || ((Rect)(ref uv)).height <= 0f)
			{
				Covering = false;
				if ((Object)(object)_image != (Object)null)
				{
					((Behaviour)_image).enabled = false;
				}
			}
			else
			{
				Rect rect = _clip.rect;
				RectTransform val = (RectTransform)((Component)_image).transform;
				val.anchoredPosition = new Vector2((((Rect)(ref _shownUv)).x - ((Rect)(ref uv)).x) / ((Rect)(ref uv)).width * ((Rect)(ref rect)).width, (((Rect)(ref _shownUv)).y - ((Rect)(ref uv)).y) / ((Rect)(ref uv)).height * ((Rect)(ref rect)).height);
				val.sizeDelta = new Vector2(((Rect)(ref _shownUv)).width / ((Rect)(ref uv)).width * ((Rect)(ref rect)).width, ((Rect)(ref _shownUv)).height / ((Rect)(ref uv)).height * ((Rect)(ref rect)).height);
				((Behaviour)_image).enabled = true;
				float num = ((Rect)(ref uv)).width * 0.002f;
				Covering = _shownKey == TerrainOverlay.Key() && ((Rect)(ref _shownUv)).xMin <= ((Rect)(ref uv)).xMin + num && ((Rect)(ref _shownUv)).yMin <= ((Rect)(ref uv)).yMin + num && ((Rect)(ref _shownUv)).xMax >= ((Rect)(ref uv)).xMax - num && ((Rect)(ref _shownUv)).yMax >= ((Rect)(ref uv)).yMax - num;
			}
		}

		private static int Classify(Color32 c)
		{
			//IL_0004: 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_0042: Unknown result type (might be due to invalid IL or missing references)
			for (int i = 0; i < GameBiome.Length; i++)
			{
				if (Math.Abs(c.r - GameBiome[i].r) < 4 && Math.Abs(c.g - GameBiome[i].g) < 4 && Math.Abs(c.b - GameBiome[i].b) < 4)
				{
					return i;
				}
			}
			return 7;
		}

		internal static bool EnsureCache(Minimap map)
		{
			//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b9: Invalid comparison between Unknown and I4
			//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_010e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0102: Unknown result type (might be due to invalid IL or missing references)
			//IL_0108: Unknown result type (might be due to invalid IL or missing references)
			//IL_0113: Unknown result type (might be due to invalid IL or missing references)
			//IL_0130: Unknown result type (might be due to invalid IL or missing references)
			//IL_0170: 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_016a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0175: Unknown result type (might be due to invalid IL or missing references)
			//IL_0242: Unknown result type (might be due to invalid IL or missing references)
			//IL_0293: Unknown result type (might be due to invalid IL or missing references)
			//IL_0298: Unknown result type (might be due to invalid IL or missing references)
			//IL_0365: Unknown result type (might be due to invalid IL or missing references)
			//IL_036a: Unknown result type (might be due to invalid IL or missing references)
			//IL_036f: Unknown result type (might be due to invalid IL or missing references)
			//IL_030d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0312: Unknown result type (might be due to invalid IL or missing references)
			Texture2D val = MapAccess.Height.Invoke(map);
			Texture2D mapTexture = map.m_mapTexture;
			if ((Object)(object)val == (Object)null || (Object)(object)mapTexture == (Object)null)
			{
				return false;
			}
			if ((Object)(object)val != (Object)(object)_cacheSource)
			{
				_cacheSource = val;
				_side = ((Texture)val).width;
				_cacheRow = 0;
				_cached = false;
				int num = _side * _side;
				if (_height == null || _height.Length != num)
				{
					_height = new float[num];
					_biome = new byte[num];
					_mask = (Color32[])(object)new Color32[num];
				}
			}
			if (_cached)
			{
				return true;
			}
			Texture2D val2 = MapAccess.Mask.Invoke(map);
			Stopwatch stopwatch = Stopwatch.StartNew();
			bool flag = (int)val.format == 18;
			NativeArray<float> val3 = (flag ? val.GetPixelData<float>(0) : default(NativeArray<float>));
			int r;
			int g;
			int b;
			int a;
			int num2 = Layout(mapTexture.format, out r, out g, out b, out a);
			if (((Texture)mapTexture).width != _side)
			{
				num2 = 0;
			}
			NativeArray<byte> val4 = ((num2 > 0) ? mapTexture.GetPixelData<byte>(0) : default(NativeArray<byte>));
			int r2;
			int g2;
			int b2;
			int a2;
			int num3 = (((Object)(object)val2 != (Object)null) ? Layout(val2.format, out r2, out g2, out b2, out a2) : Layout((TextureFormat)18, out r2, out g2, out b2, out a2));
			if ((Object)(object)val2 == (Object)null || ((Texture)val2).width != _side)
			{
				num3 = 0;
			}
			NativeArray<byte> val5 = ((num3 > 0) ? val2.GetPixelData<byte>(0) : default(NativeArray<byte>));
			while (_cacheRow < _side && stopwatch.Elapsed.TotalMilliseconds < (double)Settings.DetailMilliseconds.Value)
			{
				int num4 = _cacheRow * _side;
				if (flag)
				{
					for (int i = 0; i < _side; i++)
					{
						_height[num4 + i] = val3[num4 + i];
					}
				}
				else
				{
					Color[] pixels = val.GetPixels(0, _cacheRow, _side, 1);
					for (int j = 0; j < _side; j++)
					{
						_height[num4 + j] = pixels[j].r;
					}
				}
				if (num2 > 0)
				{
					for (int k = 0; k < _side; k++)
					{
						int num5 = (num4 + k) * num2;
						_biome[num4 + k] = (byte)Classify(new Color32(val4[num5 + r], val4[num5 + g], val4[num5 + b], byte.MaxValue));
					}
				}
				else if (((Texture)mapTexture).width == _side)
				{
					Color[] pixels2 = mapTexture.GetPixels(0, _cacheRow, _side, 1);
					for (int l = 0; l < _side; l++)
					{
						_biome[num4 + l] = (byte)Classify(Color32.op_Implicit(pixels2[l]));
					}
				}
				if (num3 > 0)
				{
					for (int m = 0; m < _side; m++)
					{
						int num6 = (num4 + m) * num3;
						_mask[num4 + m] = new Color32(val5[num6 + r2], val5[num6 + g2], val5[num6 + b2], (a2 >= 0) ? val5[num6 + a2] : byte.MaxValue);
					}
				}
				else if ((Object)(object)val2 != (Object)null && ((Texture)val2).width == _side)
				{
					Color[] pixels3 = val2.GetPixels(0, _cacheRow, _side, 1);
					for (int n = 0; n < _side; n++)
					{
						_mask[num4 + n] = Color32.op_Implicit(pixels3[n]);
					}
				}
				_cacheRow++;
			}
			if (_cacheRow < _side)
			{
				return false;
			}
			_cached = true;
			if (Plugin.LogCounters.Value)
			{
				Plugin.Log.LogInfo((object)("detailed map view data ready, " + _side + " px"));
			}
			return true;
		}

		private static void SplineWeights(float t, float[] o, int at)
		{
			float num = t * t;
			float num2 = num * t;
			float num3 = 1f - t;
			o[at] = 0.5f * (2f * num - t - num2);
			o[at + 1] = 0.5f * (2f - 5f * num + 3f * num2);
			o[at + 2] = 0.5f * (t + 4f * num - 3f * num2);
			o[at + 3] = 0.5f * (num2 - num);
			o[at + 4] = num3 * num3 * num3 / 6f;
			o[at + 5] = (3f * num2 - 6f * num + 4f) / 6f;
			o[at + 6] = (3f * num + 3f * t + 1f - 3f * num2) / 6f;
			o[at + 7] = num2 / 6f;
			o[at + 8] = -0.5f * num3 * num3;
			o[at + 9] = 0.5f * (3f * num - 4f * t);
			o[at + 10] = 0.5f * (1f + 2f * t - 3f * num);
			o[at + 11] = 0.5f * num;
		}

		private static int Clamp(int i)
		{
			if (i >= 0)
			{
				if (i < _side)
				{
					return i;
				}
				return _side - 1;
			}
			return 0;
		}

		internal static Color BiomeColor(int index)
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			if (index >= GameBiome.Length)
			{
				return new Color(0.2f, 0.35f, 0.45f, 1f);
			}
			return Color32.op_Implicit(GameBiome[index]);
		}

		internal static void RefreshFog(Texture2D fog)
		{
			//IL_00a2: 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_00bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c1: 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_00e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
			if (_fogFrame == Time.frameCount)
			{
				return;
			}
			_fogFrame = Time.frameCount;
			if ((Object)(object)fog == (Object)null || ((Texture)fog).width != _side || ((Texture)fog).height != _side)
			{
				_fogFull = false;
				return;
			}
			if (_fog == null || _fog.Length != _side * _side || (Object)(object)fog != (Object)(object)_fogSource)
			{
				_fog = new byte[_side * _side];
				_fogRow = 0;
				_fogFull = false;
				_fogSource = fog;
			}
			int num = (_fogFull ? 32 : 256);
			int r;
			int g;
			int b;
			int a;
			int num2 = Layout(fog.format, out r, out g, out b, out a);
			NativeArray<byte> val = ((num2 > 0) ? fog.GetPixelData<byte>(0) : default(NativeArray<byte>));
			if (num2 == 0 && !_fogFormatNoted)
			{
				_fogFormatNoted = true;
				Plugin.Log.LogWarning((object)("fog texture format " + ((object)fog.format/*cast due to .constrained prefix*/).ToString() + " is read row by row, which makes garbage"));
			}
			for (int i = 0; i < num; i++)
			{
				int num3 = _fogRow * _side;
				if (num2 > 0)
				{
					for (int j = 0; j < _side; j++)
					{
						_fog[num3 + j] = ((val[(num3 + j) * num2 + r] > 127) ? ((byte)1) : ((byte)0));
					}
				}
				else
				{
					Color[] pixels = fog.GetPixels(0, _fogRow, _side, 1);
					for (int k = 0; k < _side; k++)
					{
						_fog[num3 + k] = ((pixels[k].r > 0.5f) ? ((byte)1) : ((byte)0));
					}
				}
				if (++_fogRow >= _side)
				{
					_fogRow = 0;
					_fogFull = true;
				}
			}
		}

		internal static int Layout(TextureFormat format, out int r, out int g, out int b, out int a)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0003: Invalid comparison between Unknown and I4
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Invalid comparison between Unknown and I4
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Expected I4, but got Unknown
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Invalid comparison between Unknown and I4
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Invalid comparison between Unknown and I4
			if ((int)format <= 14)
			{
				switch (format - 3)
				{
				case 1:
					r = 0;
					g = 1;
					b = 2;
					a = 3;
					return 4;
				case 2:
					a = 0;
					r = 1;
					g = 2;
					b = 3;
					return 4;
				case 0:
					r = 0;
					g = 1;
					b = 2;
					a = -1;
					return 3;
				}
				if ((int)format == 14)
				{
					b = 0;
					g = 1;
					r = 2;
					a = 3;
					return 4;
				}
			}
			else
			{
				if ((int)format == 62)
				{
					r = 0;
					g = 1;
					b = 1;
					a = -1;
					return 2;
				}
				if ((int)format == 63)
				{
					r = (g = (b = 0));
					a = -1;
					return 1;
				}
			}
			r = (g = (b = 0));
			a = -1;
			return 0;
		}

		private static bool Matches(Job job, Rect uv, Vector2 size, string key)
		{
			//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_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			if (job.Uv == uv && job.Size == size)
			{
				return job.Key == key;
			}
			return false;
		}

		private static void Start(Minimap map, Rect uv, Vector2 size, string key)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			Job job = Recent.Find((Job j) => Matches(j, uv, size, key));
			if (job != null)
			{
				Show(job);
				_dirty = false;
			}
			else
			{
				if (!Settings.ShowUnexplored.Value && !_fogFull)
				{
					return;
				}
				if (_job != null)
				{
					_job.Cancel = true;
				}
				float num = Mathf.Min(1f, Mathf.Sqrt(600000f / (size.x * size.y)));
				Job job2 = new Job
				{
					Uv = uv,
					Size = size,
					Key = key,
					W = Math.Max(8, Mathf.RoundToInt(size.x * num)),
					H = Math.Max(8, Mathf.RoundToInt(size.y * num))
				};
				job2.Pixels = (Color32[])(object)new Color32[job2.W * job2.H];
				job2.Cols = new int[job2.W * 4];
				job2.ColWeights = new float[job2.W * 12];
				job2.Wx = new float[job2.W];
				job2.Fx = new float[job2.W];
				job2.Pixel = map.m_pixelSize;
				for (int num2 = 0; num2 < job2.W; num2++)
				{
					float num3 = (((Rect)(ref uv)).x + ((float)num2 + 0.5f) / (float)job2.W * ((Rect)(ref uv)).width) * (float)_side;
					int num4 = Mathf.FloorToInt(num3);
					for (int num5 = 0; num5 < 4; num5++)
					{
						job2.Cols[num2 * 4 + num5] = Clamp(num4 - 1 + num5);
					}
					SplineWeights(num3 - (float)num4, job2.ColWeights, num2 * 12);
					job2.Wx[num2] = (num3 - (float)_side / 2f) * job2.Pixel;
					job2.Fx[num2] = num3 - (float)num4;
				}
				job2.Mpp = ((Rect)(ref uv)).width * (float)_side * job2.Pixel / (float)job2.W;
				float num6 = size.x / (((Rect)(ref uv)).width * (float)_side * job2.Pixel);
				float value = Settings.ContourMeters.Value;
				job2.Interval = ((!Settings.ContourAuto.Value) ? value : ((num6 >= 0.9f) ? Math.Min(value, 5f) : ((num6 >= 0.35f) ? Math.Min(value, 10f) : value)));
				job2.Water = (((Object)(object)ZoneSystem.instance != (Object)null) ? ZoneSystem.instance.m_waterLevel : 30f);
				job2.View = Settings.TerrainView.Value;
				job2.Contours = Settings.Contours.Value;
				job2.Mist = Settings.Mist.Value;
				job2.Steep = Settings.Steepness.Value;
				job2.Shallows = Settings.Shallows.Value;
				job2.Heat = Settings.AshlandsHeat.Value;
				job2.Fogged = !Settings.ShowUnexplored.Value;
				job2.Danger = Settings.BoatDangerDepth.Value;
				job2.Warning = Math.Max(Settings.BoatWarningDepth.Value, job2.Danger);
				job2.MaxSlope = Settings.MaxSlope.Value;
				_job = job2;
				_dirty = false;
				ThreadPool.QueueUserWorkItem(Run, job2);
			}
		}

		private static void Run(object state)
		{
			Job job = (Job)state;
			Stopwatch stopwatch = Stopwatch.StartNew();
			try
			{
				float[] rowWeights = new float[12];
				int[] rowOffsets = new int[4];
				for (int i = 0; i < job.H; i++)
				{
					if (job.Cancel)
					{
						break;
					}
					Row(job, i, rowWeights, rowOffsets);
				}
			}
			catch (Exception ex)
			{
				job.Cancel = true;
				if (!_warned)
				{
					Plugin.Log.LogWarning((object)("detailed map view row failed: " + ex.Message));
				}
				_warned = true;
			}
			job.Milliseconds = stopwatch.Elapsed.TotalMilliseconds;
			job.Done = true;
		}

		private static void Collect()
		{
			Job job = _job;
			if (job == null || !job.Done)
			{
				return;
			}
			_job = null;
			if (!job.Cancel)
			{
				Show(job);
				Recent.Insert(0, job);
				if (Recent.Count > 6)
				{
					Recent.RemoveAt(Recent.Count - 1);
				}
				Renders++;
				if (Plugin.LogCounters.Value && (Renders <= 3 || Renders % 20 == 0))
				{
					Plugin.Log.LogInfo((object)("detailed map view drawn, " + job.W + "x" + job.H + ", " + job.Milliseconds.ToString("F0") + " ms on a background thread, render " + Renders));
				}
			}
		}

		private static void Show(Job job)
		{
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Expected O, but got Unknown
			//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_00a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_texture == (Object)null || ((Texture)_texture).width != job.W || ((Texture)_texture).height != job.H)
			{
				if ((Object)(object)_texture != (Object)null)
				{
					Object.Destroy((Object)(object)_texture);
				}
				_texture = new Texture2D(job.W, job.H, (TextureFormat)4, false)
				{
					wrapMode = (TextureWrapMode)1,
					filterMode = (FilterMode)1
				};
			}
			_texture.SetPixels32(job.Pixels);
			_texture.Apply(false);
			_image.texture = (Texture)(object)_texture;
			_shownUv = job.Uv;
			_shownSize = job.Size;
			_shownKey = job.Key;
			_interval = job.Interval;
		}

		private static float Shade(float gx, float gz, float ex)
		{
			gx *= ex;
			gz *= ex;
			return Mathf.Clamp((gx + 1.4f - gz) / (Mathf.Sqrt(gx * gx + 1f + gz * gz) * 1.9899749f) / 0.70352644f, 0.42f, 1.32f);
		}

		private static Color Mix(Color a, Color b, float t)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: 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_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: 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_0058: Unknown result type (might be due to invalid IL or missing references)
			return new Color(a.r + (b.r - a.r) * t, a.g + (b.g - a.g) * t, a.b + (b.b - a.b) * t, a.a + (b.a - a.a) * t);
		}

		private static void Row(Job job, int py, float[] rowWeights, int[] rowOffsets)
		{
			//IL_014d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0314: Unknown result type (might be due to invalid IL or missing references)
			//IL_0319: Unknown result type (might be due to invalid IL or missing references)
			//IL_032b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0330: Unknown result type (might be due to invalid IL or missing references)
			//IL_0342: Unknown result type (might be due to invalid IL or missing references)
			//IL_0347: Unknown result type (might be due to invalid IL or missing references)
			//IL_0359: Unknown result type (might be due to invalid IL or missing references)
			//IL_035e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0360: Unknown result type (might be due to invalid IL or missing references)
			//IL_0371: Unknown result type (might be due to invalid IL or missing references)
			//IL_0386: Unknown result type (might be due to invalid IL or missing references)
			//IL_0397: Unknown result type (might be due to invalid IL or missing references)
			//IL_03af: Unknown result type (might be due to invalid IL or missing references)
			//IL_03b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_08fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_091b: Unknown result type (might be due to invalid IL or missing references)
			//IL_092c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0940: Unknown result type (might be due to invalid IL or missing references)
			//IL_0945: Unknown result type (might be due to invalid IL or missing references)
			//IL_094a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0ae5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0ae7: Unknown result type (might be due to invalid IL or missing references)
			//IL_0aec: Unknown result type (might be due to invalid IL or missing references)
			//IL_0978: Unknown result type (might be due to invalid IL or missing references)
			//IL_0987: Unknown result type (might be due to invalid IL or missing references)
			//IL_0980: Unknown result type (might be due to invalid IL or missing references)
			//IL_0a78: Unknown result type (might be due to invalid IL or missing references)
			//IL_0aa0: Unknown result type (might be due to invalid IL or missing references)
			//IL_0aa5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0aaa: Unknown result type (might be due to invalid IL or missing references)
			//IL_098c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0991: Unknown result type (might be due to invalid IL or missing references)
			//IL_06de: Unknown result type (might be due to invalid IL or missing references)
			//IL_06e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_0a17: Unknown result type (might be due to invalid IL or missing references)
			//IL_0a2a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0a2f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0a34: Unknown result type (might be due to invalid IL or missing references)
			//IL_06f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_06fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_0802: Unknown result type (might be due to invalid IL or missing references)
			//IL_0804: Unknown result type (might be due to invalid IL or missing references)
			//IL_0808: Unknown result type (might be due to invalid IL or missing references)
			//IL_080d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0ab8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0ad1: Unknown result type (might be due to invalid IL or missing references)
			//IL_0ad6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0adb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0a3f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0a52: Unknown result type (might be due to invalid IL or missing references)
			//IL_0a57: Unknown result type (might be due to invalid IL or missing references)
			//IL_0a5c: Unknown result type (might be due to invalid IL or missing references)
			//IL_07fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_08c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_08e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_08ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_08f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_0800: Unknown result type (might be due to invalid IL or missing references)
			//IL_07b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_0768: Unknown result type (might be due to invalid IL or missing references)
			//IL_059f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0589: Unknown result type (might be due to invalid IL or missing references)
			//IL_04fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_04ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_0503: Unknown result type (might be due to invalid IL or missing references)
			//IL_0512: Unknown result type (might be due to invalid IL or missing references)
			//IL_0521: Unknown result type (might be due to invalid IL or missing references)
			//IL_05a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_067d: Unknown result type (might be due to invalid IL or missing references)
			//IL_067f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0696: Unknown result type (might be due to invalid IL or missing references)
			//IL_069b: Unknown result type (might be due to invalid IL or missing references)
			//IL_060e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0610: Unknown result type (might be due to invalid IL or missing references)
			//IL_0631: Unknown result type (might be due to invalid IL or missing references)
			//IL_0636: Unknown result type (might be due to invalid IL or missing references)
			float pixel = job.Pixel;
			float water = job.Water;
			float num = (((Rect)(ref job.Uv)).y + ((float)py + 0.5f) / (float)job.H * ((Rect)(ref job.Uv)).height) * (float)_side;
			int num2 = Mathf.FloorToInt(num);
			int num3 = Clamp(num2);
			int num4 = Clamp(num2 + 1);
			float num5 = num - (float)num2;
			SplineWeights(num5, rowWeights, 0);
			for (int i = 0; i < 4; i++)
			{
				rowOffsets[i] = Clamp(num2 - 1 + i) * _side;
			}
			float num6 = (num - (float)_side / 2f) * pixel;
			float num7 = 110250000f;
			byte[] array = (job.Fogged ? _fog : null);
			int view = job.View;
			float interval = job.Interval;
			float danger = job.Danger;
			float warning = job.Warning;
			float maxSlope = job.MaxSlope;
			int w = job.W;
			int num8 = 0;
			int num9 = py * w;
			Color val6 = default(Color);
			while (num8 < w)
			{
				float num10 = job.Wx[num8];
				int num11 = job.Cols[num8 * 4 + 1];
				int num12 = job.Cols[num8 * 4 + 2];
				if (num10 * num10 + num6 * num6 > num7 || (array != null && array[num3 * _side + num11] != 0))
				{
					job.Pixels[num9] = default(Color32);
				}
				else
				{
					int num13 = num8 * 12;
					float num14 = 0f;
					float num15 = 0f;
					float num16 = 0f;
					for (int j = 0; j < 4; j++)
					{
						int num17 = rowOffsets[j];
						float num18 = _height[num17 + job.Cols[num8 * 4]];
						float num19 = _height[num17 + num11];
						float num20 = _height[num17 + num12];
						float num21 = _height[num17 + job.Cols[num8 * 4 + 3]];
						num14 += rowWeights[j] * (job.ColWeights[num13] * num18 + job.ColWeights[num13 + 1] * num19 + job.ColWeights[num13 + 2] * num20 + job.ColWeights[num13 + 3] * num21);
						num15 += rowWeights[4 + j] * (job.ColWeights[num13 + 8] * num18 + job.ColWeights[num13 + 9] * num19 + job.ColWeights[num13 + 10] * num20 + job.ColWeights[num13 + 11] * num21);
						num16 += rowWeights[8 + j] * (job.ColWeights[num13 + 4] * num18 + job.ColWeights[num13 + 5] * num19 + job.ColWeights[num13 + 6] * num20 + job.ColWeights[num13 + 7] * num21);
					}
					float num22 = num15 / pixel;
					float num23 = num16 / pixel;
					float num24 = Mathf.Sqrt(num22 * num22 + num23 * num23);
					float num25 = Mathf.Max(0.001f, num24 * job.Mpp);
					float num26 = num14 - water;
					float num27 = water - num14;
					int num28 = num3 * _side + num11;
					int num29 = _biome[num28];
					float num30 = job.Fx[num8];
					Color32 val = _mask[num28];
					Color32 val2 = _mask[num3 * _side + num12];
					Color32 val3 = _mask[num4 * _side + num11];
					Color32 val4 = _mask[num4 * _side + num12];
					float num31 = (((float)(int)val.b * (1f - num30) + (float)(int)val2.b * num30) * (1f - num5) + ((float)(int)val3.b * (1f - num30) + (float)(int)val4.b * num30) * num5) / 255f;
					Color val5 = Color.clear;
					if (view != 0)
					{
						bool flag = num29 == 6 && num10 * num10 + (num6 + 4000f) * (num6 + 4000f) > 144000000f;
						float t = Mathf.Clamp01((num14 - ((num29 == 2) ? (water - 2.2f) : water)) / num25 + 0.5f);
						if (view == 2)
						{
							float num32 = (58f + 197f * Mathf.Pow(Mathf.Clamp01(num26 / 220f), 0.8f)) / 255f * Shade(num22, num23, 2.2f);
							((Color)(ref val6))..ctor(num32 * 0.97f, num32, num32 * 0.95f, 1f);
						}
						else
						{
							Color[] array2 = ((view == 1) ? Flat : Land);
							float num33 = 0f;
							float num34 = 0f;
							float num35 = 0f;
							float num36 = 0f;
							for (int k = 0; k < 4; k++)
							{
								int num37 = rowOffsets[k];
								for (int l = 0; l < 4; l++)
								{
									int num38 = num37 + job.Cols[num8 * 4 + l];
									float num39 = rowWeights[4 + k] * job.ColWeights[num13 + 4 + l];
									int num40 = _biome[num38];
									if (num40 == 6 && _height[num38] < water)
									{
										num40 = num29;
									}
									Color val7 = array2[num40];
									num33 += val7.r * num39;
									num34 += val7.g * num39;
									num35 += val7.b * num39;
									num36 += (float)(int)_mask[num38].r * num39;
								}
							}
							((Color)(ref val6))..ctor(num33, num34, num35, 1f);
							num36 /= 255f;
							if (flag)
							{
								val6 = ((view == 1) ? C(206, 222, 240) : North);
							}
							if (view == 3)
							{
								if (num29 <= 3 && num36 > 0f)
								{
									float num41 = 1f - 0.2f * num36;
									val6.r *= num41 * 0.94f;
									val6.g *= num41;
									val6.b *= num41 * 0.88f;
								}
								if (num29 != 2 && num29 != 5 && num29 != 6)
								{
									val6 = Mix(val6, Sand, 0.45f * Mathf.Clamp01((1.2f - num26) / num25 + 0.5f));
								}
								float num42 = Shade(num22, num23, 2.6f);
								val6.r *= num42;
								val6.g *= num42;
								val6.b *= num42;
								if (num29 == 5 && num31 > 0f)
								{
									val6 = Mix(val6, Lava, Math.Min(1f, num31 * 1.15f));
								}
							}
						}
						Color a;
						switch (view)
						{
						case 2:
						{
							float num44 = Mathf.Clamp01(num27 / 60f);
							a = C((int)(26f - 14f * num44), (int)(64f - 36f * num44), (int)(104f - 46f * num44));
							break;
						}
						case 1:
							a = C(36, 74, 98);
							break;
						default:
						{
							float num43 = Mathf.Pow(Mathf.Clamp01(num27 / 45f), 0.55f);
							a = (Color)(num29 switch
							{
								5 => new Color((60f - 30f * num43) / 255f, (40f - 22f * num43) / 255f, (44f - 18f * num43) / 255f, 1f), 
								2 => new Color((76f - 22f * num43) / 255f, (84f - 24f * num43) / 255f, (60f - 12f * num43) / 255f, 1f), 
								_ => new Color((78f - 60f * num43) / 255f, (132f - 92f * num43) / 255f, (142f - 80f * num43) / 255f, 1f), 
							});
							break;
						}
						}
						val5 = Mix(a, val6, t);
						val5.a = ((view == 1) ? 0.92f : 1f);
					}
					if (job.Contours && num26 > 0f)
					{
						int num45 = Mathf.RoundToInt(num26 / interval);
						if (num45 > 0)
						{
							bool flag2 = num45 % 5 == 0;
							float num46 = (flag2 ? (5f * interval) : interval) / num25;
							float num47 = Mathf.Clamp01((flag2 ? 1.4f : 0.9f) - Math.Abs(num26 - (float)num45 * interval) / num25) * (flag2 ? 0.8f : 0.55f) * Mathf.Clamp01((num46 - 2.5f) / 3f);
							if (num47 > 0f)
							{
								val5 = TerrainOverlay.Over(val5, new Color(ContourColor.r, ContourColor.g, ContourColor.b, num47));
							}
						}
					}
					if (job.Mist && val.g > 12 && !MistClearings.Clear(num10, num14 + 1.8f, num6))
					{
						val5 = TerrainOverlay.Over(val5, new Color(0.67f, 0.64f, 0.7f, (float)(int)val.g / 255f * 0.46f));
					}
					if (job.Steep && num26 > 0f)
					{
						float num48 = Mathf.Atan(num24) * 57.29578f;
						if (num48 > maxSlope * 0.5f)
						{
							val5 = TerrainOverlay.Over(val5, (num48 > maxSlope) ? TerrainOverlay.SteepColor : TerrainOverlay.SlopeColor);
						}
					}
					if (job.Shallows && num29 != 2 && num27 > 0f - num25 && num27 < warning + num25)
					{
						float num49 = Mathf.Clamp01(num27 / num25 + 0.5f);
						float num50 = Mathf.Clamp01((num27 - danger) / num25 + 0.5f);
						float num51 = Mathf.Clamp01((num27 - warning) / num25 + 0.5f);
						float num52 = (num49 - num50) * 0.65f;
						float num53 = (num50 - num51) * 0.53f;
						if (num52 > 0f)
						{
							val5 = TerrainOverlay.Over(val5, new Color(0.76862746f, 0.20392157f, 8f / 51f, num52));
						}
						if (num53 > 0f)
						{
							val5 = TerrainOverlay.Over(val5, new Color(0.9098039f, 0.80784315f, 24f / 85f, num53));
						}
					}
					if (job.Heat && num31 > 0.1f)
					{
						if (num27 > 0f)
						{
							val5 = TerrainOverlay.Over(val5, new Color(TerrainOverlay.BoilingColor.r, TerrainOverlay.BoilingColor.g, TerrainOverlay.BoilingColor.b, num31 * 0.45f));
						}
						else if (view != 3 && num29 == 5)
						{
							val5 = TerrainOverlay.Over(val5, new Color(0.8f, 0.29f, 0.1f, num31 * 0.62f));
						}
					}
					job.Pixels[num9] = Color32.op_Implicit(val5);
				}
				num8++;
				num9++;
			}
		}
	}
	internal sealed class DoneStore
	{
		private const string Header = "# ValheimNavigator done list, one location per line: prefab, x, z";

		private readonly string _path;

		private readonly HashSet<string> _keys = new HashSet<string>();

		internal int Count => _keys.Count;

		internal DoneStore(string path)
		{
			_path = path;
			if (!File.Exists(path))
			{
				return;
			}
			string[] array = File.ReadAllLines(path);
			foreach (string text in array)
			{
				string[] array2 = text.Split(new char[1] { '\t' });
				if (!text.StartsWith("#") && array2.Length == 3 && int.TryParse(array2[1], out var result) && int.TryParse(array2[2], out result))
				{
					_keys.Add(text);
				}
			}
		}

		internal static string FileFor(string folder, string world)
		{
			string text = new string((world ?? "").Select((char c) => (!Path.GetInvalidFileNameChars().Contains(c)) ? c : '_').ToArray()).Trim();
			if (text.Length == 0)
			{
				text = "world";
			}
			return Path.Combine(folder, text + ".done.txt");
		}

		internal static string FileFor(string folder, string world, long uid)
		{
			if (uid != 0L)
			{
				return Path.Combine(folder, "world_" + uid.ToString("X16") + ".done.txt");
			}
			return FileFor(folder, world);
		}

		internal int ImportLegacy(string legacy, IEnumerable<Entry> entries)
		{
			if (!File.Exists(legacy) || legacy == _path)
			{
				return 0;
			}
			HashSet<string> hashSet = new HashSet<string>(entries.Select((Entry e) => e.Key));
			List<string> list = (from k in File.ReadAllLines(legacy).Where(hashSet.Contains)
				where !_keys.Contains(k)
				select k).ToList();
			if (list.Count == 0)
			{
				return 0;
			}
			if (File.Exists(_path))
			{
				File.Copy(_path, _path + ".bak_" + DateTime.UtcNow.ToString("yyyyMMdd_HHmmss_fff"));
			}
			foreach (string item in list)
			{
				_keys.Add(item);
			}
			Save();
			return list.Count;
		}

		internal bool Contains(Entry entry)
		{
			return _keys.Contains(entry.Key);
		}

		internal bool Mark(Entry entry)
		{
			bool num = _keys.Add(entry.Key);
			if (num)
			{
				Save();
			}
			return num;
		}

		internal bool Unmark(Entry entry)
		{
			bool num = _keys.Remove(entry.Key);
			if (num)
			{
				Save();
			}
			return num;
		}

		internal int Reset(IEnumerable<Entry> entries)
		{
			int num = entries.Count((Entry e) => _keys.Remove(e.Key));
			if (num > 0)
			{
				Save();
			}
			return num;
		}

		private void Save()
		{
			Directory.CreateDirectory(Path.GetDirectoryName(_path));
			File.WriteAllText(_path, "# ValheimNavigator done list, one location per line: prefab, x, z\n" + string.Join("\n", _keys.OrderBy<string, string>((string k) => k, StringComparer.Ordinal)) + "\n", new UTF8Encoding(encoderShouldEmitUTF8Identifier: false));
		}
	}
	internal sealed class Hit
	{
		internal KindData Kind;

		internal PortalInfo Portal;

		internal PinData Pin;

		internal KnownMark Known;

		internal bool Is(Hit other)
		{
			if (other == null)
			{
				return false;
			}
			if (Kind != null)
			{
				return Kind == other.Kind;
			}
			if (Pin != null)
			{
				return Pin == other.Pin;
			}
			if (Known != null)
			{
				return Known == other.Known;
			}
			if (other.Portal != null && Portal.Tag == other.Portal.Tag && Portal.X == other.Portal.X)
			{
				return Portal.Z == other.Portal.Z;
			}
			return false;
		}
	}
	internal sealed class FindPage : Page
	{
		private const int PageSize = 8;

		private const int PortalLimit = 200;

		private const string PortalCategory = "portals";

		private const string PinCategory = "pins";

		private const string KnownCategory = "known";

		private const string PinNamePrefix = "Nearest ";

		private TMP_InputField _box;

		private TMP_Text _categoryText;

		private TMP_Text _pageText;

		private TMP_Text _card;

		private GameObject _previous;

		private GameObject _next;

		private readonly List<Button> _rowButtons = new List<Button>();

		private readonly List<TMP_Text> _rowTexts = new List<TMP_Text>();

		private List<string> _categories = new List<string>();

		private readonly List<Hit> _hits = new List<Hit>();

		private string _query = "";

		private int _categoryIndex;

		private int _page;

		private int _version = -1;

		private Hit _selected;

		private Entry _picked;

		private PinData _pin;

		private bool _pinWarned;

		private bool _nearest;

		private string _routeLine;

		internal override string Title => "Find";

		internal bool Typing
		{
			get
			{
				if ((Object)(object)_box != (Object)null)
				{
					return _box.isFocused;
				}
				return false;
			}
		}

		private bool Searching => _query.Trim().Length > 0;

		internal Vector3? Target
		{
			get
			{
				//IL_003b: 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_009b: Unknown re