Decompiled source of G3 Continents v1.0.0

G3Continents.dll

Decompiled a day 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.Text;
using System.Threading.Tasks;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Jotunn.Utils;
using Microsoft.CodeAnalysis;
using UnityEngine;

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

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace G3Continents
{
	internal static class Commands
	{
		[Serializable]
		[CompilerGenerated]
		private sealed class <>c
		{
			public static readonly <>c <>9 = new <>c();

			public static ConsoleEventFailable <>9__0_0;

			public static ConsoleEventFailable <>9__0_1;

			public static ConsoleEventFailable <>9__0_2;

			public static ConsoleEventFailable <>9__0_3;

			public static ConsoleEventFailable <>9__0_4;

			public static ConsoleEventFailable <>9__0_5;

			internal object <Register>b__0_0(ConsoleEventArgs args)
			{
				return Maps(args, batch: false);
			}

			internal object <Register>b__0_1(ConsoleEventArgs args)
			{
				return Maps(args, batch: true);
			}

			internal object <Register>b__0_2(ConsoleEventArgs args)
			{
				if (Preview.Busy)
				{
					return "a map is being drawn";
				}
				int size = 2048;
				for (int i = 1; i < args.Length; i++)
				{
					TrySize(args[i], ref size);
				}
				((MonoBehaviour)Plugin.Instance).StartCoroutine(Preview.ExportCurrent(args.Context, size, open: true));
				return true;
			}

			internal object <Register>b__0_3(ConsoleEventArgs args)
			{
				int keyMissing;
				int shortCount;
				string text = LocationCheck.Report(out keyMissing, out shortCount);
				if (text.Length == 0)
				{
					return "locations are known only where the world runs (host or server)";
				}
				string[] array = text.Split(new char[1] { '\n' });
				foreach (string text2 in array)
				{
					if (!text2.StartsWith("ok ", StringComparison.Ordinal) && !text2.StartsWith("   ", StringComparison.Ordinal))
					{
						Terminal context = args.Context;
						if (context != null)
						{
							context.AddString(text2.TrimEnd(new char[1] { '\r' }));
						}
					}
				}
				return true;
			}

			internal object <Register>b__0_4(ConsoleEventArgs args)
			{
				if ((Object)(object)Minimap.instance == (Object)null || (Object)(object)Player.m_localPlayer == (Object)null)
				{
					return "join a world first";
				}
				if ((Object)(object)ZNet.instance != (Object)null && !ZNet.instance.LocalPlayerIsAdminOrHost())
				{
					return "only the host or a server admin (adminlist.txt) can reveal the map";
				}
				Minimap.instance.ExploreAll();
				Terminal context = args.Context;
				if (context != null)
				{
					context.AddString("G3 Continents: the whole map is explored.");
				}
				return true;
			}

			internal object <Register>b__0_5(ConsoleEventArgs args)
			{
				Terminal context = args.Context;
				if (context != null)
				{
					context.AddString($"G3 Continents generator {1}: " + ShapeParams.ForVersion(1).Describe());
				}
				Terminal context2 = args.Context;
				if (context2 != null)
				{
					context2.AddString("New worlds get continents: " + Plugin.NewWorlds.Value);
				}
				return true;
			}
		}

		internal static void Register()
		{
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Expected O, but got Unknown
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Expected O, but got Unknown
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Expected O, but got Unknown
			//IL_00de: 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_00d4: Expected O, but got Unknown
			//IL_0117: 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_0107: Unknown result type (might be due to invalid IL or missing references)
			//IL_010d: Expected O, but got Unknown
			//IL_0150: Unknown result type (might be due to invalid IL or missing references)
			//IL_013b: 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_0146: Expected O, but got Unknown
			object obj = <>c.<>9__0_0;
			if (obj == null)
			{
				ConsoleEventFailable val = (ConsoleEventArgs args) => Maps(args, batch: false);
				<>c.<>9__0_0 = val;
				obj = (object)val;
			}
			new ConsoleCommand("g3c_map", "[G3 Continents] g3c_map [seed] [vanilla|both] [size=N] [noopen] [Key=value...]: draws a seed map", (ConsoleEventFailable)obj, false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
			object obj2 = <>c.<>9__0_1;
			if (obj2 == null)
			{
				ConsoleEventFailable val2 = (ConsoleEventArgs args) => Maps(args, batch: true);
				<>c.<>9__0_1 = val2;
				obj2 = (object)val2;
			}
			new ConsoleCommand("g3c_maps", "[G3 Continents] g3c_maps <count> [vanilla|both] [size=N] [Key=value...]: draws maps of random seeds", (ConsoleEventFailable)obj2, false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
			object obj3 = <>c.<>9__0_2;
			if (obj3 == null)
			{
				ConsoleEventFailable val3 = delegate(ConsoleEventArgs args)
				{
					if (Preview.Busy)
					{
						return "a map is being drawn";
					}
					int size = 2048;
					for (int i = 1; i < args.Length; i++)
					{
						TrySize(args[i], ref size);
					}
					((MonoBehaviour)Plugin.Instance).StartCoroutine(Preview.ExportCurrent(args.Context, size, open: true));
					return true;
				};
				<>c.<>9__0_2 = val3;
				obj3 = (object)val3;
			}
			new ConsoleCommand("g3c_world", "[G3 Continents] g3c_world [size=N]: map of this world with its key locations", (ConsoleEventFailable)obj3, false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
			object obj4 = <>c.<>9__0_3;
			if (obj4 == null)
			{
				ConsoleEventFailable val4 = delegate(ConsoleEventArgs args)
				{
					int keyMissing;
					int shortCount;
					string text = LocationCheck.Report(out keyMissing, out shortCount);
					if (text.Length == 0)
					{
						return "locations are known only where the world runs (host or server)";
					}
					string[] array = text.Split(new char[1] { '\n' });
					foreach (string text2 in array)
					{
						if (!text2.StartsWith("ok ", StringComparison.Ordinal) && !text2.StartsWith("   ", StringComparison.Ordinal))
						{
							Terminal context = args.Context;
							if (context != null)
							{
								context.AddString(text2.TrimEnd(new char[1] { '\r' }));
							}
						}
					}
					return true;
				};
				<>c.<>9__0_3 = val4;
				obj4 = (object)val4;
			}
			new ConsoleCommand("g3c_check", "[G3 Continents] g3c_check: are this world's locations all placed", (ConsoleEventFailable)obj4, false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
			object obj5 = <>c.<>9__0_4;
			if (obj5 == null)
			{
				ConsoleEventFailable val5 = delegate(ConsoleEventArgs args)
				{
					if ((Object)(object)Minimap.instance == (Object)null || (Object)(object)Player.m_localPlayer == (Object)null)
					{
						return "join a world first";
					}
					if ((Object)(object)ZNet.instance != (Object)null && !ZNet.instance.LocalPlayerIsAdminOrHost())
					{
						return "only the host or a server admin (adminlist.txt) can reveal the map";
					}
					Minimap.instance.ExploreAll();
					Terminal context = args.Context;
					if (context != null)
					{
						context.AddString("G3 Continents: the whole map is explored.");
					}
					return true;
				};
				<>c.<>9__0_4 = val5;
				obj5 = (object)val5;
			}
			new ConsoleCommand("g3c_explore", "[G3 Continents] g3c_explore: reveals the whole map (single player, the host or a server admin)", (ConsoleEventFailable)obj5, false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
			object obj6 = <>c.<>9__0_5;
			if (obj6 == null)
			{
				ConsoleEventFailable val6 = delegate(ConsoleEventArgs args)
				{
					Terminal context = args.Context;
					if (context != null)
					{
						context.AddString($"G3 Continents generator {1}: " + ShapeParams.ForVersion(1).Describe());
					}
					Terminal context2 = args.Context;
					if (context2 != null)
					{
						context2.AddString("New worlds get continents: " + Plugin.NewWorlds.Value);
					}
					return true;
				};
				<>c.<>9__0_5 = val6;
				obj6 = (object)val6;
			}
			new ConsoleCommand("g3c_params", "[G3 Continents] g3c_params: the continent generator numbers", (ConsoleEventFailable)obj6, false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
		}

		private static object Maps(ConsoleEventArgs args, bool batch)
		{
			if (Preview.Busy)
			{
				return "a map is being drawn";
			}
			int num = 1;
			int size = (batch ? 512 : 1024);
			bool open = true;
			bool flag = false;
			bool flag2 = false;
			string text = null;
			ShapeParams shapeParams = null;
			ShapeParams shapeParams2 = ShapeParams.ForVersion(1);
			for (int i = 1; i < args.Length; i++)
			{
				string text2 = args[i];
				switch (text2.ToLowerInvariant())
				{
				case "vanilla":
					flag = true;
					continue;
				case "both":
					flag2 = true;
					continue;
				case "noopen":
					open = false;
					continue;
				}
				if (TrySize(text2, ref size))
				{
					continue;
				}
				int result;
				if (text2.Contains("="))
				{
					string[] array = text2.Split(new char[1] { '=' }, 2);
					shapeParams = shapeParams ?? shapeParams2.Clone();
					if (!shapeParams.Set(array[0], array[1]))
					{
						return "unknown generator number '" + text2 + "', see g3c_params";
					}
				}
				else if (batch && num == 1 && int.TryParse(text2, NumberStyles.Integer, CultureInfo.InvariantCulture, out result))
				{
					num = Math.Max(1, Math.Min(result, 200));
				}
				else
				{
					if (batch || text != null)
					{
						return "unknown word '" + text2 + "'";
					}
					text = text2;
				}
			}
			List<Preview.Job> list = new List<Preview.Job>();
			for (int j = 0; j < num; j++)
			{
				string seed = text ?? World.GenerateSeed();
				if (!flag || flag2)
				{
					list.Add(new Preview.Job
					{
						Seed = seed,
						Custom = shapeParams,
						Size = size
					});
				}
				if (flag || flag2)
				{
					list.Add(new Preview.Job
					{
						Seed = seed,
						Vanilla = true,
						Size = size
					});
				}
			}
			((MonoBehaviour)Plugin.Instance).StartCoroutine(Preview.Run(list, args.Context, open));
			return true;
		}

		private static bool TrySize(string a, ref int size)
		{
			if (!a.StartsWith("size=", StringComparison.OrdinalIgnoreCase) || !int.TryParse(a.Substring(5), NumberStyles.Integer, CultureInfo.InvariantCulture, out var result))
			{
				return false;
			}
			size = Math.Max(128, Math.Min(result, 4096));
			return true;
		}
	}
	[HarmonyPatch]
	internal static class Compat
	{
		[Serializable]
		[CompilerGenerated]
		private sealed class <>c
		{
			public static readonly <>c <>9 = new <>c();

			public static Func<MethodBase, bool> <>9__5_1;

			public static PopupButtonCallback <>9__8_0;

			internal bool <Check>b__5_1(MethodBase m)
			{
				return m != null;
			}

			internal void <Show>b__8_0()
			{
				UnifiedPopup.Pop();
			}
		}

		private static readonly string[] s_knownMods = new string[7] { "bettercontinents", "better continents", "expand_world_data", "expand world data", "expand_world_size", "expand world size", "riverheim" };

		internal static readonly List<string> Conflicts = new List<string>();

		private static bool s_shown;

		internal static bool HasConflict => Conflicts.Count > 0;

		internal static void Check()
		{
			Conflicts.Clear();
			foreach (PluginInfo value in Chainloader.PluginInfos.Values)
			{
				string guid = value.Metadata.GUID ?? string.Empty;
				string name = value.Metadata.Name ?? string.Empty;
				if (!(guid == "g3.continents") && s_knownMods.Any((string k) => guid.IndexOf(k, StringComparison.OrdinalIgnoreCase) >= 0 || name.IndexOf(k, StringComparison.OrdinalIgnoreCase) >= 0))
				{
					Add(name + " (" + guid + ")");
				}
			}
			List<MethodBase> list = new List<MethodBase>();
			list.Add(AccessTools.Method(typeof(WorldGenerator), "GetBaseHeight", (Type[])null, (Type[])null));
			list.Add(AccessTools.Method(typeof(WorldGenerator), "GetBiome", new Type[4]
			{
				typeof(float),
				typeof(float),
				typeof(float),
				typeof(bool)
			}, (Type[])null));
			list.Add(AccessTools.Method(typeof(WorldGenerator), "GetBiomeHeight", (Type[])null, (Type[])null));
			foreach (MethodBase item in list.Where((MethodBase m) => m != null))
			{
				Patches patchInfo = Harmony.GetPatchInfo(item);
				if (patchInfo == null)
				{
					continue;
				}
				foreach (string owner in patchInfo.Owners)
				{
					if (owner != "g3.continents")
					{
						Add(owner + " (patches WorldGenerator." + item.Name + ")");
					}
				}
			}
			if (HasConflict)
			{
				Plugin.Log.LogWarning((object)("Other world generation mods found: " + string.Join(", ", Conflicts) + ". New worlds will NOT get continents."));
			}
		}

		private static void Add(string what)
		{
			if (!Conflicts.Contains(what))
			{
				Conflicts.Add(what);
			}
		}

		[HarmonyPatch(typeof(FejdStartup), "Start")]
		[HarmonyPostfix]
		private static void MainMenu(FejdStartup __instance)
		{
			if (HasConflict && !s_shown)
			{
				((MonoBehaviour)__instance).StartCoroutine(Show());
			}
		}

		private static IEnumerator Show()
		{
			yield return (object)new WaitForSeconds(1.5f);
			if (s_shown)
			{
				yield break;
			}
			s_shown = true;
			string text = string.Join("\n", Conflicts);
			string text2 = "Another world generation mod is installed, so new worlds will NOT get continents:\n" + text + "\n\nУстановлен другой мод генерации мира, поэтому новые миры НЕ получат континенты. Уберите один из модов.";
			object obj = <>c.<>9__8_0;
			if (obj == null)
			{
				PopupButtonCallback val = delegate
				{
					UnifiedPopup.Pop();
				};
				<>c.<>9__8_0 = val;
				obj = (object)val;
			}
			UnifiedPopup.Push((PopupBase)new WarningPopup("G3 Continents", text2, (PopupButtonCallback)obj, false));
		}
	}
	internal sealed class ShapeParams
	{
		public float Warp = 700f;

		public float WarpScale = 2600f;

		public float PoleBand = 500f;

		public float EdgeOcean = 150f;

		public float Shelf = 220f;

		public float DeepDistance = 600f;

		public float LowlandSpan = 0.07f;

		public float Relief = 0.05f;

		public float BigLakes = 0.845f;

		public float BigLakeScale = 1400f;

		public float SmallLakes = 0.87f;

		public float SmallLakeScale = 450f;

		public float BiomeWarp = 600f;

		public float BiomeWarpScale = 1800f;

		public float RingWobble = 250f;

		public float MountainBoost = 0.4f;

		public ShapeParams Clone()
		{
			return (ShapeParams)MemberwiseClone();
		}

		public static ShapeParams ForVersion(int gen)
		{
			if (gen == 1)
			{
				return new ShapeParams();
			}
			return null;
		}

		public bool Set(string key, string value)
		{
			FieldInfo[] fields = typeof(ShapeParams).GetFields(BindingFlags.Instance | BindingFlags.Public);
			foreach (FieldInfo fieldInfo in fields)
			{
				if (string.Equals(fieldInfo.Name, key, StringComparison.OrdinalIgnoreCase))
				{
					if (fieldInfo.FieldType == typeof(float) && float.TryParse(value.Replace(',', '.'), NumberStyles.Float, CultureInfo.InvariantCulture, out var result))
					{
						fieldInfo.SetValue(this, result);
						return true;
					}
					return false;
				}
			}
			return false;
		}

		public string Describe(ShapeParams baseline = null)
		{
			StringBuilder stringBuilder = new StringBuilder();
			FieldInfo[] fields = typeof(ShapeParams).GetFields(BindingFlags.Instance | BindingFlags.Public);
			foreach (FieldInfo fieldInfo in fields)
			{
				object value = fieldInfo.GetValue(this);
				if (baseline == null || !object.Equals(value, fieldInfo.GetValue(baseline)))
				{
					if (stringBuilder.Length > 0)
					{
						stringBuilder.Append(' ');
					}
					stringBuilder.Append(fieldInfo.Name).Append('=').Append(Convert.ToString(value, CultureInfo.InvariantCulture));
				}
			}
			return stringBuilder.ToString();
		}
	}
	internal sealed class ContinentShape
	{
		private struct Rng
		{
			private ulong _s;

			public Rng(int seed, int gen)
			{
				_s = ((ulong)(uint)seed << 16) ^ (ulong)((uint)gen * -7046029254386353131L) ^ 0xD1B54A32D192ED03uL;
			}

			private ulong Next()
			{
				ulong num = (_s += 11400714819323198485uL);
				long num2 = (long)(num ^ (num >> 30)) * -4658895280553007687L;
				long num3 = (num2 ^ (num2 >>> 27)) * -7723592293110705685L;
				return (ulong)(num3 ^ (num3 >>> 31));
			}

			public float Float()
			{
				return (float)(Next() >> 40) / 16777216f;
			}

			public float Range(float a, float b)
			{
				return a + (b - a) * Float();
			}
		}

		private const float WorldRadius = 10000f;

		private const float InlandFloor = 0.24f;

		private const float LakeBottom = 0.01f;

		private const float ShoreSea = 0.12f;

		private const float DeepSea = -0.14f;

		private readonly ShapeParams _p;

		private readonly Vector2[] _offsets = (Vector2[])(object)new Vector2[19];

		public ShapeParams Params => _p;

		public ContinentShape(int seed, int gen, ShapeParams p)
		{
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			_p = p;
			Rng rng = new Rng(seed, gen);
			for (int i = 0; i < _offsets.Length; i++)
			{
				_offsets[i] = new Vector2(rng.Range(0f, 1000f), rng.Range(0f, 1000f));
			}
		}

		private static float PoleDistance(float x, float y)
		{
			float num = WorldGenerator.WorldAngle(x, y) * 100f;
			float num2 = Len(x, y - 4000f) - (12000f + num);
			float num3 = Len(x, y + 4000f) - (12000f + num);
			return Mathf.Max(num2, num3);
		}

		public void BiomePoint(float x, float y, out float bx, out float by)
		{
			ShapeParams p = _p;
			float num = Mathf.Clamp01(Len(x, y) / 2000f);
			float biomeWarpScale = p.BiomeWarpScale;
			float scale = biomeWarpScale * 0.29f;
			float scale2 = biomeWarpScale * 0.09f;
			float scale3 = biomeWarpScale * 0.03f;
			float num2 = p.BiomeWarp * num;
			float num3 = num2 * 0.45f;
			float num4 = num2 * 0.2f;
			float num5 = num2 * 0.08f;
			bx = x + num2 * Signed(11, x, y, biomeWarpScale) + num3 * Signed(13, x, y, scale) + num4 * Signed(15, x, y, scale2) + num5 * Signed(17, x, y, scale3);
			by = y + num2 * Signed(12, x, y, biomeWarpScale) + num3 * Signed(14, x, y, scale) + num4 * Signed(16, x, y, scale2) + num5 * Signed(18, x, y, scale3);
		}

		public float RingWobble(float bx, float by)
		{
			return _p.RingWobble * Signed(6, bx, by, 1200f);
		}

		private float Signed(int k, float x, float y, float scale)
		{
			return Noise(k, x / scale, y / scale) * 2f - 1f;
		}

		public float Apply(float x, float y, float h)
		{
			float num = Len(x, y);
			if (num >= 10000f)
			{
				return h;
			}
			float num2 = PoleDistance(x, y);
			if (num2 >= 0f)
			{
				return h;
			}
			ShapeParams p = _p;
			float warpScale = p.WarpScale;
			float num3 = warpScale * 0.33f;
			float x2 = x + p.Warp * (Noise(0, x / warpScale, y / warpScale) * 2f - 1f) + p.Warp * 0.35f * (Noise(2, x / num3, y / num3) * 2f - 1f);
			float y2 = y + p.Warp * (Noise(1, x / warpScale, y / warpScale) * 2f - 1f) + p.Warp * 0.35f * (Noise(3, x / num3, y / num3) * 2f - 1f);
			float num4 = Noise(5, x / 1500f, y / 1500f) * 2f - 1f;
			float num5 = Mathf.Min(0f - (PoleDistance(x2, y2) + p.PoleBand + num4 * 300f), 0f - (num2 + 500f));
			float num6 = Mathf.Min(10000f - p.EdgeOcean - (Len(x2, y2) + num4 * 150f), 9750f - num);
			float num7 = Mathf.Min(num5, num6);
			num7 = Mathf.Max(num7, 900f - num);
			float num8 = h;
			if (h < 0.24f)
			{
				float num9 = 1f - Mathf.Exp(0f - (0.24f - h) / p.LowlandSpan);
				num8 = 0.24f - p.LowlandSpan * num9 + (Noise(9, x / 900f, y / 900f) - 0.5f) * p.Relief * num9;
			}
			if (h > 0.3f)
			{
				num8 += (h - 0.3f) * p.MountainBoost;
			}
			float num10 = SmoothStep(p.BigLakes, p.BigLakes + 0.05f, Noise(7, x / p.BigLakeScale, y / p.BigLakeScale));
			float num11 = SmoothStep(p.SmallLakes, p.SmallLakes + 0.05f, Noise(8, x / p.SmallLakeScale, y / p.SmallLakeScale));
			float num12 = Mathf.Max(num10, num11) * SmoothStep(p.Shelf + 150f, p.Shelf + 500f, num7) * (1f - SmoothStep(0.3f, 0.38f, h)) * SmoothStep(900f, 1400f, num);
			num8 = Mathf.Lerp(num8, 0.01f, num12);
			float num13 = SmoothStep(0f, p.Shelf, num7);
			return Mathf.Lerp(Mathf.Lerp(0.12f, -0.14f, SmoothStep(0f, p.DeepDistance, 0f - num7)), num8, num13);
		}

		private float Noise(int k, float x, float y)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			Vector2 val = _offsets[k];
			return Mathf.PerlinNoise(val.x + x, val.y + y);
		}

		private static float Len(float x, float y)
		{
			return Mathf.Sqrt(x * x + y * y);
		}

		private static float SmoothStep(float a, float b, float v)
		{
			float num = Mathf.Clamp01((v - a) / (b - a));
			return num * num * (3f - 2f * num);
		}
	}
	[HarmonyPatch]
	internal static class LocationCheck
	{
		internal sealed class Row
		{
			public string Name;

			public int Placed;

			public int Wanted;

			public bool Key;

			public bool Short => Placed < Wanted;
		}

		private static readonly string[] s_key = new string[11]
		{
			"StartTemple", "Eikthyrnir", "GDKing", "Bonemass", "Dragonqueen", "GoblinKing", "Mistlands_DvergrBossEntrance1", "FaderLocation", "Vendor_BlackForest", "Hildir_camp",
			"BogWitch_Camp"
		};

		private const int RareQuantity = 5;

		internal static bool IsKey(string name)
		{
			return s_key.Any((string k) => string.Equals(k, name, StringComparison.OrdinalIgnoreCase));
		}

		internal static List<Row> Rows()
		{
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			ZoneSystem instance = ZoneSystem.instance;
			if ((Object)(object)instance == (Object)null || (Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer())
			{
				return null;
			}
			Dictionary<string, int> placed = new Dictionary<string, int>();
			foreach (LocationInstance value4 in instance.m_locationInstances.Values)
			{
				string text = value4.m_location?.m_prefabName;
				if (!string.IsNullOrEmpty(text))
				{
					placed.TryGetValue(text, out var value);
					placed[text] = value + 1;
				}
			}
			Dictionary<string, int> dictionary = new Dictionary<string, int>();
			foreach (ZoneLocation location in instance.m_locations)
			{
				if (location != null && location.m_enable && location.m_quantity > 0 && !string.IsNullOrEmpty(location.m_prefabName))
				{
					dictionary.TryGetValue(location.m_prefabName, out var value2);
					dictionary[location.m_prefabName] = value2 + location.m_quantity;
				}
			}
			int value3;
			return (from w in dictionary
				select new Row
				{
					Name = w.Key,
					Wanted = w.Value,
					Placed = (placed.TryGetValue(w.Key, out value3) ? value3 : 0),
					Key = IsKey(w.Key)
				} into r
				orderby r.Key && r.Short descending, r.Key descending, r.Short descending, r.Name
				select r).ToList();
		}

		internal static List<Preview.Marker> Markers()
		{
			List<Row> list = Rows();
			if (list == null)
			{
				return null;
			}
			HashSet<string> shown = new HashSet<string>(from r in list
				where r.Key || r.Wanted <= 5
				select r.Name);
			return (from li in ZoneSystem.instance.m_locationInstances.Values
				where li.m_location != null && shown.Contains(li.m_location.m_prefabName)
				select new Preview.Marker
				{
					Name = li.m_location.m_prefabName,
					X = li.m_position.x,
					Z = li.m_position.z,
					Key = IsKey(li.m_location.m_prefabName)
				}).ToList();
		}

		internal static string Report(out int keyMissing, out int shortCount)
		{
			keyMissing = 0;
			shortCount = 0;
			List<Row> list = Rows();
			if (list == null)
			{
				return string.Empty;
			}
			StringBuilder stringBuilder = new StringBuilder();
			foreach (Row item in list)
			{
				if (item.Key && item.Short)
				{
					keyMissing++;
				}
				if (item.Short)
				{
					shortCount++;
				}
			}
			World val = WorldGenerator.instance?.m_world;
			stringBuilder.AppendLine($"G3 Continents location check, world '{val?.m_name}', seed '{val?.m_seedName}', generator {WorldGenPatch.GenOf(val)}");
			stringBuilder.AppendLine((keyMissing > 0) ? $"PROBLEM: {keyMissing} key location(s) not placed in full. Make the world again with another seed." : "Key locations: all placed.");
			stringBuilder.AppendLine($"Locations placed short of their quantity: {shortCount}.");
			stringBuilder.AppendLine();
			foreach (Row item2 in list)
			{
				string text = (item2.Key ? (item2.Short ? "!! " : "ok ") : (item2.Short ? " - " : "   "));
				stringBuilder.AppendLine($"{text}{item2.Name}: {item2.Placed}/{item2.Wanted}");
			}
			return stringBuilder.ToString();
		}

		[HarmonyPatch(typeof(ZoneSystem), "Start")]
		[HarmonyPostfix]
		private static void Watch(ZoneSystem __instance)
		{
			if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer())
			{
				__instance.GenerateLocationsCompleted += OnLocations;
			}
		}

		private static void OnLocations()
		{
			try
			{
				if (WorldGenPatch.GenOf(WorldGenerator.instance?.m_world) <= 0)
				{
					return;
				}
				int keyMissing;
				int shortCount;
				string text = Report(out keyMissing, out shortCount);
				if (text.Length != 0)
				{
					Directory.CreateDirectory(Preview.Folder);
					World world = WorldGenerator.instance.m_world;
					string text2 = Path.Combine(Preview.Folder, "locations_" + Preview.Safe(world.m_name) + ".txt");
					File.WriteAllText(text2, text, new UTF8Encoding(encoderShouldEmitUTF8Identifier: false));
					if (keyMissing > 0)
					{
						Plugin.Log.LogWarning((object)$"{keyMissing} key location(s) missing in world '{world.m_name}', see {text2}");
					}
					else
					{
						Plugin.Log.LogInfo((object)$"Locations checked: key ones all placed, {shortCount} other(s) short. Report: {text2}");
					}
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("Location check failed: " + ex));
			}
		}
	}
	[HarmonyPatch]
	internal static class NetGate
	{
		private sealed class Info
		{
			public string Version;

			public int Gen;
		}

		private const string Hello = "G3C_Hello";

		private const int ErrorVersion = 3;

		private static readonly ConditionalWeakTable<ZRpc, Info> s_hello = new ConditionalWeakTable<ZRpc, Info>();

		[HarmonyPatch(typeof(ZNet), "OnNewConnection")]
		[HarmonyPostfix]
		private static void Connected(ZNet __instance, ZNetPeer peer)
		{
			if (__instance.IsServer())
			{
				peer.m_rpc.Register<string, int>("G3C_Hello", (Action<ZRpc, string, int>)delegate(ZRpc rpc, string version, int gen)
				{
					s_hello.Remove(rpc);
					s_hello.Add(rpc, new Info
					{
						Version = version,
						Gen = gen
					});
				});
			}
			else
			{
				peer.m_rpc.Invoke("G3C_Hello", new object[2] { "1.0.0", 1 });
			}
		}

		[HarmonyPatch(typeof(ZNet), "RPC_PeerInfo")]
		[HarmonyPrefix]
		private static bool PeerInfo(ZNet __instance, ZRpc rpc)
		{
			if (!__instance.IsServer())
			{
				return true;
			}
			int num = WorldGenPatch.GenOf(WorldGenerator.instance?.m_world);
			if (num <= 0)
			{
				return true;
			}
			ISocket socket = rpc.GetSocket();
			string text = ((socket != null) ? socket.GetEndPointString() : null) ?? "?";
			if (!s_hello.TryGetValue(rpc, out var value))
			{
				Plugin.Log.LogWarning((object)("Turned away " + text + ": this world has continents and the player has no G3 Continents."));
				rpc.Invoke("Error", new object[1] { 3 });
				return false;
			}
			if (value.Gen < num)
			{
				Plugin.Log.LogWarning((object)$"Turned away {text}: G3 Continents {value.Version} knows generators up to {value.Gen}, this world uses {num}.");
				rpc.Invoke("Error", new object[1] { 3 });
				return false;
			}
			return true;
		}
	}
	[BepInPlugin("g3.continents", "G3 Continents", "1.0.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[NetworkCompatibility(/*Could not decode attribute arguments.*/)]
	public class Plugin : BaseUnityPlugin
	{
		public const string PluginGUID = "g3.continents";

		public const string PluginName = "G3 Continents";

		public const string PluginVersion = "1.0.0";

		internal static Plugin Instance;

		internal static ManualLogSource Log;

		internal static ConfigEntry<bool> NewWorlds;

		private Harmony _harmony;

		private void Awake()
		{
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Expected O, but got Unknown
			Instance = this;
			Log = ((BaseUnityPlugin)this).Logger;
			NewWorlds = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "NewWorldsWithContinents", true, "New worlds made while this is on get continents. The choice is saved in the world: existing worlds never change, either way.");
			_harmony = new Harmony("g3.continents");
			_harmony.PatchAll(typeof(Plugin).Assembly);
			Commands.Register();
			Log.LogInfo((object)string.Format("{0} {1} loaded, generator {2}.", "G3 Continents", "1.0.0", 1));
		}

		private void Start()
		{
			Compat.Check();
		}

		private void OnDestroy()
		{
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}
	}
	internal static class Preview
	{
		internal sealed class Job
		{
			public string Seed;

			public bool Vanilla;

			public ShapeParams Custom;

			public int Size = 1024;
		}

		internal sealed class Marker
		{
			public string Name;

			public float X;

			public float Z;

			public bool Key;
		}

		private sealed class MapData
		{
			public Color32[] Pixels;

			public string StatsJson;

			public string Summary;
		}

		public const string WorldName = "g3c_preview";

		private const float Span = 21000f;

		private const float WaterLevel = 30f;

		private static readonly ConstructorInfo s_genCtor = AccessTools.Constructor(typeof(WorldGenerator), new Type[1] { typeof(World) }, false);

		private static readonly Dictionary<Biome, Color> s_colors = new Dictionary<Biome, Color>
		{
			{
				(Biome)1,
				new Color(0.56f, 0.71f, 0.33f)
			},
			{
				(Biome)8,
				new Color(0.25f, 0.42f, 0.22f)
			},
			{
				(Biome)2,
				new Color(0.48f, 0.4f, 0.3f)
			},
			{
				(Biome)4,
				new Color(0.9f, 0.91f, 0.94f)
			},
			{
				(Biome)16,
				new Color(0.86f, 0.78f, 0.42f)
			},
			{
				(Biome)512,
				new Color(0.42f, 0.36f, 0.47f)
			},
			{
				(Biome)32,
				new Color(0.6f, 0.22f, 0.16f)
			},
			{
				(Biome)64,
				new Color(0.78f, 0.86f, 0.94f)
			},
			{
				(Biome)256,
				new Color(0.8f, 0.75f, 0.55f)
			}
		};

		private static readonly Biome[] s_biomeOrder;

		internal static string Folder => Path.Combine(Paths.BepInExRootPath, "G3Continents", "maps");

		internal static string ViewerPath => Path.Combine(Folder, "viewer.html");

		internal static bool Busy { get; private set; }

		internal static IEnumerator Run(List<Job> jobs, Terminal ctx, bool open)
		{
			Busy = true;
			string last = null;
			try
			{
				Directory.CreateDirectory(Folder);
				for (int i = 0; i < jobs.Count; i++)
				{
					Job job = jobs[i];
					string text = (job.Vanilla ? "vanilla" : ((job.Custom != null) ? "custom" : ("continents " + 1)));
					Say(ctx, $"G3 Continents: map {i + 1}/{jobs.Count}, seed '{job.Seed}' ({text})...");
					yield return null;
					World val = new World("g3c_preview", job.Seed);
					val.m_worldGenVersion = (job.Vanilla ? 2 : 1002);
					if (!job.Vanilla && job.Custom != null)
					{
						WorldGenPatch.PreviewParams.Add(val, job.Custom);
					}
					Stopwatch watch = Stopwatch.StartNew();
					WorldGenerator gen = (WorldGenerator)s_genCtor.Invoke(new object[1] { val });
					double pregen = watch.Elapsed.TotalSeconds;
					MapData data = null;
					Task task = Task.Run(() => data = Render(gen, job.Size, null));
					while (!task.IsCompleted)
					{
						yield return null;
					}
					if (task.Exception != null)
					{
						Plugin.Log.LogError((object)("Map failed: " + task.Exception));
						Say(ctx, "G3 Continents: the map failed, see the log.");
						continue;
					}
					double num = watch.Elapsed.TotalSeconds - pregen;
					string text2 = FileName(job.Seed, job.Vanilla ? "vanilla" : ("g" + 1), job.Custom);
					SavePng(data.Pixels, job.Size, Path.Combine(Folder, text2 + ".png"));
					ContinentShape shape = (job.Vanilla ? null : WorldGenPatch.ShapeOf(gen));
					WriteJson(text2, job.Seed, (!job.Vanilla) ? 1 : 0, (job.Custom != null) ? "custom" : (job.Vanilla ? "vanilla" : "continents"), shape, job.Custom, job.Size, pregen, num, data.StatsJson, null, null, gen);
					last = text2;
					Say(ctx, $"G3 Continents: {text2}.png in {pregen + num:0.0} s. {data.Summary}");
				}
				WriteIndex();
				if (last != null)
				{
					Say(ctx, "G3 Continents: maps are in " + Folder);
					if (open)
					{
						Application.OpenURL(new Uri(ViewerPath).AbsoluteUri + "#" + last);
					}
				}
			}
			finally
			{
				Busy = false;
			}
		}

		internal static IEnumerator ExportCurrent(Terminal ctx, int size, bool open)
		{
			WorldGenerator gen = WorldGenerator.instance;
			World world = gen?.m_world;
			if (world == null)
			{
				Say(ctx, "G3 Continents: no world is loaded.");
				yield break;
			}
			Busy = true;
			try
			{
				Directory.CreateDirectory(Folder);
				List<LocationCheck.Row> rows = LocationCheck.Rows();
				List<Marker> markers = LocationCheck.Markers();
				Say(ctx, "G3 Continents: drawing world '" + world.m_name + "'...");
				yield return null;
				Stopwatch watch = Stopwatch.StartNew();
				MapData data = null;
				Task task = Task.Run(() => data = Render(gen, size, markers));
				while (!task.IsCompleted)
				{
					yield return null;
				}
				if (task.Exception != null)
				{
					Plugin.Log.LogError((object)("Map failed: " + task.Exception));
					Say(ctx, "G3 Continents: the map failed, see the log.");
					yield break;
				}
				int gen2 = WorldGenPatch.GenOf(world);
				string text = FileName(world.m_seedName, "world_" + Safe(world.m_name), null);
				SavePng(data.Pixels, size, Path.Combine(Folder, text + ".png"));
				WriteJson(text, world.m_seedName, gen2, "world " + world.m_name, WorldGenPatch.ShapeOf(gen), null, size, 0.0, watch.Elapsed.TotalSeconds, data.StatsJson, markers, rows, gen);
				WriteIndex();
				Say(ctx, "G3 Continents: " + text + ".png. " + data.Summary);
				if (rows == null)
				{
					Say(ctx, "G3 Continents: locations are known only where the world runs (host or server), so the map has none.");
				}
				if (open)
				{
					Application.OpenURL(new Uri(ViewerPath).AbsoluteUri + "#" + text);
				}
			}
			finally
			{
				Busy = false;
			}
		}

		private static void Say(Terminal ctx, string text)
		{
			Plugin.Log.LogInfo((object)text);
			if (ctx != null)
			{
				ctx.AddString(text);
			}
		}

		internal static string Safe(string s)
		{
			StringBuilder stringBuilder = new StringBuilder();
			string text = s ?? string.Empty;
			foreach (char c in text)
			{
				stringBuilder.Append((c < '\u0080' && char.IsLetterOrDigit(c)) ? c : '_');
			}
			if (stringBuilder.Length != 0)
			{
				return stringBuilder.ToString();
			}
			return "_";
		}

		private static string FileName(string seed, string mode, ShapeParams custom)
		{
			string text = $"{Safe(seed)}_{StringExtensionMethods.GetStableHashCode(seed) & 0xFFFF:x4}_{mode}";
			if (custom != null)
			{
				text += $"_c{StringExtensionMethods.GetStableHashCode(custom.Describe()) & 0xFFFF:x4}";
			}
			return text;
		}

		private static void SavePng(Color32[] pixels, int size, string path)
		{
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Expected O, but got Unknown
			Texture2D val = new Texture2D(size, size, (TextureFormat)3, false);
			val.SetPixels32(pixels);
			val.Apply(false);
			File.WriteAllBytes(path, ImageConversion.EncodeToPNG(val));
			Object.Destroy((Object)(object)val);
		}

		private static MapData Render(WorldGenerator gen, int size, List<Marker> markers)
		{
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_0104: Unknown result type (might be due to invalid IL or missing references)
			float m = 21000f / (float)size;
			float[] heights = new float[size * size];
			Biome[] biomes = (Biome[])(object)new Biome[size * size];
			Parallel.For(0, size, delegate(int row)
			{
				//IL_0072: Unknown result type (might be due to invalid IL or missing references)
				//IL_0077: Unknown result type (might be due to invalid IL or missing references)
				//IL_0080: Unknown result type (might be due to invalid IL or missing references)
				//IL_0083: Expected I4, but got Unknown
				//IL_0090: Unknown result type (might be due to invalid IL or missing references)
				float num = ((float)row + 0.5f) * m - 10500f;
				Color val2 = default(Color);
				for (int i = 0; i < size; i++)
				{
					float num2 = ((float)i + 0.5f) * m - 10500f;
					int num3 = row * size + i;
					if (num2 * num2 + num * num > 110250000f)
					{
						biomes[num3] = (Biome)0;
						heights[num3] = -400f;
					}
					else
					{
						Biome biome = gen.GetBiome(num2, num, 0.02f, false);
						biomes[num3] = (Biome)(int)biome;
						heights[num3] = gen.GetBiomeHeight(biome, num2, num, ref val2, false, true);
					}
				}
			});
			Color32[] pixels = (Color32[])(object)new Color32[size * size];
			Vector3 val = new Vector3(-1f, 1f, 1.3f);
			Vector3 light = ((Vector3)(ref val)).normalized;
			float flat = light.z;
			Color shallow = new Color(0.33f, 0.58f, 0.72f);
			Color deep = new Color(0.07f, 0.19f, 0.38f);
			Color outside = new Color(0.07f, 0.09f, 0.12f);
			Parallel.For(0, size, delegate(int row)
			{
				//IL_0024: Unknown result type (might be due to invalid IL or missing references)
				//IL_0029: Unknown result type (might be due to invalid IL or missing references)
				//IL_002e: 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_0118: Unknown result type (might be due to invalid IL or missing references)
				//IL_011c: Unknown result type (might be due to invalid IL or missing references)
				//IL_0121: Unknown result type (might be due to invalid IL or missing references)
				//IL_0128: Unknown result type (might be due to invalid IL or missing references)
				//IL_012b: Unknown result type (might be due to invalid IL or missing references)
				//IL_004a: Unknown result type (might be due to invalid IL or missing references)
				//IL_0050: Unknown result type (might be due to invalid IL or missing references)
				//IL_0067: Unknown result type (might be due to invalid IL or missing references)
				//IL_006c: Unknown result type (might be due to invalid IL or missing references)
				//IL_0177: 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_01b8: Unknown result type (might be due to invalid IL or missing references)
				//IL_01b9: Unknown result type (might be due to invalid IL or missing references)
				//IL_01be: 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_0180: Unknown result type (might be due to invalid IL or missing references)
				//IL_0181: Unknown result type (might be due to invalid IL or missing references)
				//IL_0182: Unknown result type (might be due to invalid IL or missing references)
				//IL_019f: Unknown result type (might be due to invalid IL or missing references)
				//IL_01a4: Unknown result type (might be due to invalid IL or missing references)
				for (int i = 0; i < size; i++)
				{
					int num = row * size + i;
					if ((int)biomes[num] == 0)
					{
						pixels[num] = Color32.op_Implicit(outside);
					}
					else
					{
						float num2 = heights[num];
						Color val2;
						if (num2 < 30f)
						{
							val2 = Color.Lerp(shallow, deep, Mathf.Clamp01((30f - num2) / 45f));
						}
						else
						{
							float num3 = heights[row * size + Mathf.Max(i - 1, 0)];
							float num4 = heights[row * size + Mathf.Min(i + 1, size - 1)];
							float num5 = heights[Mathf.Max(row - 1, 0) * size + i];
							float num6 = heights[Mathf.Min(row + 1, size - 1) * size + i];
							Vector3 val3 = new Vector3((0f - (num4 - num3)) / (2f * m), (0f - (num6 - num5)) / (2f * m), 1f);
							Vector3 normalized = ((Vector3)(ref val3)).normalized;
							float num7 = Mathf.Clamp(1f + (Vector3.Dot(normalized, light) / flat - 1f) * 1.8f, 0.55f, 1.3f);
							val2 = (s_colors.TryGetValue(biomes[num], out var value) ? value : Color.magenta) * num7;
							val2 = Color.Lerp(val2, Color.white, Mathf.Clamp01((num2 - 60f) / 400f) * 0.25f);
						}
						val2.a = 1f;
						pixels[num] = Color32.op_Implicit(val2);
					}
				}
			});
			return Stats(gen, size, m, heights, biomes, pixels);
		}

		private static MapData Stats(WorldGenerator gen, int size, float m, float[] heights, Biome[] biomes, Color32[] pixels)
		{
			//IL_01f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_02df: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f0: Invalid comparison between Unknown and I4
			//IL_0333: Unknown result type (might be due to invalid IL or missing references)
			//IL_0337: Unknown result type (might be due to invalid IL or missing references)
			//IL_03d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_035d: Unknown result type (might be due to invalid IL or missing references)
			float num = 10500f;
			int num2 = size * size;
			bool[] array = new bool[num2];
			bool[] array2 = new bool[num2];
			int num3 = 0;
			int num4 = 0;
			Dictionary<Biome, int> dictionary = new Dictionary<Biome, int>();
			for (int i = 0; i < size; i++)
			{
				float num5 = ((float)i + 0.5f) * m - num;
				for (int j = 0; j < size; j++)
				{
					float num6 = ((float)j + 0.5f) * m - num;
					int num7 = i * size + j;
					if (!(num6 * num6 + num5 * num5 > 100000000f))
					{
						array[num7] = true;
						num3++;
						if (heights[num7] >= 30f)
						{
							array2[num7] = true;
							num4++;
							dictionary.TryGetValue(biomes[num7], out var value);
							dictionary[biomes[num7]] = value + 1;
						}
					}
				}
			}
			float km2 = m * m / 1000000f;
			List<int> list = Components(size, array2, null);
			list.Sort((int a, int b) => b.CompareTo(a));
			int num8 = list.Count((int a) => (float)a * km2 >= 25f);
			int num9 = list.Count((int a) => (float)a * km2 >= 0.05f && (float)a * km2 < 25f);
			float v = ((list.Count > 0) ? ((float)list[0] * km2) : 0f);
			bool[] array3 = new bool[num2];
			for (int num10 = 0; num10 < num2; num10++)
			{
				array3[num10] = array[num10] && !array2[num10];
			}
			bool[] array4 = new bool[num2];
			FloodFromEdge(size, m, array3, array4);
			bool[] array5 = new bool[num2];
			for (int num11 = 0; num11 < num2; num11++)
			{
				array5[num11] = array3[num11] && !array4[num11];
			}
			int num12 = Components(size, array5, null).Count((int a) => (float)a * km2 >= 0.1f);
			Biome biome = gen.GetBiome(0f, 0f, 0.02f, false);
			Color val = default(Color);
			float biomeHeight = gen.GetBiomeHeight(biome, 0f, 0f, ref val, false, true);
			int num13 = gen.GetRivers()?.Count ?? 0;
			int num14 = gen.GetLakes()?.Count ?? 0;
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.Append('{');
			stringBuilder.Append("\"land\":" + Pct(num4, num3) + ",");
			stringBuilder.Append($"\"continents\":{num8},\"islands\":{num9},\"largestKm2\":{F(v)},\"landKm2\":{F((float)num4 * km2)},");
			stringBuilder.Append($"\"inlandWaters\":{num12},\"rivers\":{num13},\"lakePoints\":{num14},");
			stringBuilder.Append(string.Format("\"center\":{{\"biome\":\"{0}\",\"height\":{1},\"ok\":{2}}},", biome, F(biomeHeight), ((int)biome == 1 && biomeHeight >= 30f) ? "true" : "false"));
			stringBuilder.Append("\"biomes\":{");
			bool flag = true;
			Biome[] array6 = s_biomeOrder;
			foreach (Biome val2 in array6)
			{
				dictionary.TryGetValue(val2, out var value2);
				if (value2 != 0)
				{
					if (!flag)
					{
						stringBuilder.Append(',');
					}
					flag = false;
					stringBuilder.Append($"\"{val2}\":{Pct(value2, num4)}");
				}
			}
			stringBuilder.Append("}}");
			string summary = $"Land {Pct(num4, num3)}%, continents {num8}, islets {num9}, inland waters {num12}, rivers {num13}, center {biome}.";
			return new MapData
			{
				Pixels = pixels,
				StatsJson = stringBuilder.ToString(),
				Summary = summary
			};
		}

		private static List<int> Components(int size, bool[] mask, bool[] seen)
		{
			seen = seen ?? new bool[mask.Length];
			List<int> list = new List<int>();
			Stack<int> stack = new Stack<int>();
			for (int i = 0; i < mask.Length; i++)
			{
				if (mask[i] && !seen[i])
				{
					int num = 0;
					seen[i] = true;
					stack.Push(i);
					while (stack.Count > 0)
					{
						int num2 = stack.Pop();
						num++;
						int num3 = num2 / size;
						int num4 = num2 % size;
						Visit((num4 > 0) ? (num2 - 1) : (-1));
						Visit((num4 < size - 1) ? (num2 + 1) : (-1));
						Visit((num3 > 0) ? (num2 - size) : (-1));
						Visit((num3 < size - 1) ? (num2 + size) : (-1));
					}
					list.Add(num);
				}
			}
			return list;
			void Visit(int j)
			{
				if (j >= 0 && mask[j] && !seen[j])
				{
					seen[j] = true;
					stack.Push(j);
				}
			}
		}

		private static void FloodFromEdge(int size, float m, bool[] water, bool[] reached)
		{
			float num = 10500f;
			Stack<int> stack = new Stack<int>();
			for (int i = 0; i < size; i++)
			{
				float num2 = ((float)i + 0.5f) * m - num;
				for (int j = 0; j < size; j++)
				{
					float num3 = ((float)j + 0.5f) * m - num;
					int num4 = i * size + j;
					float num5 = Mathf.Sqrt(num3 * num3 + num2 * num2);
					if (water[num4] && num5 > 10000f - 2f * m && !reached[num4])
					{
						reached[num4] = true;
						stack.Push(num4);
					}
				}
			}
			while (stack.Count > 0)
			{
				int num6 = stack.Pop();
				int num7 = num6 / size;
				int num8 = num6 % size;
				Push((num8 > 0) ? (num6 - 1) : (-1));
				Push((num8 < size - 1) ? (num6 + 1) : (-1));
				Push((num7 > 0) ? (num6 - size) : (-1));
				Push((num7 < size - 1) ? (num6 + size) : (-1));
			}
			void Push(int num9)
			{
				if (num9 >= 0 && water[num9] && !reached[num9])
				{
					reached[num9] = true;
					stack.Push(num9);
				}
			}
		}

		private static string Pct(int part, int whole)
		{
			return F((whole > 0) ? (100f * (float)part / (float)whole) : 0f);
		}

		private static string F(float v)
		{
			return v.ToString("0.##", CultureInfo.InvariantCulture);
		}

		private static string Str(string s)
		{
			StringBuilder stringBuilder = new StringBuilder("\"");
			string text = s ?? string.Empty;
			foreach (char c in text)
			{
				if (c == '"' || c == '\\')
				{
					stringBuilder.Append('\\').Append(c);
				}
				else if (c < ' ')
				{
					stringBuilder.Append($"\\u{(int)c:x4}");
				}
				else
				{
					stringBuilder.Append(c);
				}
			}
			return stringBuilder.Append('"').ToString();
		}

		private static void WriteJson(string name, string seed, int gen, string mode, ContinentShape shape, ShapeParams custom, int size, double pregen, double render, string stats, List<Marker> markers, List<LocationCheck.Row> rows, WorldGenerator wg)
		{
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.Append('{');
			stringBuilder.Append(string.Format("\"file\":{0},\"name\":{1},\"seed\":{2},\"gen\":{3},\"mode\":{4},", Str(name + ".png"), Str(name), Str(seed), gen, Str(mode)));
			stringBuilder.Append("\"made\":" + Str(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture)) + ",\"plugin\":" + Str("1.0.0") + ",");
			stringBuilder.Append($"\"size\":{size},\"span\":{F(21000f)},\"seconds\":{{\"layout\":{F((float)pregen)},\"draw\":{F((float)render)}}},");
			stringBuilder.Append("\"params\":" + Str((shape == null) ? string.Empty : ((custom != null) ? custom.Describe(ShapeParams.ForVersion(1)) : string.Empty)) + ",");
			stringBuilder.Append("\"stats\":").Append(stats).Append(',');
			stringBuilder.Append("\"rivers\":[");
			List<River> rivers = wg.GetRivers();
			if (rivers != null)
			{
				for (int i = 0; i < rivers.Count; i++)
				{
					River val = rivers[i];
					stringBuilder.Append((i > 0) ? "," : string.Empty).Append("[" + F(val.p0.x) + "," + F(val.p0.y) + "," + F(val.p1.x) + "," + F(val.p1.y) + "]");
				}
			}
			stringBuilder.Append("],\"markers\":[");
			if (markers != null)
			{
				for (int j = 0; j < markers.Count; j++)
				{
					Marker marker = markers[j];
					stringBuilder.Append((j > 0) ? "," : string.Empty).Append("{\"n\":" + Str(marker.Name) + ",\"x\":" + F(marker.X) + ",\"z\":" + F(marker.Z) + ",\"key\":" + (marker.Key ? "true" : "false") + "}");
				}
			}
			stringBuilder.Append("],\"locations\":");
			if (rows == null)
			{
				stringBuilder.Append("null");
			}
			else
			{
				stringBuilder.Append('[');
				for (int k = 0; k < rows.Count; k++)
				{
					LocationCheck.Row row = rows[k];
					stringBuilder.Append((k > 0) ? "," : string.Empty).Append(string.Format("{{\"n\":{0},\"placed\":{1},\"wanted\":{2},\"key\":{3}}}", Str(row.Name), row.Placed, row.Wanted, row.Key ? "true" : "false"));
				}
				stringBuilder.Append(']');
			}
			stringBuilder.Append('}');
			File.WriteAllText(Path.Combine(Folder, name + ".json"), stringBuilder.ToString(), new UTF8Encoding(encoderShouldEmitUTF8Identifier: false));
		}

		internal static void WriteIndex()
		{
			Directory.CreateDirectory(Folder);
			List<FileInfo> list = (from f in new DirectoryInfo(Folder).GetFiles("*.json")
				orderby f.LastWriteTimeUtc descending
				select f).ToList();
			StringBuilder stringBuilder = new StringBuilder("window.G3C_MAPS = [\n");
			for (int num = 0; num < list.Count; num++)
			{
				stringBuilder.Append(File.ReadAllText(list[num].FullName, Encoding.UTF8)).Append((num < list.Count - 1) ? ",\n" : "\n");
			}
			stringBuilder.Append("];\n");
			File.WriteAllText(Path.Combine(Folder, "maps.js"), stringBuilder.ToString(), new UTF8Encoding(encoderShouldEmitUTF8Identifier: false));
			using Stream stream = typeof(Preview).Assembly.GetManifestResourceStream("G3Continents.viewer.html");
			using StreamReader streamReader = new StreamReader(stream, Encoding.UTF8);
			File.WriteAllText(ViewerPath, streamReader.ReadToEnd(), new UTF8Encoding(encoderShouldEmitUTF8Identifier: false));
		}

		static Preview()
		{
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e6: 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_0121: Unknown result type (might be due to invalid IL or missing references)
			Biome[] array = new Biome[9];
			RuntimeHelpers.InitializeArray(array, (RuntimeFieldHandle)/*OpCode not supported: LdMemberToken*/);
			s_biomeOrder = (Biome[])(object)array;
		}
	}
	[HarmonyPatch]
	internal static class WorldGenPatch
	{
		private sealed class Holder
		{
			public ContinentShape Shape;
		}

		private sealed class Last
		{
			public WorldGenerator Gen;

			public ContinentShape Shape;
		}

		public const int GenStep = 1000;

		public const int CurrentGen = 1;

		private static readonly ConditionalWeakTable<WorldGenerator, Holder> s_shapes = new ConditionalWeakTable<WorldGenerator, Holder>();

		private static Last s_last = new Last();

		internal static readonly ConditionalWeakTable<World, ShapeParams> PreviewParams = new ConditionalWeakTable<World, ShapeParams>();

		[ThreadStatic]
		private static WorldGenerator t_lastGen;

		[ThreadStatic]
		private static float t_lastX;

		[ThreadStatic]
		private static float t_lastY;

		[ThreadStatic]
		private static float t_lastH;

		private static readonly FieldRef<WorldGenerator, float> s_offset0 = AccessTools.FieldRefAccess<WorldGenerator, float>("m_offset0");

		private static readonly FieldRef<WorldGenerator, float> s_offset1 = AccessTools.FieldRefAccess<WorldGenerator, float>("m_offset1");

		private static readonly FieldRef<WorldGenerator, float> s_offset2 = AccessTools.FieldRefAccess<WorldGenerator, float>("m_offset2");

		private static readonly FieldRef<WorldGenerator, float> s_offset4 = AccessTools.FieldRefAccess<WorldGenerator, float>("m_offset4");

		private static readonly FieldRef<WorldGenerator, float> s_maxMarsh = AccessTools.FieldRefAccess<WorldGenerator, float>("maxMarshDistance");

		private static readonly FieldRef<WorldGenerator, float> s_minDarkland = AccessTools.FieldRefAccess<WorldGenerator, float>("minDarklandNoise");

		private static readonly Func<WorldGenerator, float, float, bool, float> s_baseHeight = AccessTools.MethodDelegate<Func<WorldGenerator, float, float, bool, float>>(AccessTools.Method(typeof(WorldGenerator), "GetBaseHeight", (Type[])null, (Type[])null), (object)null, true);

		internal static int GenOf(World world)
		{
			if (world != null && !world.m_menu)
			{
				return world.m_worldGenVersion / 1000;
			}
			return 0;
		}

		internal static ContinentShape ShapeOf(WorldGenerator gen)
		{
			Last last = s_last;
			if (last.Gen == gen)
			{
				return last.Shape;
			}
			ContinentShape shape = s_shapes.GetValue(gen, Create).Shape;
			s_last = new Last
			{
				Gen = gen,
				Shape = shape
			};
			return shape;
		}

		private static Holder Create(WorldGenerator gen)
		{
			Holder holder = new Holder();
			World world = gen.m_world;
			int num = GenOf(world);
			if (num <= 0)
			{
				return holder;
			}
			ShapeParams value;
			ShapeParams shapeParams = (PreviewParams.TryGetValue(world, out value) ? value : ShapeParams.ForVersion(num));
			if (shapeParams == null)
			{
				Plugin.Log.LogError((object)$"World '{world.m_name}' was made by G3 Continents generator {num}, which this build does not know: update G3 Continents. The terrain will not match the world.");
				return holder;
			}
			if (Compat.HasConflict && world.m_name != "g3c_preview")
			{
				Plugin.Log.LogError((object)("World '" + world.m_name + "' has continents, but another world generation mod is installed (" + string.Join(", ", Compat.Conflicts) + "): the terrain may come out wrong. Remove that mod to play this world."));
			}
			holder.Shape = new ContinentShape(world.m_seed, num, shapeParams);
			return holder;
		}

		[HarmonyPatch(typeof(WorldGenerator), "GetBaseHeight")]
		[HarmonyPrefix]
		private static bool BaseHeightCached(WorldGenerator __instance, float wx, float wy, bool menuTerrain, ref float __result, out bool __state)
		{
			__state = !menuTerrain && t_lastGen == __instance && t_lastX == wx && t_lastY == wy && t_lastGen != null;
			if (__state)
			{
				__result = t_lastH;
				return false;
			}
			return true;
		}

		[HarmonyPatch(typeof(WorldGenerator), "GetBaseHeight")]
		[HarmonyPostfix]
		private static void BaseHeight(WorldGenerator __instance, float wx, float wy, bool menuTerrain, ref float __result, bool __state)
		{
			if (!(menuTerrain || __state))
			{
				ContinentShape continentShape = ShapeOf(__instance);
				if (continentShape != null)
				{
					__result = continentShape.Apply(wx, wy, __result);
					t_lastGen = __instance;
					t_lastX = wx;
					t_lastY = wy;
					t_lastH = __result;
				}
			}
		}

		[HarmonyPatch(typeof(WorldGenerator), "GetBiome", new Type[]
		{
			typeof(float),
			typeof(float),
			typeof(float),
			typeof(bool)
		})]
		[HarmonyPrefix]
		private static bool Biome(WorldGenerator __instance, float wx, float wy, float oceanLevel, bool waterAlwaysOcean, ref Biome __result)
		{
			if (__instance.m_world.m_menu)
			{
				return true;
			}
			ContinentShape continentShape = ShapeOf(__instance);
			if (continentShape == null)
			{
				return true;
			}
			float num = s_baseHeight(__instance, wx, wy, arg4: false);
			if (waterAlwaysOcean && __instance.GetHeight(wx, wy) <= oceanLevel)
			{
				__result = (Biome)256;
				return false;
			}
			if (WorldGenerator.IsAshlands(wx, wy))
			{
				__result = (Biome)32;
				return false;
			}
			if (!waterAlwaysOcean && num <= oceanLevel)
			{
				__result = (Biome)256;
				return false;
			}
			if (WorldGenerator.IsDeepnorth(wx, wy))
			{
				__result = (Biome)64;
				return false;
			}
			if (num > 0.4f)
			{
				__result = (Biome)4;
				return false;
			}
			continentShape.BiomePoint(wx, wy, out var bx, out var by);
			float num2 = DUtils.Length(bx, by);
			float num3 = continentShape.RingWobble(bx, by);
			float num4 = s_offset0.Invoke(__instance);
			float num5 = s_offset1.Invoke(__instance);
			float num6 = s_offset2.Invoke(__instance);
			float num7 = s_offset4.Invoke(__instance);
			if (DUtils.PerlinNoise((double)(float)((double)num4 + (double)bx) * 0.0010000000474974513, (double)(float)((double)num4 + (double)by) * 0.0010000000474974513) > 0.6f && num2 > 2000f && num2 < s_maxMarsh.Invoke(__instance) && num > 0.05f && num < 0.25f)
			{
				__result = (Biome)2;
			}
			else if (DUtils.PerlinNoise((double)(float)((double)num7 + (double)bx) * 0.0010000000474974513, (double)(float)((double)num7 + (double)by) * 0.0010000000474974513) > s_minDarkland.Invoke(__instance) && num2 > 6000f + num3 && num2 < 10000f)
			{
				__result = (Biome)512;
			}
			else if (DUtils.PerlinNoise((double)(float)((double)num5 + (double)bx) * 0.0010000000474974513, (double)(float)((double)num5 + (double)by) * 0.0010000000474974513) > 0.4f && num2 > 3000f + num3 && num2 < 8000f)
			{
				__result = (Biome)16;
			}
			else if (DUtils.PerlinNoise((double)(float)((double)num6 + (double)bx) * 0.0010000000474974513, (double)(float)((double)num6 + (double)by) * 0.0010000000474974513) > 0.4f && num2 > 600f + num3 && num2 < 6000f)
			{
				__result = (Biome)8;
			}
			else
			{
				__result = (Biome)((!(num2 > 5000f + num3)) ? 1 : 8);
			}
			return false;
		}

		[HarmonyPatch(typeof(WorldGenerator), "VersionSetup")]
		[HarmonyPrefix]
		private static void VanillaVersion(ref int version)
		{
			if (version >= 1000)
			{
				version %= 1000;
			}
		}

		[HarmonyPatch(/*Could not decode attribute arguments.*/)]
		[HarmonyPostfix]
		private static void NewWorld(World __instance, string name)
		{
			if (Plugin.NewWorlds == null || !Plugin.NewWorlds.Value)
			{
				return;
			}
			switch (name)
			{
			case "editor":
				return;
			case "g3c_preview":
				return;
			}
			if (Compat.HasConflict)
			{
				Plugin.Log.LogWarning((object)("New world '" + name + "' stays vanilla: another world generation mod is installed (" + string.Join(", ", Compat.Conflicts) + ")."));
			}
			else
			{
				__instance.m_worldGenVersion = 1000 + __instance.m_worldGenVersion % 1000;
				Plugin.Log.LogInfo((object)$"New world '{name}' (seed '{__instance.m_seedName}') gets continents, generator {1}.");
			}
		}
	}
}