Decompiled source of Riverheim v1.3.1

BepInEx/plugins/Riverheim.dll

Decompiled 14 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using BepInEx;
using BepInEx.Configuration;
using DelaunatorSharp;
using Microsoft.CodeAnalysis;
using Riverheim.Configuration;
using Riverheim.Configuration.Extensions;
using Riverheim.Pipeline;
using Riverheim.Pipeline.Extensions;
using Riverheim.Rendering;
using Riverheim.Rendering.Biomes;
using Riverheim.Rendering.Rivers;
using Riverheim.Rendering.Splats;
using Riverheim.Util;
using Riverheim.Util.Debug;
using Riverheim.Util.Geometry;
using Riverheim.Util.Grids;
using Riverheim.Util.Interpolation;
using Riverheim.Util.Mathematics;
using Riverheim.Util.Random;
using Riverheim.Util.Random.Noise;
using Riverheim.Util.Random.Noise.Primitives;
using Riverheim.Util.Random.Primitives;
using Riverheim.Util.Splines;
using Riverheim.Util.Tilings;
using Riverheim.Util.Tilings.Containers;
using Riverheim.Util.Tilings.Internal;
using Riverheim.Util.Tilings.Processing;
using Riverheim.World;
using Riverheim.World.Biomes;
using Riverheim.World.Biomes.Competitive;
using Riverheim.World.Height;
using Riverheim.World.Height.Mountains;
using Riverheim.World.Landmass;
using Riverheim.World.Rivers;
using Riverheim.World.Splats;
using Riverheim.World.Tilings;
using Worldhalla.API;
using Worldhalla.API.Attributes;
using Worldhalla.API.Settings;
using Worldhalla.API.Settings.Controls;
using Worldhalla.API.World;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: IgnoresAccessChecksTo("assembly_utils")]
[assembly: IgnoresAccessChecksTo("assembly_valheim")]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("Riverheim")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Comprehensive terrain generator for Valheim")]
[assembly: AssemblyFileVersion("1.3.1.0")]
[assembly: AssemblyInformationalVersion("1.3.1+7a361b24ebc146c5637b40c4d8f11413f4e8db1c")]
[assembly: AssemblyProduct("Riverheim")]
[assembly: AssemblyTitle("Riverheim")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.3.1.0")]
[module: UnverifiableCode]
[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;
		}
	}
}
internal static class PluginInfo
{
	public const string PLUGIN_GUID = "Riverheim";

	public const string PLUGIN_NAME = "Riverheim";

	public const string PLUGIN_VERSION = "1.3.1";
}
namespace Riverheim.Plugin
{
	[BepInPlugin("dev.gurebu.riverheim.stable", "Riverheim", "1.3.1")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class RiverheimPlugin : BaseUnityPlugin
	{
		private const string WORLDHALLA_GUID = "dev.gurebu.worldhalla";

		private const string GUID = "dev.gurebu.riverheim.stable";

		private const string PLUGIN_NAME = "Riverheim";

		public static ConfigEntry<bool> ParallelGeneration;

		public static ConfigEntry<bool> PipelineCaching;

		public void Awake()
		{
			ParallelGeneration = ((BaseUnityPlugin)this).Config.Bind<bool>("Performance", "Parallel generation", true, "Whether world generation should compute across multiple threads in parallel. Disabling this makes generation slower but easier to debug.");
			PipelineCaching = ((BaseUnityPlugin)this).Config.Bind<bool>("Performance", "Pipeline caching", false, "Significantly increases memory usage to cache generation intermediates. Doesn't speed up generation unless you're using another mod to reload");
			Logger.SetLogger(delegate(Logger.LogLevel level, object message)
			{
				switch (level)
				{
				case Logger.LogLevel.Info:
					((BaseUnityPlugin)this).Logger.LogInfo(message);
					break;
				case Logger.LogLevel.Warning:
					((BaseUnityPlugin)this).Logger.LogWarning(message);
					break;
				case Logger.LogLevel.Error:
					((BaseUnityPlugin)this).Logger.LogError(message);
					break;
				default:
					throw new ArgumentOutOfRangeException("level", level, null);
				}
			});
		}
	}
}
namespace Riverheim.Plugin.Worldhalla
{
	public class SettingsFormImpl : ISettingsForm
	{
		private readonly WorldSettings current;

		private readonly List<string> presets;

		private readonly Select preset;

		public IReadOnlyList<Control> Controls { get; }

		public SettingsFormImpl(WorldSettings current)
		{
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c2: Expected O, but got Unknown
			this.current = current;
			List<ConfigManager.PresetInfo> source = (from info in ConfigManager.GetAvailablePresets()
				where !info.Hidden || info.Id == current.Preset
				select info).ToList();
			presets = source.Select((ConfigManager.PresetInfo info) => info.Id).ToList();
			preset = new Select("Preset", (IReadOnlyList<string>)source.Select((ConfigManager.PresetInfo info) => info.DisplayName).ToList(), Math.Max(0, presets.IndexOf(current.Preset)), (string)null);
			Controls = new <>z__ReadOnlyArray<Control>((Control[])(object)new Control[1] { (Control)preset });
		}

		public ISettings Build()
		{
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			string text = presets[preset.Selected];
			if (text == current.Preset)
			{
				return (ISettings)(object)new SettingsImpl(current);
			}
			if (!ConfigManager.TryGetLatestSettings(text, out var settings))
			{
				throw new SettingsException("Unknown preset '" + text + "'");
			}
			return (ISettings)(object)new SettingsImpl(settings);
		}
	}
	public class SettingsImpl(WorldSettings settings) : ISettings
	{
		public readonly WorldSettings settings = settings;

		public byte[] SaveToBinaryBlob()
		{
			return WorldSettingSerialization.Serialize(settings);
		}

		public string Summary()
		{
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.AppendLine("Preset: " + (ConfigManager.GetAvailablePresets().FirstOrDefault((ConfigManager.PresetInfo info) => info.Id == settings.Preset).DisplayName ?? settings.Preset));
			return stringBuilder.ToString();
		}
	}
	[WorldhallaWorldGenerator("dev.gurebu.riverheim")]
	public class WorldGeneratorImpl : IWorldGenerator, ISettingsHandler
	{
		private const string GUID = "dev.gurebu.riverheim";

		public string Moniker => "RH";

		public string Name => "Riverheim";

		public string Description => "Worlds with larger continents and terrain based on sprawling river networks.";

		public string Version => "1.3.1";

		public Feature[] Features => (Feature[])(object)new Feature[1];

		public ISettings DefaultSettings => (ISettings)(object)new SettingsImpl(ConfigManager.DefaultWorldSettings());

		public string CommandLineUsage => "preset <name>";

		public ISettings BuildSettingsFromBinaryBlob(byte[] data)
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				return (ISettings)(object)new SettingsImpl(WorldSettingSerialization.Deserialize(data));
			}
			catch (WorldSettingSerialization.Error error)
			{
				throw new SettingsException(error.Message);
			}
		}

		public ISettings BuildSettingsFromCommandLine(string settings)
		{
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			string[] array = settings.Split(' ', StringSplitOptions.RemoveEmptyEntries);
			if (array.Length == 0)
			{
				return DefaultSettings;
			}
			if (array.Length != 2 || array[0] != "preset")
			{
				throw new SettingsException("Expected 'preset <name>', got '" + settings + "'");
			}
			if (!ConfigManager.TryGetLatestSettings(array[1], out var settings2))
			{
				throw new SettingsException("Unknown preset '" + array[1] + "'");
			}
			return (ISettings)(object)new SettingsImpl(settings2);
		}

		public ISettingsForm CreateForm(ISettings current)
		{
			return (ISettingsForm)(object)new SettingsFormImpl(((SettingsImpl)(object)current).settings);
		}

		public IWorldState Generate(int seed, ISettings settings, IProgress<float> progress, CancellationToken cancellation)
		{
			GeneratorPipeline pipeline = BuildPipeline();
			WorldSettings settings2 = (settings as SettingsImpl).settings;
			pipeline.TaskCompleted += delegate(GeneratorPipeline.TaskCompletionEvent evt)
			{
				progress.Report((float)evt.progress / (float)pipeline.Length);
			};
			Logger.LogInfo($"Generating world seed {seed} with preset: '{settings2.Preset}' v.{settings2.PresetVersion}...");
			pipeline.Compute(seed, ConfigManager.GetConfig(settings2), cancellation);
			return (IWorldState)(object)new WorldStateImpl(pipeline.GetResult<WorldRenderer>("Rendering/WorldRenderer"));
		}

		private static GeneratorPipeline BuildPipeline()
		{
			string[] resultResourceIds = new string[1] { "Rendering/WorldRenderer" };
			GeneratorPipeline pipeline = new GeneratorPipeline.Builder(resultResourceIds).SetParallel(RiverheimPlugin.ParallelGeneration.Value).SetClearUnneededCache(!RiverheimPlugin.PipelineCaching.Value).Build();
			pipeline.PipelineCompleted += delegate(GeneratorPipeline.PipelineCompletionEvent evt)
			{
				Logger.LogInfo($"World generation completed in {evt.duration.TotalMilliseconds:0.}ms");
			};
			pipeline.TaskCompleted += delegate(GeneratorPipeline.TaskCompletionEvent evt)
			{
				string text = "";
				string text2 = string.Join(", ", evt.resources);
				string text3 = evt.duration.TotalMilliseconds.ToString("0.");
				Logger.LogInfo(evt.cached ? $"[{evt.progress} / {pipeline.Length}] {text2} retrieved from cache" : $"[{evt.progress} / {pipeline.Length}] {text2} computed in {text3}ms {text}");
			};
			return pipeline;
		}
	}
	public class WorldStateImpl(WorldRenderer renderer) : IWorldState
	{
		private static readonly Biome[] Biomes = BuildBiomes();

		private static Biome[] BuildBiomes()
		{
			int num = 0;
			foreach (Biome value in Enum.GetValues(typeof(Biome)))
			{
				if ((int)value > num)
				{
					num = (int)value;
				}
			}
			Biome[] array = new Biome[num + 1];
			array[2] = (Biome)1;
			array[3] = (Biome)1;
			array[5] = (Biome)2;
			array[6] = (Biome)4;
			array[4] = (Biome)8;
			array[13] = (Biome)8;
			array[7] = (Biome)16;
			array[10] = (Biome)32;
			array[11] = (Biome)32;
			array[12] = (Biome)64;
			array[1] = (Biome)256;
			array[8] = (Biome)512;
			array[9] = (Biome)512;
			return (Biome[])(object)array;
		}

		private static Biome BiomeConv(Biome biome)
		{
			return Biomes[(int)biome];
		}

		public Biome GetBiome(float x, float y)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			return BiomeConv(renderer.GetBiome(new Rfloat2(x, y)));
		}

		public float GetForestation(float x, float y)
		{
			return (float)renderer.GetForestation(new Rfloat2(x, y));
		}

		public PointData RenderPoint(float x, float y, Biome biome)
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			IWorldGen.PointData pointData = renderer.RenderPoint(new Rfloat2(x, y));
			return new PointData
			{
				biome = BiomeConv(pointData.biome),
				height = (float)pointData.height,
				dirt = (float)pointData.dirt,
				fertility = (float)pointData.fertility,
				lava = (float)pointData.lava,
				pave = (float)pointData.pave,
				snow = (float)pointData.snow
			};
		}
	}
}
namespace System.Runtime.CompilerServices
{
	[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
	internal sealed class IgnoresAccessChecksToAttribute : Attribute
	{
		public IgnoresAccessChecksToAttribute(string assemblyName)
		{
		}
	}
}
internal sealed class <>z__ReadOnlyArray<T> : IEnumerable, IEnumerable<T>, IReadOnlyCollection<T>, IReadOnlyList<T>, ICollection<T>, IList<T>
{
	int IReadOnlyCollection<T>.Count => _items.Length;

	T IReadOnlyList<T>.this[int index] => _items[index];

	int ICollection<T>.Count => _items.Length;

	bool ICollection<T>.IsReadOnly => true;

	T IList<T>.this[int index]
	{
		get
		{
			return _items[index];
		}
		set
		{
			throw new NotSupportedException();
		}
	}

	public <>z__ReadOnlyArray(T[] items)
	{
		_items = items;
	}

	IEnumerator IEnumerable.GetEnumerator()
	{
		return ((IEnumerable)_items).GetEnumerator();
	}

	IEnumerator<T> IEnumerable<T>.GetEnumerator()
	{
		return ((IEnumerable<T>)_items).GetEnumerator();
	}

	void ICollection<T>.Add(T item)
	{
		throw new NotSupportedException();
	}

	void ICollection<T>.Clear()
	{
		throw new NotSupportedException();
	}

	bool ICollection<T>.Contains(T item)
	{
		return ((ICollection<T>)_items).Contains(item);
	}

	void ICollection<T>.CopyTo(T[] array, int arrayIndex)
	{
		((ICollection<T>)_items).CopyTo(array, arrayIndex);
	}

	bool ICollection<T>.Remove(T item)
	{
		throw new NotSupportedException();
	}

	int IList<T>.IndexOf(T item)
	{
		return ((IList<T>)_items).IndexOf(item);
	}

	void IList<T>.Insert(int index, T item)
	{
		throw new NotSupportedException();
	}

	void IList<T>.RemoveAt(int index)
	{
		throw new NotSupportedException();
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class IsUnmanagedAttribute : Attribute
	{
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
}
namespace Riverheim
{
	internal static class DeterministicMath
	{
		private const short INLINE = 256;

		public const double PI = Math.PI;

		public const double RELATIVE_ERROR = 1E-10;

		private const double TWO_PI = Math.PI * 2.0;

		private const double HALF_PI = Math.PI / 2.0;

		private const double EIGHTH_OF_PI = Math.PI / 8.0;

		private const double TAN_PI_OVER_8 = 0.4142135623730951;

		private const double SQRT2 = 1.4142135623730951;

		private const double LN2 = 0.6931471805599453;

		private const double LN2_INV = 1.4426950408889634;

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		private static double GuardNaN(double x)
		{
			throw new ArgumentOutOfRangeException($"Function input outside range: {x}");
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static double Abs(double x)
		{
			return BitConverter.Int64BitsToDouble(BitConverter.DoubleToInt64Bits(x) & 0x7FFFFFFFFFFFFFFFL);
		}

		public static double Sqrt(double x)
		{
			if ((x < 0.0 || double.IsNaN(x)) ? true : false)
			{
				return GuardNaN(x);
			}
			if (x == 0.0 || double.IsPositiveInfinity(x))
			{
				return x;
			}
			return x * InvSqrt(x);
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		private static double InvSqrt(double x)
		{
			double num = 0.5 * x;
			long num2 = BitConverter.DoubleToInt64Bits(x);
			num2 = 6910469410427058089L - (num2 >> 1);
			double num3 = BitConverter.Int64BitsToDouble(num2);
			num3 *= 1.5 - num * num3 * num3;
			num3 *= 1.5 - num * num3 * num3;
			return num3 * (1.5 - num * num3 * num3);
		}

		public static double Exp(double x)
		{
			if (double.IsNaN(x))
			{
				return GuardNaN(x);
			}
			if (double.IsNegativeInfinity(x) || x < -745.133)
			{
				return 0.0;
			}
			if (double.IsPositiveInfinity(x) || x > 709.782)
			{
				return double.PositiveInfinity;
			}
			double num = x * 1.4426950408889634;
			long num2 = (long)(num + ((num >= 0.0) ? 0.5 : (-0.5)));
			double num3 = x - (double)num2 * 0.6931471805599453;
			return (1.0 + num3 * (1.0 + num3 * (0.5 + num3 * (1.0 / 6.0 + num3 * (1.0 / 24.0 + num3 * (1.0 / 120.0 + num3 * (1.0 / 720.0 + num3 * (0.0001984126984126984 + num3 * (2.48015873015873E-05 + num3 * 2.7557319223985893E-06))))))))) * BitConverter.Int64BitsToDouble(num2 + 1023 << 52);
		}

		public static double Log(double x)
		{
			if (double.IsNaN(x) || x < 0.0)
			{
				return GuardNaN(x);
			}
			if (x == 0.0)
			{
				return double.NegativeInfinity;
			}
			int num = 0;
			if (x < 2.2250738585072014E-308)
			{
				x *= 4503599627370496.0;
				num = -52;
			}
			long num2 = BitConverter.DoubleToInt64Bits(x);
			int num3 = (int)((num2 >> 52) & 0x7FF) - 1023 + num;
			double num4 = BitConverter.Int64BitsToDouble((num2 & 0xFFFFFFFFFFFFFL) | 0x3FF0000000000000L);
			if (num4 > 1.4142135623730951)
			{
				num4 *= 0.5;
				num3++;
			}
			double num5 = (num4 - 1.0) / (num4 + 1.0);
			double num6 = num5 * num5;
			return 2.0 * num5 * (1.0 + num6 * (1.0 / 3.0 + num6 * (0.2 + num6 * (1.0 / 7.0 + num6 * (1.0 / 9.0 + num6 * (1.0 / 11.0)))))) + (double)num3 * 0.6931471805599453;
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		private static double Pow(double x, long p)
		{
			bool flag = p < 0;
			if (flag)
			{
				p = -p;
			}
			double num = 1.0;
			double num2 = x;
			while (p > 0)
			{
				if ((p & 1) != 0L)
				{
					num *= num2;
				}
				num2 *= num2;
				p >>= 1;
			}
			if (!flag)
			{
				return num;
			}
			return 1.0 / num;
		}

		public static double Pow(double x, int p)
		{
			if (p == 0)
			{
				return 1.0;
			}
			if (x == 0.0)
			{
				if (p <= 0)
				{
					return double.PositiveInfinity;
				}
				return 0.0;
			}
			return Pow(x, (long)p);
		}

		public static double Pow(double x, double p)
		{
			if (x < 0.0)
			{
				return GuardNaN(x);
			}
			if (p == 0.0)
			{
				return 1.0;
			}
			if (x == 0.0)
			{
				if (!(p > 0.0))
				{
					return double.PositiveInfinity;
				}
				return 0.0;
			}
			long num = (long)p;
			if (p == (double)num)
			{
				return Pow(x, num);
			}
			if (p == 0.5)
			{
				return Sqrt(x);
			}
			return Exp(p * Log(x));
		}

		public static double Sin(double x)
		{
			if (!double.IsFinite(x))
			{
				return GuardNaN(x);
			}
			x %= Math.PI * 2.0;
			if (x > Math.PI)
			{
				x -= Math.PI * 2.0;
			}
			else if (x < -Math.PI)
			{
				x += Math.PI * 2.0;
			}
			if (x > Math.PI / 2.0)
			{
				x = Math.PI - x;
			}
			else if (x < -Math.PI / 2.0)
			{
				x = -Math.PI - x;
			}
			double num = x * x;
			return x * (1.0 - num * (1.0 / 6.0 - num * (1.0 / 120.0 - num * (0.0001984126984126984 - num * (2.7557319223985893E-06 - num * (2.505210838544172E-08 - num * (1.6059043836821613E-10 - num * 7.647163731819816E-13)))))));
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static double Cos(double x)
		{
			return Sin(x + Math.PI / 2.0);
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static double Tan(double x)
		{
			return Sin(x) / Cos(x);
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static double Asin(double x)
		{
			if (x < -1.0 || x > 1.0)
			{
				return GuardNaN(x);
			}
			return Atan2(x, Sqrt(1.0 - x * x));
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static double Acos(double x)
		{
			if (x < -1.0 || x > 1.0)
			{
				return GuardNaN(x);
			}
			return Atan2(Sqrt(1.0 - x * x), x);
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static double Atan(double x)
		{
			if (!double.IsFinite(x))
			{
				return GuardNaN(x);
			}
			bool num = x < 0.0;
			x = Abs(x);
			bool num2 = x > 1.0;
			if (num2)
			{
				x = 1.0 / x;
			}
			bool num3 = x > 0.4142135623730951;
			if (num3)
			{
				x = (x - 0.4142135623730951) / (1.0 + x * 0.4142135623730951);
			}
			double num4 = x * x;
			double num5 = x * (1.0 - num4 * (1.0 / 3.0 - num4 * (0.2 - num4 * (1.0 / 7.0 - num4 * (1.0 / 9.0 - num4 * (1.0 / 11.0 - num4 * (1.0 / 13.0 - num4 * (1.0 / 15.0 - num4 * (1.0 / 17.0 - num4 * (1.0 / 19.0 - num4 * (1.0 / 21.0 - num4 * (1.0 / 23.0))))))))))));
			if (num3)
			{
				num5 += Math.PI / 8.0;
			}
			if (num2)
			{
				num5 = Math.PI / 2.0 - num5;
			}
			if (num)
			{
				num5 = 0.0 - num5;
			}
			return num5;
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static double Atan2(double y, double x)
		{
			if (x > 0.0)
			{
				return Atan(y / x);
			}
			if (x < 0.0)
			{
				if (!(y >= 0.0))
				{
					return Atan(y / x) - Math.PI;
				}
				return Atan(y / x) + Math.PI;
			}
			if (y > 0.0)
			{
				return Math.PI / 2.0;
			}
			if (y < 0.0)
			{
				return -Math.PI / 2.0;
			}
			return 0.0;
		}
	}
	public readonly struct InertFloat
	{
		private readonly float value;

		private InertFloat(float value)
		{
			this.value = value;
		}

		public static implicit operator double(InertFloat f)
		{
			return f.value;
		}

		public static implicit operator InertFloat(double f)
		{
			return new InertFloat((float)f);
		}

		public override string ToString()
		{
			return $"{value}";
		}
	}
	public readonly struct InertFloat2
	{
		private readonly float x;

		private readonly float y;

		private InertFloat2(float x, float y)
		{
			this.x = x;
			this.y = y;
		}

		public static implicit operator Rfloat2(InertFloat2 f2)
		{
			return new Rfloat2(f2.x, f2.y);
		}

		public static implicit operator InertFloat2(Rfloat2 f2)
		{
			return new InertFloat2((float)f2.x, (float)f2.y);
		}

		public override string ToString()
		{
			return $"({x}, {y})";
		}
	}
	public readonly struct Rfloat2 : IEquatable<Rfloat2>
	{
		private const MethodImplOptions INLINE = MethodImplOptions.AggressiveInlining;

		private const double TOLERANCE = 1E-06;

		public readonly double x;

		public readonly double y;

		public double Length
		{
			[MethodImpl(MethodImplOptions.AggressiveInlining)]
			get
			{
				return Rmath.Sqrt(x * x + y * y);
			}
		}

		public double SqrLength
		{
			[MethodImpl(MethodImplOptions.AggressiveInlining)]
			get
			{
				return x * x + y * y;
			}
		}

		public bool IsNegligible
		{
			[MethodImpl(MethodImplOptions.AggressiveInlining)]
			get
			{
				if (Rmath.Abs(x) < 1E-06)
				{
					return Rmath.Abs(y) < 1E-06;
				}
				return false;
			}
		}

		public static Rfloat2 Zero => new Rfloat2(0.0, 0.0);

		public static Rfloat2 One => new Rfloat2(1.0, 1.0);

		public Rfloat2 Normalized
		{
			[MethodImpl(MethodImplOptions.AggressiveInlining)]
			get
			{
				double length = Length;
				if (!(length > 1E-06))
				{
					return Zero;
				}
				return new Rfloat2(x / length, y / length);
			}
		}

		public Rfloat2(double x, double y)
		{
			this.x = x;
			this.y = y;
		}

		public Rfloat2(double val)
			: this(val, val)
		{
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static implicit operator Rfloat2(Rint2 ivec)
		{
			return new Rfloat2(ivec.x, ivec.y);
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static double Distance(Rfloat2 lhs, Rfloat2 rhs)
		{
			return (lhs - rhs).Length;
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static double Dot(Rfloat2 lhs, Rfloat2 rhs)
		{
			return lhs.x * rhs.x + lhs.y * rhs.y;
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static double Cross(Rfloat2 lhs, Rfloat2 rhs)
		{
			return lhs.x * rhs.y - lhs.y * rhs.x;
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static double Angle(Rfloat2 a, Rfloat2 b)
		{
			double num = Rmath.Sqrt(a.SqrLength * b.SqrLength);
			if (num < 1E-06)
			{
				return 0.0;
			}
			return Rmath.Acos(Rmath.Clamp(Dot(a, b) / num, -1.0, 1.0)) * (double)Rmath.Sign(Cross(a, b));
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static Rfloat2 UnclampedLerp(double t, Rfloat2 a, Rfloat2 b)
		{
			return new Rfloat2(Rmath.UnclampedLerp(t, a.x, b.x), Rmath.UnclampedLerp(t, a.y, b.y));
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static Rfloat2 Lerp(double t, Rfloat2 a, Rfloat2 b)
		{
			return new Rfloat2(Rmath.Lerp(t, a.x, b.x), Rmath.Lerp(t, a.y, b.y));
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public double DistanceTo(Rfloat2 other)
		{
			return (this - other).Length;
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public Rint2 FloorToInt()
		{
			return new Rint2(Rmath.FloorToInt(x), Rmath.FloorToInt(y));
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public Rint2 CeilToInt()
		{
			return new Rint2(Rmath.CeilToInt(x), Rmath.CeilToInt(y));
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public Rint2 RoundToInt()
		{
			return new Rint2(Rmath.RoundToInt(x), Rmath.RoundToInt(y));
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public bool Equals(Rfloat2 other)
		{
			if (x.Equals(other.x))
			{
				return y.Equals(other.y);
			}
			return false;
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public override bool Equals(object obj)
		{
			if (obj is Rfloat2 other)
			{
				return Equals(other);
			}
			return false;
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public override int GetHashCode()
		{
			return HashCode.Combine(x, y);
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public override string ToString()
		{
			return $"({x}, {y})";
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static bool operator ==(Rfloat2 lhs, Rfloat2 rhs)
		{
			if (Rmath.Abs(lhs.x - rhs.x) < 1E-06)
			{
				return Rmath.Abs(lhs.y - rhs.y) < 1E-06;
			}
			return false;
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static bool operator !=(Rfloat2 lhs, Rfloat2 rhs)
		{
			if (!(Rmath.Abs(lhs.x - rhs.x) > 1E-06))
			{
				return Rmath.Abs(lhs.y - rhs.y) > 1E-06;
			}
			return true;
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static Rfloat2 operator +(Rfloat2 lhs, Rfloat2 rhs)
		{
			return new Rfloat2(lhs.x + rhs.x, lhs.y + rhs.y);
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static Rfloat2 operator -(Rfloat2 lhs, Rfloat2 rhs)
		{
			return new Rfloat2(lhs.x - rhs.x, lhs.y - rhs.y);
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static Rfloat2 operator -(Rfloat2 vec)
		{
			return new Rfloat2(0.0 - vec.x, 0.0 - vec.y);
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static Rfloat2 operator +(Rfloat2 vec, double scalar)
		{
			return new Rfloat2(vec.x + scalar, vec.y + scalar);
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static Rfloat2 operator +(double scalar, Rfloat2 vec)
		{
			return new Rfloat2(scalar * vec.x, scalar * vec.y);
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static Rfloat2 operator -(Rfloat2 vec, double scalar)
		{
			return new Rfloat2(vec.x - scalar, vec.y - scalar);
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static Rfloat2 operator -(double scalar, Rfloat2 vec)
		{
			return new Rfloat2(scalar - vec.x, scalar - vec.y);
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static Rfloat2 operator *(Rfloat2 lhs, Rfloat2 rhs)
		{
			return new Rfloat2(lhs.x * rhs.x, lhs.y * rhs.y);
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static Rfloat2 operator *(Rfloat2 vec, double scalar)
		{
			return new Rfloat2(vec.x * scalar, vec.y * scalar);
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static Rfloat2 operator *(double scalar, Rfloat2 vec)
		{
			return new Rfloat2(scalar * vec.x, scalar * vec.y);
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static Rfloat2 operator /(Rfloat2 lhs, Rfloat2 rhs)
		{
			return new Rfloat2(lhs.x / rhs.x, lhs.y / rhs.y);
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static Rfloat2 operator /(Rfloat2 vec, double scalar)
		{
			return new Rfloat2(vec.x / scalar, vec.y / scalar);
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static Rfloat2 operator /(double scalar, Rfloat2 vec)
		{
			return new Rfloat2(scalar / vec.x, scalar / vec.y);
		}
	}
	[Serializable]
	public struct Rint2 : IEquatable<Rint2>
	{
		private const MethodImplOptions INLINE = MethodImplOptions.AggressiveInlining;

		public readonly int x;

		public readonly int y;

		public static Rint2 Zero => new Rint2(0, 0);

		public static Rint2 One => new Rint2(1, 1);

		public Rint2(int x, int y)
		{
			this.x = x;
			this.y = y;
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public bool Equals(Rint2 other)
		{
			if (x == other.x)
			{
				return y == other.y;
			}
			return false;
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public override bool Equals(object obj)
		{
			if (obj is Rint2 other)
			{
				return Equals(other);
			}
			return false;
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public override int GetHashCode()
		{
			return HashCode.Combine(x, y);
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static bool operator ==(Rint2 left, Rint2 right)
		{
			return left.Equals(right);
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static bool operator !=(Rint2 left, Rint2 right)
		{
			return !left.Equals(right);
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public override string ToString()
		{
			return $"({x}, {y})";
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static Rint2 operator +(Rint2 lhs, Rint2 rhs)
		{
			return new Rint2(lhs.x + rhs.x, lhs.y + rhs.y);
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static Rint2 operator -(Rint2 lhs, Rint2 rhs)
		{
			return new Rint2(lhs.x - rhs.x, lhs.y - rhs.y);
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static Rint2 operator -(Rint2 vec)
		{
			return new Rint2(-vec.x, -vec.y);
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static Rint2 operator +(Rint2 vec, int scalar)
		{
			return new Rint2(vec.x + scalar, vec.y + scalar);
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static Rint2 operator +(int scalar, Rint2 vec)
		{
			return new Rint2(scalar * vec.x, scalar * vec.y);
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static Rint2 operator -(Rint2 vec, int scalar)
		{
			return new Rint2(vec.x - scalar, vec.y - scalar);
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static Rint2 operator -(int scalar, Rint2 vec)
		{
			return new Rint2(scalar - vec.x, scalar - vec.y);
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static Rint2 operator *(Rint2 lhs, Rint2 rhs)
		{
			return new Rint2(lhs.x * rhs.x, lhs.y * rhs.x);
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static Rint2 operator *(Rint2 vec, int scalar)
		{
			return new Rint2(vec.x * scalar, vec.y * scalar);
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static Rint2 operator *(int scalar, Rint2 vec)
		{
			return new Rint2(scalar * vec.x, scalar * vec.y);
		}
	}
	public static class Rmath
	{
		[Serializable]
		public struct Range
		{
			public double min;

			public double max;

			public bool IsEmpty => min.Equals(max);

			public bool IsPositive => max > min;

			public static Range Unit => new Range
			{
				min = -1.0,
				max = 1.0
			};

			public static Range From(double a, double b)
			{
				if (!(a <= b))
				{
					return new Range
					{
						min = b,
						max = a
					};
				}
				return new Range
				{
					min = a,
					max = b
				};
			}
		}

		[Serializable]
		public struct RemapConfig
		{
			public double fromA;

			public double fromB;

			public double toA;

			public double toB;

			public Range From
			{
				get
				{
					return new Range
					{
						min = fromA,
						max = fromB
					};
				}
				set
				{
					double min = value.min;
					double max = value.max;
					fromA = min;
					fromB = max;
				}
			}

			public Range To
			{
				get
				{
					return new Range
					{
						min = toA,
						max = toB
					};
				}
				set
				{
					double min = value.min;
					double max = value.max;
					toA = min;
					toB = max;
				}
			}

			public bool IsEmpty
			{
				get
				{
					if (!From.IsEmpty)
					{
						return To.IsEmpty;
					}
					return true;
				}
			}
		}

		private const short INLINE = 256;

		public const double PI = Math.PI;

		public const double RAD_TO_DEG = 180.0 / Math.PI;

		public const double DEG_TO_RAD = Math.PI / 180.0;

		public const double EPSILON = 1E-06;

		public const double RELATIVE_TOLERANCE = 1E-10;

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static int FloorToInt(double f)
		{
			if (f < 0.0)
			{
				return (int)f - 1;
			}
			return (int)f;
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static int CeilToInt(double f)
		{
			if (f > 0.0)
			{
				return (int)f + 1;
			}
			return (int)f;
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static int RoundToInt(double f)
		{
			return FloorToInt(f + 0.5);
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static int Min(int a, int b)
		{
			if (a >= b)
			{
				return b;
			}
			return a;
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static int Max(int a, int b)
		{
			if (a <= b)
			{
				return b;
			}
			return a;
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static double Min(double a, double b)
		{
			if (!double.IsNaN(a) && !(a < b))
			{
				return b;
			}
			return a;
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static double Max(double a, double b)
		{
			if (!double.IsNaN(a) && !(a > b))
			{
				return b;
			}
			return a;
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static double Abs(double value)
		{
			return DeterministicMath.Abs(value);
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static int Sign(double value)
		{
			if (!(value < 0.0))
			{
				return 1;
			}
			return -1;
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static double Sqrt(double value)
		{
			return DeterministicMath.Sqrt(value);
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static double Pow(double value, int power)
		{
			return DeterministicMath.Pow(value, power);
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static double Pow(double value, double power)
		{
			return DeterministicMath.Pow(value, power);
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static double Exp(double value)
		{
			return DeterministicMath.Exp(value);
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static double Log(double value)
		{
			return DeterministicMath.Log(value);
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static double Sin(double value)
		{
			return DeterministicMath.Sin(value);
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static double Cos(double value)
		{
			return DeterministicMath.Cos(value);
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static double Tan(double value)
		{
			return DeterministicMath.Tan(value);
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static double Asin(double value)
		{
			return DeterministicMath.Asin(value);
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static double Acos(double value)
		{
			return DeterministicMath.Acos(value);
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static double Atan(double value)
		{
			return DeterministicMath.Atan(value);
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static double Atan2(double y, double x)
		{
			return DeterministicMath.Atan2(y, x);
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static double Smooth3(double t)
		{
			return t * t * (3.0 - 2.0 * t);
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static double Smooth5(double t)
		{
			return t * t * t * (t * (t * 6.0 - 15.0) + 10.0);
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static double UnclampedLerp(double t, double a, double b)
		{
			return a + t * (b - a);
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static double UnclampedLerp(double t, Range range)
		{
			return UnclampedLerp(t, range.min, range.max);
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static double Lerp(double t, double a, double b)
		{
			return UnclampedLerp(Clamp01(t), a, b);
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static double Lerp(double t, Range range)
		{
			return Lerp(t, range.min, range.max);
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static double SmoothStep(double t, double a, double b)
		{
			if (!(t <= 0.0))
			{
				if (t >= 1.0)
				{
					return b;
				}
				return UnclampedLerp(Smooth3(t), a, b);
			}
			return a;
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static double SmoothStep(double t, Range range)
		{
			return SmoothStep(t, range.min, range.max);
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static double SmootherStep(double t, double a, double b)
		{
			if (!(t <= 0.0))
			{
				if (t >= 1.0)
				{
					return b;
				}
				return UnclampedLerp(Smooth5(t), a, b);
			}
			return a;
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static double SmootherStep(double t, Range range)
		{
			return SmootherStep(t, range.min, range.max);
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static double InverseLerp(double value, double a, double b)
		{
			if (a.Equals(b))
			{
				return 0.5;
			}
			return Clamp01((value - a) / (b - a));
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static double InverseLerp(double value, Range range)
		{
			return InverseLerp(value, range.min, range.max);
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static double Clamp(double value, double min, double max)
		{
			if (value < min)
			{
				return min;
			}
			if (!(value > max))
			{
				return value;
			}
			return max;
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static double Clamp(double value, Range range)
		{
			return Clamp(value, range.min, range.max);
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static double Clamp01(double value)
		{
			if (!(value < 0.0))
			{
				if (value > 1.0)
				{
					return 1.0;
				}
				return value;
			}
			return 0.0;
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static double Remap(double value, RemapConfig config)
		{
			return Lerp(InverseLerp(value, config.fromA, config.fromB), config.toA, config.toB);
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static bool Within(double value, Range range)
		{
			if (value >= range.min)
			{
				return value <= range.max;
			}
			return false;
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static double Rectifier(double value)
		{
			if (!(value > 0.0))
			{
				return 0.0;
			}
			return value;
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static double Curvature(Rfloat2 d, Rfloat2 d2)
		{
			double num = 1.0 + d.SqrLength;
			return d2.Length / Sqrt(num * num * num);
		}
	}
	[Serializable]
	public struct CommonConfig
	{
		public double worldSize;

		public double tileSpacing;

		public double mountainHeight;

		public double oceanDepth;

		public double startingAreaRadius;
	}
	public interface IWorldGen
	{
		public struct PointData
		{
			public Biome biome;

			public double height;

			public double fertility;

			public double lava;

			public double snow;

			public double dirt;

			public double pave;
		}

		double WorldSize { get; }

		Biome GetBiome(Rfloat2 pos);

		double GetForestation(Rfloat2 pos);

		PointData RenderPoint(Rfloat2 pos);
	}
	[Serializable]
	[CompositeConfig]
	public struct WorldGenerationConfig
	{
		public CommonConfig common;

		public WorldConfig world;

		public RenderingConfig rendering;
	}
}
namespace Riverheim.World
{
	public readonly struct ExtendedTileFlags
	{
		public bool IsCenter { get; }

		public bool IsLand => !IsWater;

		public bool IsWater
		{
			get
			{
				if (!IsOcean)
				{
					return IsLake;
				}
				return true;
			}
		}

		public bool IsOcean { get; }

		public bool IsLake { get; }

		public bool IsCoast { get; }

		public bool IsOceanCoast { get; }

		public bool IsLakeCoast { get; }

		public ExtendedTileFlags(bool isCenter, bool isOcean, bool isLake, bool isOceanCoast, bool isLakeCoast)
		{
			IsCenter = isCenter;
			IsOcean = isOcean;
			IsLake = isLake;
			IsCoast = isOceanCoast || isLakeCoast;
			IsOceanCoast = isOceanCoast;
			IsLakeCoast = isLakeCoast;
		}
	}
	[Producer(new string[] { "Flags/Extended" })]
	[Fork("Region")]
	public class ExtendedTileFlagStage : IGeneratorStage<TileData<ExtendedTileFlags>>
	{
		public const string OUTPUT = "Flags/Extended";

		[Resource("Regions/Tiling")]
		public Tiling tiling;

		[Resource("Flags/Base")]
		public TileData<TileFlags> baseFlags;

		[Resource("Heights/Lakes")]
		public TileSet lakes;

		public TileData<ExtendedTileFlags> Compute()
		{
			return tiling.CreateData((Tile tile) => new ExtendedTileFlags(baseFlags[tile].IsCenter, baseFlags[tile].IsOcean, lakes.Contains(tile), baseFlags[tile].IsCoast, IsLakeCoast(tile, lakes)));
		}

		private bool IsLakeCoast(Tile tile, TileSet lakeTiles)
		{
			if (!baseFlags[tile].IsLand)
			{
				return false;
			}
			bool isLake = lakeTiles.Contains(tile);
			return tile.GetNeighbors().Any((Tile neighbor) => lakeTiles.Contains(neighbor) != isLake);
		}
	}
	[Producer(new string[] { "Forestation" })]
	[Fork("Region")]
	public class ForestationStage : IGeneratorStage<TileData<InertFloat>>
	{
		[Serializable]
		public struct Config
		{
			public double noiseScale;

			public NoiseField.FractalConfig noiseFractal;

			public double marshBonus;

			public double plainsRiverBonus;

			public Rmath.Range plainsRiverBonusProximityRange;

			public Rmath.Range plainsRiverBonusHeightRange;

			public double meadowsRiverBonus;

			public Rmath.Range meadowsRiverBonusProximityRange;
		}

		public const string OUTPUT = "Forestation";

		[Random("Forestation")]
		public RandomDirectory random;

		[LaneConfig]
		public Config config;

		[Resource("Regions/Tiling")]
		public Tiling tiling;

		[Resource("Biomes")]
		public TileData<Biome> biomes;

		[Resource("Rivers/Proximity")]
		public TileData<InertFloat> riverProximity;

		[Resource("Heights/SettledHeightMap")]
		public TileData<InertFloat> heights;

		public TileData<InertFloat> Compute()
		{
			NoiseField noise = random.BiasedNoise2D("Forestation", config.noiseScale).Fractal(config.noiseFractal);
			return tiling.CreateData((Func<Tile, InertFloat>)delegate(Tile tile)
			{
				double result = 1.0 - noise.Sample(tile.Center);
				return Rmath.Clamp01(AdjustResult(biomes[tile], tile, result));
			});
		}

		private double AdjustResult(Biome biome, Tile tile, double result)
		{
			switch (biome)
			{
			case Biome.Meadows:
			{
				double meadowsRiverBonus = config.meadowsRiverBonus;
				meadowsRiverBonus *= 1.0 - Rmath.InverseLerp(riverProximity[tile], config.meadowsRiverBonusProximityRange);
				return result + meadowsRiverBonus;
			}
			case Biome.Marsh:
				return result + config.marshBonus;
			case Biome.Plains:
			{
				double plainsRiverBonus = config.plainsRiverBonus;
				plainsRiverBonus *= 1.0 - Rmath.InverseLerp(riverProximity[tile], config.plainsRiverBonusProximityRange);
				plainsRiverBonus *= 1.0 - Rmath.InverseLerp(heights[tile], config.plainsRiverBonusHeightRange);
				return result + plainsRiverBonus;
			}
			case Biome.Mistglade:
				return 0.0;
			default:
				return result;
			}
		}
	}
	public class LandmassInfo
	{
		public int border;

		public double area;

		public double coastline;

		public double Compactness => Math.PI * 4.0 * area / (coastline * coastline);
	}
	public struct LandmassMetrics
	{
		public int count;

		public double totalArea;

		public double maxArea;

		public double avgArea;

		public double avgCompactness;
	}
	public struct RiverMetrics
	{
		public int count;

		public double maxLength;

		public double avgLength;
	}
	public class BiomeMetrics
	{
		public double totalArea;

		public double innerArea;
	}
	public struct WorldMetrics
	{
		public LandmassMetrics landmassMetrics;

		public RiverMetrics riverMetrics;

		public Dictionary<Biome, BiomeMetrics> biomeMetrics;

		public double highestPoint;

		public double centerCoastalProximity;

		public override string ToString()
		{
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.AppendLine("World Metrics:");
			stringBuilder.AppendLine("  Total Land: " + ToPercentage(landmassMetrics.totalArea));
			stringBuilder.AppendLine($"  Landmass Count: {landmassMetrics.count}");
			stringBuilder.AppendLine("  Largest Landmass: " + ToPercentage(landmassMetrics.maxArea));
			stringBuilder.AppendLine("  Average Landmass: " + ToPercentage(landmassMetrics.avgArea));
			stringBuilder.AppendLine($"  Average Compactness: {landmassMetrics.avgCompactness:0.0}");
			stringBuilder.AppendLine($"  Highest point: {highestPoint:0.0}m");
			stringBuilder.AppendLine();
			stringBuilder.AppendLine($"  Total Rivers: {riverMetrics.count}");
			stringBuilder.AppendLine($"  Longest River: {riverMetrics.maxLength:0.0}m");
			stringBuilder.AppendLine($"  Average River: {riverMetrics.avgLength:0.0}m");
			stringBuilder.AppendLine();
			foreach (Biome key in this.biomeMetrics.Keys)
			{
				BiomeMetrics biomeMetrics = this.biomeMetrics[key];
				stringBuilder.AppendLine($"  {key} Total Area (Inner): {ToPercentage(biomeMetrics.totalArea)} ({ToPercentage(biomeMetrics.innerArea)})");
			}
			return stringBuilder.ToString();
		}

		private static string ToPercentage(double value)
		{
			return $"{value * 100.0:0.00}%";
		}
	}
	[Producer(new string[] { "WorldMetrics" })]
	public class WorldMetricCalculator : IGeneratorStage<WorldMetrics>
	{
		public const string OUTPUT = "WorldMetrics";

		[Resource("MainTiling")]
		public Tiling tiling;

		[Resource("Regions/Tiling")]
		public Tiling[] regionTilings;

		[Resource("Regions/Mapping")]
		public SubtilingMapping[] regionMappings;

		[Resource("LandmassIds")]
		public TileData<int>[] landmassIds;

		[Resource("Rivers/Splines")]
		public River[][] rivers;

		[Resource("Biomes")]
		public TileData<Biome>[] biomes;

		[Resource("Heights/JoinedHeights")]
		public TileData<InertFloat> heights;

		[Resource("CoastalProximity")]
		public TileData<InertFloat>[] coastalProximity;

		public WorldMetrics Compute()
		{
			return new WorldMetrics
			{
				landmassMetrics = ComputeLandmassMetrics(),
				riverMetrics = ComputeRiverMetrics(RegionStage.JoinRegionData(rivers)),
				biomeMetrics = ComputeBiomeMetrics(tiling, RegionStage.JoinRegionData(tiling, regionTilings, regionMappings, biomes)),
				highestPoint = tiling.Tiles.Max((Func<Tile, double>)((Tile tile) => heights[tile])),
				centerCoastalProximity = coastalProximity[0][tiling.GetTile(Rfloat2.Zero).Value]
			};
		}

		private static List<LandmassInfo> ComputeLandmassInfos(Tiling tiling, TileData<int> landmassIds)
		{
			Dictionary<int, LandmassInfo> dictionary = new Dictionary<int, LandmassInfo>();
			foreach (Tile tile in tiling.Tiles)
			{
				int landmassId = tile.Data(landmassIds);
				if (landmassId != -1)
				{
					if (!dictionary.ContainsKey(landmassId))
					{
						dictionary[landmassId] = new LandmassInfo();
					}
					dictionary[landmassId].area += tile.Area;
					if (tile.GetNeighbors().Any((Tile neighbor) => neighbor.Data(landmassIds) != landmassId))
					{
						dictionary[landmassId].border++;
					}
				}
			}
			foreach (LandmassInfo value in dictionary.Values)
			{
				value.coastline = (double)value.border * tiling.Spacing;
			}
			return dictionary.Values.ToList();
		}

		private LandmassMetrics ComputeLandmassMetrics()
		{
			List<LandmassInfo> list = new List<LandmassInfo>();
			foreach (int value in Enum.GetValues(typeof(Region)))
			{
				list.AddRange(ComputeLandmassInfos(regionTilings[value], landmassIds[value]));
			}
			return new LandmassMetrics
			{
				count = list.Count,
				totalArea = list.Sum((LandmassInfo x) => x.area) / tiling.TotalArea,
				maxArea = list.Max((LandmassInfo x) => x.area) / tiling.TotalArea,
				avgArea = list.Average((LandmassInfo x) => x.area) / tiling.TotalArea,
				avgCompactness = list.Average((LandmassInfo x) => x.Compactness)
			};
		}

		private static RiverMetrics ComputeRiverMetrics(River[] rivers)
		{
			List<double> source = rivers.Select(RiverLength).ToList();
			return new RiverMetrics
			{
				count = rivers.Length,
				maxLength = ((rivers.Length != 0) ? source.Max() : 0.0),
				avgLength = ((rivers.Length != 0) ? source.Average() : 0.0)
			};
		}

		private static double RiverLength(River river)
		{
			return river.spline.Length;
		}

		private static Dictionary<Biome, BiomeMetrics> ComputeBiomeMetrics(Tiling tiling, TileData<Biome> biomes)
		{
			Dictionary<Biome, BiomeMetrics> dictionary = new Dictionary<Biome, BiomeMetrics>();
			foreach (Tile tile in tiling.Tiles)
			{
				if (!dictionary.ContainsKey(tile.Data(biomes)))
				{
					dictionary[tile.Data(biomes)] = new BiomeMetrics();
				}
				double num = tile.Area / tiling.TotalArea;
				dictionary[tile.Data(biomes)].totalArea += num;
				if (tile.GetNeighbors().All((Tile neighbor) => neighbor.Data(biomes) == tile.Data(biomes)))
				{
					dictionary[tile.Data(biomes)].innerArea += num;
				}
			}
			return dictionary;
		}
	}
	public enum PolarProximityType
	{
		Any,
		North,
		South
	}
	public delegate double PolarProximityMap(Rfloat2 pos, PolarProximityType type = PolarProximityType.Any);
	[Serializable]
	public struct PolarBeltConfig
	{
		public double offset;

		public double radius;

		public double noiseScale;

		public double noiseAmplitude;
	}
	[Producer(new string[] { "PolarProximity" })]
	public class PolarProximityStage : IGeneratorStage<PolarProximityMap>
	{
		public const string OUTPUT = "PolarProximity";

		[Random("PolarProximityMap")]
		public RandomDirectory random;

		[Config]
		public PolarBeltConfig config;

		public PolarProximityMap Compute()
		{
			NoiseField noise = random.Noise2D("PolarProximity", config.noiseScale);
			return delegate(Rfloat2 pos, PolarProximityType type)
			{
				double num = Rmath.Atan2(pos.x, (pos.y < 0.0) ? (pos.y - config.offset) : pos.y);
				double num2 = config.radius + Rmath.Sin(num * 20.0) * 100.0;
				double num3 = (pos + new Rfloat2(0.0, config.offset)).Length - num2;
				double num4 = (pos - new Rfloat2(0.0, config.offset)).Length - num2;
				double num5 = type switch
				{
					PolarProximityType.North => num3, 
					PolarProximityType.South => num4, 
					PolarProximityType.Any => Rmath.Max(num3, num4), 
					_ => throw new ArgumentOutOfRangeException(), 
				};
				double num6 = num5 / config.noiseAmplitude;
				return num5 + Rmath.Clamp01(num6 * num6) * config.noiseAmplitude * noise.Sample(pos);
			};
		}
	}
	public enum Region
	{
		Main,
		SouthPole,
		NorthPole
	}
	[Serializable]
	public struct RegionalConfig<T> : ILaneConfig<T>, ILaneConfig
	{
		public T main;

		public T southPole;

		public T northPole;

		readonly object ILaneConfig.Get(int lane)
		{
			return Get((Region)lane);
		}

		public readonly T Get(int lane)
		{
			return Get((Region)lane);
		}

		public readonly T Get(Region region)
		{
			return region switch
			{
				Region.Main => main, 
				Region.SouthPole => southPole, 
				Region.NorthPole => northPole, 
				_ => default(T), 
			};
		}

		public IEnumerable<(Region, T)> All()
		{
			yield return (Region.Main, main);
			yield return (Region.SouthPole, southPole);
			yield return (Region.NorthPole, northPole);
		}
	}
	[ForkingDimension("Region", typeof(Region))]
	[Producer(new string[] { "Regions" })]
	public class RegionStage : IGeneratorStage<TileData<Region>[]>
	{
		public const string OUTPUT = "Regions";

		public const string FORK_DIMENSION = "Region";

		[Config]
		public CommonConfig commonConfig;

		[Config]
		public PolarBeltConfig polarBeltConfig;

		[Resource("MainTiling")]
		public Tiling tiling;

		[Resource("TilingLods")]
		public Tiling[] tilingLods;

		[Resource("PolarProximity")]
		public PolarProximityMap polarProximity;

		public TileData<Region>[] Compute()
		{
			TileData<Region>[] array = new TileData<Region>[tilingLods.Length + 1];
			for (int i = 0; i < array.Length; i++)
			{
				array[i] = ComputeLod(i);
			}
			return array;
		}

		private TileData<Region> ComputeLod(int lod)
		{
			if (lod == 0)
			{
				MutableTileData<Region> mutableTileData = tiling.CreateData((Tile tile) => GetRegion(tile, polarProximity));
				FixOrphanRegions(mutableTileData);
				return mutableTileData;
			}
			return tilingLods[lod - 1].CreateData((Tile tile) => GetRegion(tile, polarProximity));
		}

		private static Region GetRegion(Tile tile, PolarProximityMap polarProximity)
		{
			if (polarProximity(tile.Center, PolarProximityType.North) > 0.0)
			{
				return Region.NorthPole;
			}
			if (polarProximity(tile.Center, PolarProximityType.South) > 0.0)
			{
				return Region.SouthPole;
			}
			return Region.Main;
		}

		private Rfloat2 RegionOrigin(Region region)
		{
			double num = Rmath.Lerp(0.5, commonConfig.worldSize, polarBeltConfig.radius - polarBeltConfig.offset);
			return region switch
			{
				Region.Main => Rfloat2.Zero, 
				Region.NorthPole => new Rfloat2(0.0, num), 
				Region.SouthPole => new Rfloat2(0.0, 0.0 - num), 
				_ => throw new ArgumentOutOfRangeException(), 
			};
		}

		private void FixOrphanRegions(MutableTileData<Region> regions)
		{
			MutableTileData<int> mutableTileData = MarkRegionComponents(regions);
			Dictionary<Region, int> dictionary = new Dictionary<Region, int>();
			foreach (Region value in Enum.GetValues(typeof(Region)))
			{
				Tile? tile = tiling.GetTile(RegionOrigin(value));
				if (tile.HasValue)
				{
					Tile valueOrDefault = tile.GetValueOrDefault();
					dictionary[value] = mutableTileData[valueOrDefault];
				}
			}
			TileQueue tileQueue = new TileQueue(tiling);
			foreach (Tile tile2 in tiling.Tiles)
			{
				Region region2 = regions[tile2];
				if (mutableTileData[tile2] != dictionary[region2])
				{
					continue;
				}
				tileQueue.Enqueue(tile2);
				while (tileQueue.Count > 0)
				{
					foreach (Tile neighbor in tileQueue.Dequeue().GetNeighbors())
					{
						if (mutableTileData[neighbor] != dictionary[regions[neighbor]])
						{
							regions[neighbor] = region2;
							mutableTileData[neighbor] = dictionary[region2];
							tileQueue.Enqueue(neighbor);
						}
					}
				}
			}
		}

		private MutableTileData<int> MarkRegionComponents(TileData<Region> regions)
		{
			MutableTileData<int> mutableTileData = tiling.CreateData(-1);
			int num = 0;
			TileQueue tileQueue = new TileQueue(tiling);
			foreach (Tile tile in tiling.Tiles)
			{
				if (mutableTileData[tile] != -1)
				{
					continue;
				}
				mutableTileData[tile] = num;
				tileQueue.Enqueue(tile);
				while (tileQueue.Count > 0)
				{
					foreach (Tile neighbor in tileQueue.Dequeue().GetNeighbors())
					{
						if (regions[neighbor] == regions[tile] && mutableTileData[neighbor] == -1)
						{
							mutableTileData[neighbor] = num;
							tileQueue.Enqueue(neighbor);
						}
					}
				}
				num++;
			}
			return mutableTileData;
		}

		public static TileData<T> JoinRegionData<T>(Tiling tiling, Tiling[] regionTilings, SubtilingMapping[] regionMappings, TileData<T>[] regionData)
		{
			MutableTileData<T> mutableTileData = tiling.CreateData<T>();
			foreach (int value in Enum.GetValues(typeof(Region)))
			{
				SubtilingMapping subtilingMapping = regionMappings[value];
				foreach (Tile tile in regionTilings[value].Tiles)
				{
					mutableTileData[subtilingMapping.GetParentTile(tile)] = regionData[value][tile];
				}
			}
			return mutableTileData;
		}

		public static T[] JoinRegionData<T>(T[][] regionData)
		{
			List<T> list = new List<T>();
			foreach (int value in Enum.GetValues(typeof(Region)))
			{
				list.AddRange(regionData[value]);
			}
			return list.ToArray();
		}
	}
	public readonly struct TileFlags
	{
		public bool IsCenter { get; }

		public bool IsLand { get; }

		public bool IsOcean => !IsLand;

		public bool IsCoast { get; }

		public TileFlags(bool isLand, bool isCenter, bool isCoast)
		{
			IsLand = isLand;
			IsCenter = isCenter;
			IsCoast = isCoast;
		}
	}
	[Producer(new string[] { "Flags/Base" })]
	[Fork("Region")]
	public class TileFlagStage : IGeneratorStage<TileData<TileFlags>>
	{
		public const string OUTPUT = "Flags/Base";

		[Resource("Regions/Tiling")]
		public Tiling tiling;

		[Resource("LandmassIds")]
		public TileData<int> landmassIds;

		public TileData<TileFlags> Compute()
		{
			return tiling.CreateData((Tile tile) => new TileFlags(IsLandTile(tile), tile.Id == tiling.GetTile(Rfloat2.Zero)?.Id, IsCoastalTile(tile)));
		}

		private bool IsLandTile(Tile tile)
		{
			return tile.Data(landmassIds) != -1;
		}

		private bool IsCoastalTile(Tile tile)
		{
			foreach (Tile neighbor in tile.GetNeighbors())
			{
				if (IsLandTile(tile) != IsLandTile(neighbor))
				{
					return true;
				}
			}
			return false;
		}
	}
	[Producer(new string[] { "TravelDistance" })]
	[Fork("Region")]
	public class TravelDistanceStage : IGeneratorStage<TileData<InertFloat>>
	{
		[Serializable]
		public struct Config
		{
			public bool normalize;

			public double landCost;

			public double riverCost;

			public double oceanCost;

			public double embarkCost;

			public FrontierRaymarchingConfig frontier;
		}

		[Serializable]
		public struct FrontierRaymarchingConfig
		{
			public int rays;

			public int maxSteps;

			public double stepRate;

			public double noiseScale;

			public double noiseMagnitude;
		}

		public const string OUTPUT = "TravelDistance";

		[Lane]
		public Region region;

		[Random("TravelDistance")]
		public RandomDirectory random;

		[LaneConfig]
		public Config config;

		[Resource("MainTiling")]
		public Tiling mainTiling;

		[Resource("Regions")]
		public TileData<Region>[] regions;

		[Resource("Regions/Tiling")]
		public Tiling tiling;

		[Resource("Flags/Base")]
		public TileData<TileFlags> flags;

		[Resource("PrunedRivers")]
		public TileData<RiverTile> rivers;

		public TileData<InertFloat> Compute()
		{
			TileSet frontier = Frontier();
			if (frontier.Count == 0)
			{
				return tiling.CreateData((Func<Tile, InertFloat>)((Tile tile) => tile.Center.Length));
			}
			NoiseField noise = config.frontier.noiseMagnitude * random.Noise2D("FrontierNoise", config.frontier.noiseScale);
			TileData<InertFloat> tileData = tiling.DistanceTransform((Tile tile) => (!frontier.Contains(tile)) ? ((double?)null) : new double?(tile.Center.Length * (1.0 + noise.Sample(tile.Center))), (Tile _) => true, (Tile t1, Tile t2) => Rfloat2.Distance(t1.Center, t2.Center) * EdgeCostMultiplier(t1, t2));
			if (!config.normalize)
			{
				return tileData;
			}
			double correction = GetAverageRealDistanceRate(tileData);
			return tileData.Transform((Func<InertFloat, InertFloat>)((InertFloat distance) => correction * (double)distance));
		}

		private double GetAverageRealDistanceRate(TileData<InertFloat> distances)
		{
			double num = 0.0;
			int num2 = 0;
			foreach (Tile tile in tiling.Tiles)
			{
				if (tile.HopsToBounds <= 0)
				{
					num += tile.Center.SqrLength / ((double)distances[tile] * (double)distances[tile]);
					num2++;
				}
			}
			return num / (double)num2;
		}

		private TileSet Frontier()
		{
			Tile? tile = tiling.GetTile(Rfloat2.Zero);
			if (tile.HasValue)
			{
				Tile valueOrDefault = tile.GetValueOrDefault();
				return TileSet.Of(valueOrDefault);
			}
			TileSet tileSet = new TileSet(tiling);
			if (config.frontier.rays == 0)
			{
				return tileSet;
			}
			TileData<InertFloat> tileData = mainTiling.DistanceTransform((Tile tile2) => (regions[0][tile2] != region) ? ((double?)null) : new double?(0.0), (Tile _) => true, (Tile t1, Tile t2) => Rfloat2.Distance(t1.Center, t2.Center));
			double spacing = tiling.Spacing;
			for (int num = 0; num < config.frontier.rays; num++)
			{
				Direction direction = new Direction(Math.PI * 2.0 * (double)num / (double)config.frontier.rays);
				Rfloat2 zero = Rfloat2.Zero;
				for (int num2 = 0; num2 < config.frontier.maxSteps; num2++)
				{
					tile = tiling.GetTile(zero);
					if (tile.HasValue)
					{
						Tile valueOrDefault2 = tile.GetValueOrDefault();
						tileSet.Add(valueOrDefault2);
						break;
					}
					tile = mainTiling.GetTile(zero);
					double num3;
					if (tile.HasValue)
					{
						Tile valueOrDefault3 = tile.GetValueOrDefault();
						num3 = tileData[valueOrDefault3];
					}
					else
					{
						num3 = spacing;
					}
					double num4 = num3;
					zero += Rmath.Max(spacing, num4 * config.frontier.stepRate) * direction.ToVector();
				}
			}
			return tileSet;
		}

		private double EdgeCostMultiplier(Tile t1, Tile t2)
		{
			if (flags[t1].IsLand != flags[t2].IsLand)
			{
				return config.embarkCost;
			}
			if (flags[t1].IsOcean || flags[t2].IsOcean)
			{
				return config.oceanCost;
			}
			if (rivers[t1].IsRiver() || rivers[t2].IsRiver())
			{
				return config.riverCost;
			}
			return config.landCost;
		}
	}
	[Serializable]
	[CompositeConfig]
	public record WorldConfig
	{
		public WorldVerticesCalculator.Config tiling;

		public PolarBeltConfig polar;

		public RegionalConfig<TravelDistanceStage.Config> travel;

		public LandmassConfig landmass;

		public RiverConfig rivers;

		public HeightConfig height;

		public BiomeConfig biomes;

		public Riverheim.World.Splats.SplatConfig splats;

		public RegionalConfig<ForestationStage.Config> forestation;
	}
	public class WorldState
	{
		public readonly Tiling tiling;

		public readonly TriangleMesh interpolationMesh;

		public readonly TileData<InertFloat> heights;

		public readonly TileData<Biome> biomes;

		public readonly TileData<InertFloat> forestation;

		public readonly River[] rivers;

		public readonly Splat[] splats;

		public WorldState(Tiling tiling, TriangleMesh interpolationMesh, TileData<InertFloat> heights, TileData<Biome> biomes, TileData<InertFloat> forestation, River[] rivers, Splat[] splats)
		{
			this.tiling = tiling;
			this.interpolationMesh = interpolationMesh;
			this.heights = heights;
			this.biomes = biomes;
			this.forestation = forestation;
			this.rivers = rivers;
			this.splats = splats;
		}
	}
	[Producer(new string[] { "WorldState" })]
	public class WorldStateCompiler : IGeneratorStage<WorldState>
	{
		public const string OUTPUT = "WorldState";

		[Resource("MainTiling")]
		public Tiling tiling;

		[Resource("MainInterpolationMesh")]
		public TriangleMesh interpolationMesh;

		[Resource("Regions/Tiling")]
		public Tiling[] regionTilings;

		[Resource("Regions/Mapping")]
		public SubtilingMapping[] regionMappings;

		[Resource("Heights/JoinedHeights")]
		public TileData<InertFloat> heights;

		[Resource("Biomes")]
		public TileData<Biome>[] biomes;

		[Resource("Rivers/Splines")]
		public River[][] rivers;

		[Resource("Splats")]
		public Splat[] splats;

		[Resource("Forestation")]
		public TileData<InertFloat>[] forestation;

		public WorldState Compute()
		{
			return new WorldState(tiling, interpolationMesh, heights, RegionStage.JoinRegionData(tiling, regionTilings, regionMappings, biomes), RegionStage.JoinRegionData(tiling, regionTilings, regionMappings, forestation), RegionStage.JoinRegionData(rivers), splats);
		}
	}
}
namespace Riverheim.World.Tilings
{
	[Producer(new string[] { "MainTiling", "MainInterpolationMesh" })]
	public class MainTilingStage : IGeneratorStage<(Tiling, TriangleMesh)>
	{
		public const string TILING = "MainTiling";

		public const string MESH = "MainInterpolationMesh";

		[Resource("WorldVertices")]
		public WorldVertices[] vertexLayers;

		public (Tiling, TriangleMesh) Compute()
		{
			WorldVertices worldVertices = vertexLayers[0];
			BarycentricDualTilingBuilder barycentricDualTilingBuilder = new BarycentricDualTilingBuilder(worldVertices.vertices, worldVertices.spacing);
			return (barycentricDualTilingBuilder.Build(), barycentricDualTilingBuilder.BuildMesh());
		}
	}
	[Producer(new string[] { "Regions/Tiling", "Regions/Mapping" })]
	[Fork("Region")]
	public class RegionTilingStage : IGeneratorStage<(Tiling, SubtilingMapping)>
	{
		public const string TILING = "Regions/Tiling";

		public const string MAPPING = "Regions/Mapping";

		[Lane]
		public Region region;

		[Resource("MainTiling")]
		public Tiling tiling;

		[Resource("Regions")]
		public TileData<Region>[] regions;

		public (Tiling, SubtilingMapping) Compute()
		{
			SubtilingMapping mapping;
			return (tiling.CreateSubTiling((Tile tile) => regions[0][tile] == region, out mapping), mapping);
		}
	}
	[Producer(new string[] { "TilingLods" })]
	public class TilingLodStage : IGeneratorStage<Tiling[]>
	{
		public const string OUTPUT = "TilingLods";

		[Resource("WorldVertices")]
		public WorldVertices[] worldVertices;

		public Tiling[] Compute()
		{
			Tiling[] array = new Tiling[this.worldVertices.Length - 1];
			for (int i = 0; i < array.Length; i++)
			{
				WorldVertices worldVertices = this.worldVertices[i + 1];
				VoronoiTilingBuilder voronoiTilingBuilder = new VoronoiTilingBuilder(worldVertices.vertices, worldVertices.spacing, worldVertices.boundingRadius);
				array[i] = voronoiTilingBuilder.Build();
			}
			return array;
		}
	}
	public struct WorldVertices
	{
		public double spacing;

		public double boundingRadius;

		public InertFloat2[] vertices;
	}
	[Producer(new string[] { "WorldVertices" })]
	public class WorldVerticesCalculator : IGeneratorStage<WorldVertices[]>
	{
		[Serializable]
		public struct Config
		{
			public int lodLevels;

			public double lodScale;
		}

		public const string OUTPUT = "WorldVertices";

		[Random("WorldVertices")]
		public RandomDirectory random;

		[Config]
		public CommonConfig commonConfig;

		[Config]
		public Config config;

		public WorldVertices[] Compute()
		{
			WorldVertices[] array = new WorldVertices[1 + config.lodLevels];
			for (int i = 0; i < array.Length; i++)
			{
				double num = commonConfig.tileSpacing * Rmath.Pow(config.lodScale, i);
				double boundingRadius = commonConfig.worldSize + Rmath.Sqrt(3.0) * num;
				InertFloat2[] vertices = (from point in new PoissonDiscSampler(random.Sequence(i.ToString()), boundingRadius, num / Rmath.Sqrt(2.0)).GetPoints()
					where point.SqrLength < boundingRadius * boundingRadius
					select point).Select((Func<Rfloat2, InertFloat2>)((Rfloat2 point) => point)).ToArray();
				array[i] = new WorldVertices
				{
					spacing = num,
					boundingRadius = boundingRadius,
					vertices = vertices
				};
			}
			return array;
		}
	}
}
namespace Riverheim.World.Splats
{
	public record Crag : Splat
	{
		public InertFloat radius;

		public InertFloat terrace;

		public InertFloat cliffiness;
	}
	public record Pond : Splat
	{
		public InertFloat radius;
	}
	[Producer(new string[] { "Splats/RiverSources" })]
	[Fork("Region")]
	public class RiverSourceSplatStage : IGeneratorStage<Splat[]>
	{
		[Serializable]
		public struct Config
		{
			public AllBiomeConfig<bool> allowedBiomes;

			public CragConfig crags;

			public PondConfig ponds;
		}

		[Serializable]
		public struct SpawnFeatureAffinityConfig
		{
			public enum AffinityType
			{
				None,
				LowerBound,
				UpperBound,
				Both
			}

			public AffinityType type;

			public Rmath.Range lowerBound;

			public Rmath.Range upperBound;
		}

		[Serializable]
		public struct CragConfig
		{
			public double frequency;

			public SpawnFeatureAffinityConfig height;

			public SpawnFeatureAffinityConfig riverLength;

			public CragRandomizer.Config random;
		}

		[Serializable]
		public struct PondConfig
		{
			public double frequency;

			public SpawnFeatureAffinityConfig height;

			public SpawnFeatureAffinityConfig riverLength;

			public double stepback;

			public PondRandomizer.Config random;
		}

		public const string OUTPUT = "Splats/RiverSources";

		[Random("Splats/RiverSources")]
		public RandomDirectory random;

		[LaneConfig]
		public Config config;

		[Resource("Regions/Tiling")]
		public Tiling tiling;

		[Resource("Regions/Mapping")]
		public SubtilingMapping mapping;

		[Resource("MainTiling")]
		public Tiling mainTiling;

		[Resource("MainInterpolationMesh")]
		public TriangleMesh mesh;

		[Resource("Rivers/Splines")]
		public River[] rivers;

		[Resource("Biomes/PostprocessedHeightMap")]
		public TileData<InertFloat> heights;

		[Resource("Biomes")]
		public TileData<Biome> biomes;

		public Splat[] Compute()
		{
			List<Crag> list = new List<Crag>();
			List<Pond> list2 = new List<Pond>();
			RandomIndexer randomIndexer = random.Indexer("Crags/Spawn");
			CragRandomizer cragRandomizer = new CragRandomizer(random.Sub("Crags"), config.crags.random);
			RandomIndexer randomIndexer2 = random.Indexer("Ponds/Spawn");
			PondRandomizer pondRandomizer = new PondRandomizer(random.Sub("Ponds"), config.ponds.random);
			BarycentricInterpolator barycentricInterpolator = new BarycentricInterpolator(mainTiling, mesh);
			River[] array = rivers;
			for (int i = 0; i < array.Length; i++)
			{
				River river = array[i];
				Rfloat2 pos = river.spline.EvaluatePosition(1.0);
				Tile? tile = tiling.GetTile(pos);
				if (!tile.HasValue)
				{
					continue;
				}
				Tile valueOrDefault = tile.GetValueOrDefault();
				if (config.allowedBiomes.Get(biomes[valueOrDefault]))
				{
					double num = barycentricInterpolator.InterpolateOnSubtilingData(pos, heights, mapping);
					double length = river.spline.Length;
					double frequency = config.crags.frequency;
					frequency *= SpawnProb(num, config.crags.height);
					frequency *= SpawnProb(length, config.crags.riverLength);
					if (randomIndexer.Single(valueOrDefault.Id).Value < frequency)
					{
						list.Add(cragRandomizer.Generate(valueOrDefault.Id, pos, num));
					}
					double frequency2 = config.ponds.frequency;
					frequency2 *= SpawnProb(num, config.ponds.height);
					frequency2 *= SpawnProb(length, config.ponds.riverLength);
					if (randomIndexer2.Single(valueOrDefault.Id).Value < frequency2)
					{
						Rfloat2 pos2 = river.spline.EvaluatePosition(1.0 - config.ponds.stepback / length);
						list2.Add(pondRandomizer.Generate(valueOrDefault.Id, pos2));
					}
				}
			}
			List<Splat> list3 = new List<Splat>();
			list3.AddRange(list);
			list3.AddRange(list2);
			return list3.ToArray();
		}

		private static double SpawnProb(double value, SpawnFeatureAffinityConfig config)
		{
			return config.type switch
			{
				SpawnFeatureAffinityConfig.AffinityType.LowerBound => Rmath.Smooth3(Rmath.InverseLerp(value, config.lowerBound)), 
				SpawnFeatureAffinityConfig.AffinityType.UpperBound => 1.0 - Rmath.Smooth3(Rmath.InverseLerp(value, config.upperBound)), 
				SpawnFeatureAffinityConfig.AffinityType.Both => Rmath.Smooth3(Rmath.InverseLerp(value, config.lowerBound)) - Rmath.Smooth3(Rmath.InverseLerp(value, config.upperBound)), 
				SpawnFeatureAffinityConfig.AffinityType.None => 1.0, 
				_ => 0.0, 
			};
		}
	}
	public class CragRandomizer
	{
		[Serializable]
		public struct Config
		{
			public Rmath.Range height;

			public Rmath.Range radius;

			public Rmath.Range terrace;
		}

		private readonly Config config;

		private readonly RandomIndexer height;

		private readonly RandomIndexer radius;

		private readonly RandomIndexer terrace;

		public CragRandomizer(RandomDirectory random, Config config)
		{
			this.config = config;
			height = random.Indexer("Height");
			radius = random.Indexer("Radius");
			terrace = random.Indexer("Terrace");
		}

		public Crag Generate(int id, Rfloat2 pos, double baseHeight)
		{
			double num = height.Single(id).Range(config.height);
			return new Crag
			{
				pos = pos,
				height = baseHeight + num,
				radius = radius.Single(id).Range(config.radius),
				terrace = num * terrace.Single(id).Range(config.terrace),
				cliffiness = 0.0
			};
		}
	}
	public class PondRandomizer
	{
		[Serializable]
		public struct Config
		{
			public Rmath.Range height;

			public Rmath.Range radius;
		}

		private readonly Config config;

		private readonly RandomIndexer height;

		private readonly RandomIndexer radius;

		public PondRandomizer(RandomDirectory random, Config config)
		{
			this.config = config;
			height = random.Indexer("Height");
			radius = random.Indexer("Radius");
		}

		public Pond Generate(int id, Rfloat2 pos)
		{
			return new Pond
			{
				pos = pos,
				height = height.Single(id).Range(config.height),
				radius = radius.Single(id).Range(config.radius)
			};
		}
	}
	public record Plateau : Splat
	{
		public InertFloat2 p1;

		public InertFloat2 p2;

		public InertFloat radius;

		public InertFloat2 incline;

		public Direction bulgeDirection;

		public InertFloat2 bulgeMagnitude;

		public double maxNeighborHeight;

		public bool inverted;
	}
	[Serializable]
	public struct PlateauConfig
	{
		public double spawnNoiseScale;

		public PlateauMountainPeakConfig mountainPeaks;

		public PlateauSeasideCliffConfig coastalCliffs;

		public PlateauHillConfig hills;
	}
	[Serializable]
	public struct PlateauMountainPeakConfig
	{
		public double spawnRate;

		public double minHeightDiff;

		public Rmath.Range size;

		public ClampingConfig gradientClamping;

		public Rpoint2 bulge;

		public double cragThreshold;

		public Rmath.Range cragHeight;

		public double cragRadius;

		public double cragTerrace;

		public AllBiomeConfig<bool> allowedBiomes;
	}
	[Serializable]
	public struct PlateauSeasideCliffConfig
	{
		public double spawnRate;

		public double neighborHeight;

		public double minHeight;

		public double maxHeight;

		public Rmath.Range size;

		public ClampingConfig gradientClamping;

		public Rmath.Range bulge;
	}
	[Serializable]
	public struct PlateauHillConfig
	{
		public double spawnRate;

		public double minHeightDiff;

		public Rmath.Range size;

		public double maxLength;

		public ClampingConfig gradientClamping;

		public Rpoint2 bulge;

		public AllBiomeConfig<bool> allowedBiomes;
	}
	[Serializable]
	public struct ClampingConfig
	{
		public enum Mode
		{
			Linear,
			Asymptotic
		}

		public Mode mode;

		public double min;

		public double max;

		public double midpoint;

		public double pow;

		public double rate;
	}
	[Producer(new string[] { "Splats/Plateaus" })]
	[Fork("Region")]
	public class PlateauStage : IGeneratorStage<Splat[]>
	{
		public const string OUTPUT = "Splats/Plateaus";

		[Random("Plateaus")]
		public RandomDirectory random;

		[LaneConfig]
		public PlateauConfig config;

		[Resource("Regions/Tiling")]
		public Tiling tiling;

		[Resource("Biomes/PostprocessedHeightMap")]
		public TileData<InertFloat> heights;

		[Resource("Biomes")]
		public TileData<Biome> biomes;

		public Splat[] Compute()
		{
			List<Splat> list = new List<Splat>();
			TileSet occupied = new TileSet(tiling);
			AddMountainPeaks(list, occupied);
			AddCoastalCliffs(list, occupied);
			AddHills(list, occupied);
			return list.ToArray();
		}

		private void AddMountainPeaks(List<Splat> result, TileSet occupied)
		{
			PlateauMountainPeakConfig cfg = config.mountainPeaks;
			NoiseField noiseField = random.BiasedNoise2D("SpawnRate/MountainPeaks", config.spawnNoiseScale);
			RandomIndexer randomIndexer = random.Indexer("Size");
			foreach (Tile tile in tiling.Tiles)
			{
				if (cfg.allowedBiomes.Get(biomes[tile]) && !(noiseField.Sample(tile.Center) > cfg.spawnRate) && !tile.GetNeighbors().Any((Tile neighbor) => !cfg.allowedBiomes.Get(biomes[neighbor])) && !tile.GetNeighbors().Any((Tile neighbor) => (double)neighbor.Data(heights) > (double)tile.Data(heights) - cfg.minHeightDiff) && occupied.Add(tile))
				{
					Rfloat2 gradient = HeightUtility.ComputeRoughness(tile, heights).Gradient;
					if (gradient.Length < cfg.cragThreshold)
					{
						result.Add(new Crag
						{
							pos = tile.Center,
							height = (double)tile.Data(heights) + randomIndexer.Single(tile.Id).Range(cfg.cragHeight),
							radius = cfg.cragRadius,
							terrace = cfg.cragTerrace,
							cliffiness = 1.0
						});
					}
					else
					{
						result.Add(new Plateau
						{
							pos = tile.Center,
							height = tile.Data(heights),
							radius = Rmath.Lerp(randomIndexer.Single(tile.Id).Value, cfg.size),
							incline = Clamp(gradient, cfg.gradientClamping),
							bulgeDirection = new Direction(gradient),
							bulgeMagnitude = cfg.bulge
						});
					}
				}
			}
		}

		private void AddCoastalCliffs(List<Splat> result, TileSet occupied)
		{
			NoiseField noiseField = random.BiasedNoise2D("SpawnRate/CoastalCliffs", config.spawnNoiseScale);
			RandomIndexer randomIndexer = random.Indexer("Size");
			RandomIndexer randomIndexer2 = random.Indexer("PlateauBulge");
			TileSet tileSet = new TileSet(tiling);
			List<(Tile, Tile)> list = new List<(Tile, Tile)>();
			foreach (Tile tile2 in tiling.Tiles)
			{
				if ((double)tile2.Data(heights) < config.coastalCliffs.minHeight || (double)tile2.Data(heights) > config.coastalCliffs.maxHeight || noiseField.Sample(tile2.Center) > config.coastalCliffs.spawnRate || tile2.GetNeighbors().All((Tile neighbor) => (double)neighbor.Data(heights) >= config.coastalCliffs.neighborHeight) || !occupied.Add(tile2))
				{
					continue;
				}
				tileSet.Add(tile2);
				foreach (Tile neighbor in tile2.GetNeighbors())
				{
					if (tileSet.Contains(neighbor))
					{
						list.Add((tile2, neighbor));
					}
				}
			}
			foreach (var item3 in list)
			{
				Tile item = item3.Item1;
				Tile item2 = item3.Item2;
				Rfloat2 v = 0.5 * (item.Center + item2.Center);
				double num = 0.5 * ((double)item.Data(heights) + (double)item2.Data(heights));
				double num2 = Rmath.Lerp(randomIndexer.Single(item.Id, item2.Id).Value, config.coastalCliffs.size);
				Rfloat2 rfloat = heights.InclineVector(item, item2);
				List<Tile> list2 = item.GetNeighbors().Intersect(item2.GetNeighbors()).ToList();
				if (list2.Count != 2)
				{
					continue;
				}
				int num3 = list2.Count((Tile neighbor) => (double)neighbor.Data(heights) >= config.coastalCliffs.neighborHeight);
				if (num3 != 0)
				{
					if (num3 != 1)
					{
						continue;
					}
					Tile tile = list2.First((Tile neighbor) => (double)neighbor.Data(heights) >= config.coastalCliffs.neighborHeight);
					rfloat += Clamp(HeightUtility.InclineVector(v, num, tile.Center, tile.Data(heights)), config.coastalCliffs.gradientClamping);
				}
				else
				{
					int num4 = Rmath.Sign(Rfloat2.Dot(Normal(item.Center, item2.Center), list2[1].Center - list2[0].Center));
					rfloat += num4 * heights.InclineVector(list2[0], list2[1]);
				}
				Rfloat2 rfloat2 = 0.5 * (item.Center + item2.Center);
				result.Add(new Plateau
				{
					pos = rfloat2,
					p1 = item.Center - rfloat2,
					p2 = item2.Center - rfloat2,
					height = num,
					radius = num2,
					incline = rfloat,
					bulgeDirection = new Direction(rfloat),
					bulgeMagnitude = randomIndexer2.Double(item.Id, item2.Id).Range(config.coastalCliffs.bulge)
				});
			}
		}

		private void AddHills(List<Splat> result, TileSet occupied)
		{
			NoiseField noiseField = random.BiasedNoise2D("SpawnRate/Hills", config.spawnNoiseScale);
			RandomIndexer randomIndexer = random.Indexer("Size");
			RandomIndexer randomIndexer2 = random.Indexer("Length");
			foreach (Tile tile in tiling.Tiles)
			{
				if (config.hills.allowedBiomes.Get(tile.Data(biomes)) && !(noiseField.Sample(tile.Center) > config.hills.spawnRate) && !tile.GetNeighbors().Any((Tile neighbor) => (double)neighbor.Data(heights) > (double)tile.Data(heights) - config.hills.minHeightDiff) && occupied.Add(tile))
				{
					Rfloat2 rfloat = Clamp(HeightUtility.ComputeRoughness(tile, heights).Gradient, config.hills.gradientClamping);
					Rfloat2 rfloat2 = rfloat.Normalized * randomIndexer2.Single(tile.Id).Value * 0.5 * config.hills.maxLength;
					result.Add(new Plateau
					{
						pos = tile.Center,
						p1 = rfloat2,
						p2 = -rfloat2,
						height = tile.Data(heights),
						radius = Rmath.Lerp(randomIndexer.Single(tile.Id).Value, config.hills.size),
						incline = rfloat,
						bulgeDirection = new Direction(rfloat),
						bulgeMagnitude = config.hills.bulge
					});
				}
			}
		}

		private static Rfloat2 Normal(Rfloat2 v1, Rfloat2 v2)
		{
			Rfloat2 rfloat = v2 - v1;
			return new Rfloat2(rfloat.y, 0.0 - rfloat.x);
		}

		private static Rfloat2 Clamp(Rfloat2 vector, ClampingConfig config)
		{
			if (vector.IsNegligible)
			{
				return config.min * new Rfloat2(-1.0, 0.0);
			}
			double length = vector.Length;
			double num = config.max - config.min;
			return config.mode switch
			{
				ClampingConfig.Mode.Linear => Rmath.Clamp(length, config.min, config.max), 
				ClampingConfig.Mode.Asymptotic => config.min + num * Functions.Saturation(length / num, config.midpoint, config.pow), 
				_ => throw new ArgumentOutOfRangeException(), 
			} / length * vector;
		}
	}
	[Serializable]
	[CompositeConfig]
	public record SplatConfig
	{
		public RegionalConfig<PlateauConfig> plateaus;

		public RegionalConfig<RiverSourceSplatStage.Config> riverSourceSplats;
	}
	public abstract record Splat
	{
		public InertFloat2 pos;

		public InertFloat height;
	}
	[Producer(new string[] { "Splats" })]
	public class SplatJoinStage : IGeneratorStage<Splat[]>
	{
		public const string OUTPUT = "Splats";

		[Resource("Splats/Plateaus")]
		public Splat[][] regionalRoughnessSplats;

		[Resource("Splats/RiverSources")]
		public Splat[][] regionalRiverSourceSplats;

		public Splat[] Compute()
		{
			List<Splat> list = new List<Splat>();
			list.AddRange(regionalRoughnessSplats.SelectMany((Splat[] splats) => splats));
			list.AddRange(regionalRiverSourceSplats.SelectMany((Splat[] splats) => splats));
			return list.ToArray();
		}
	}
}
namespace Riverheim.World.Rivers
{
	public struct Channel
	{
		public TileList path;

		public double width;
	}
	[Producer(new string[] { "Rivers/Channels" })]
	[Fork("Region")]
	public class ChannelStage : IGeneratorStage<Channel[]>
	{
		[Serializable]
		public record Config
		{
			public double width;
		}

		public const string OUTPUT = "Rivers/Channels";

		[LaneConfig]
		public Config config;

		[Random("Rivers/Channels")]
		public RandomDirectory random;

		[Resource("Regions/Tiling")]
		public Tiling tiling;

		[Resource("Flags/Base")]
		public TileData<TileFlags> flags;

		[Resource("Landmasses/MergedPartitions")]
		public TileData<int> partitions;

		public Channel[] Compute()
		{
			List<Channel> list = new List<Channel>();
			if (config.width <= 0.0)
			{
				return list.ToArray();
			}
			RandomIndexer selectionRandom = random.Indexer("Selection");
			foreach (TileSet faultLine in GetFaultLines())
			{
				if (MakeChannel(faultLine, selectionRandom, out var channel))
				{
					list.Add(channel);
				}
			}
			return list.ToArray();
		}

		private List<TileSet> GetFaultLines()
		{
			Dictionary<Rint2, TileSet> dictionary = new Dictionary<Rint2, TileSet>();
			foreach (Tile tile in tiling.Tiles)
			{
				if (tile.Data(flags).IsOcean)
				{
					continue;
				}
				int num = partitions[tile];
				foreach (Tile neighbor in tile.GetNeighbors())
				{
					if (neighbor.Data(flags).IsOcean)
					{
						continue;
					}
					int num2 = partitions[neighbor];
					if (num != num2)
					{
						Rint2 key = ((num > num2) ? new Rint2(num2, num) : new Rint2(num, num2));
						if (!dictionary.ContainsKey(key))
						{
							dictionary[key] = new TileSet(tiling);
						}
						dictionary[key].Add(tile);
					}
				}
			}
			return new List<TileSet>(dictionary.Values);
		}

		private bool MakeChannel(TileSet faultLine, RandomIndexer selectionRandom, out Channel channel)
		{
			(TileSet, TileSet) oceanCoastalTiles = GetOceanCoastalTiles(faultLine);
			if (oceanCoastalTiles.Item1.Count == 0 || oceanCoastalTiles.Item2.Count == 0)
			{
				channel = default(Channel);
				return false;
			}
			Tile origin = Select(oceanCoastalTiles.Item1, selectionRandom);
			Tile target = Select(oceanCoastalTiles.Item2, selectionRandom);
			TileList tileList = TilingUtility.FindPathToTile(origin, target, (Tile tile) => faultLine.Contains(tile) || tile == target);
			if (tileList == null)
			{
				channel = default(Channel);
				return false;
			}
			channel = new Channel
			{
				path = tileList,
				width = config.width
			};
			return true;
		}

		private Tile Select(TileSet tiles, RandomIndexer selectionRandom)
		{
			TileHeap tileHeap = new TileHeap(tiling, HeapType.Min);
			foreach (Tile item in tiles.Unordered())
			{
				tileHeap.AddOrUpdate(item, selectionRandom.Single(item.Id).Value);
			}
			return tileHeap.Pop();
		}

		private (TileSet, TileSet) GetOceanCoastalTiles(TileSet faultLine)
		{
			TileList tileList = new TileList(tiling);
			foreach (Tile item in faultLine.Unordered())
			{
				if (!item.Data(flags).IsCoast)
				{
					continue;
				}
				foreach (Tile neighbor in item.GetNeighbors())
				{
					if (neighbor.Data(flags).IsOcean)
					{
						tileList.Add(neighbor);
					}
				}
			}
			TileSet tileSet = new TileSet(tiling);
			TileSet tileSet2 = new TileSet(tiling);
			if (tileList.Count == 0)
			{
				return (tileSet, tileSet2);
			}
			TileSet tileSet3 = TileSet.Of(tileList[0]);
			foreach (Tile neighbor2 in tileList[0].GetNeighbors(3, (Tile neighbor) => neighbor.Data(flags).IsOcean))
			{
				tileSet3.Add(neighbor2);
			}
			foreach (Tile item2 in tileList)
			{
				(tileSet3.Contains(item2) ? tileSet : tileSet2).Add(item2);
			}
			return (tileSet, tileSet2);
		}
	}
	[Serializable]
	public struct PrecipitationConfig
	{
		public double noiseScale;

		public double variability;

		public double spawnMinRadius;

		public double spawnMaxRadius;

		public double basinContribution;

		public double minimum;
	}
	[Producer(new string[] { "Precipitation" })]
	[Fork("Region")]
	public class PrecipitationStage : IGeneratorStage<TileData<InertFloat>>
	{
		public const string OUTPUT = "Precipitation";

		[Random("PrecipitationMap")]
		public RandomDirectory random;

		[LaneConfig]
		public PrecipitationConfig config;

		[Resource("Regions/Tiling")]
		public Tiling tiling;

		[Resource("Landmasses/Artifacts")]
		public TileData<LandmassArtifacts> artifacts;

		public TileData<InertFloat> Compute()
		{
			NoiseField noise = random.Noise2D("Noise", config.noiseScale);
			return tiling.CreateData((Func<Tile, InertFloat>)delegate(Tile tile)
			{
				double num = Rmath.Clamp(config.variability * noise.Sample(tile.Center), Rmath.Lerp(Rmath.InverseLerp(tile.Center.Length, config.spawnMinRadius, config.spawnMaxRadius), 1.0, -1.0), 1.0);
				if (tile.Data(artifacts).isBasin)
				{
					num += config.basinContribution;
				}
				return Rmath.Max(config.minimum, 1.0 + num);
			});
		}
	}
	[Serializable]
	[CompositeConfig]
	public record RiverConfig
	{
		public RegionalConfig<PrecipitationConfig> precipitation;

		public RegionalConfig<RiverOriginFinder.Config> origins;

		public RegionalConfig<RiverFillStage.Config> floodfill;

		public RegionalConfig<RiverWidthConfig> width;

		public RegionalConfig<RiverPruneStage.Config> prune;

		public RegionalConfig<RiverCuriosityConfig> curiosities;

		public RegionalConfig<ChannelStage.Config> channels;

		public RegionalConfig<RiverPolylineIndexBuilder.Config> polylineIndex;

		public RegionalConfig<RiverMeanderer.Config> meander;

		public RegionalConfig<RiverSplineBuilder.Config> splines;
	}
	[Serializable]
	public struct RiverCuriosityConfig
	{
		public double sourceMult;

		public double sourceMinLength;

		public double sourceMaxLength;

		public double proximityMult;

		public double proximityScanRange;

		public double proximityDifferentBasinsMultiplier;

		public double proximityMinRatio;

		public double proximityMaxRatio;

		public double angleMult;

		public double angleMinDegrees;

		public double angleMaxDegrees;

		public double angleNormWidth;

		public double smoothingRange;
	}
	[Producer(new string[] { "Rivers/Curiosities" })]
	[Fork("Region")]
	public class RiverCuriosityStage : IGeneratorStage<TileData<InertFloat>>
	{
		private class CuriosityData
		{
			public readonly Dictionary<int, double> lengthsById;

			public InertFloat score;

			public double Length
			{
				get
				{
					double num = 0.0;
					foreach (double value in lengthsById.Values)
					{
						num += value;
					}
					return num;
				}
			}

			public CuriosityData(Dictionary<int, double> lengths)
			{
				lengthsById = ((lengths == null) ? new Dictionary<int, double>() : new Dictionary<int, double>(lengths));
				score = 0.0;
			}
		}

		public const string OUTPUT = "Rivers/Curiosities";

		[Config]
		public CommonConfig commonConfig;

		[LaneConfig]
		public RiverCuriosityConfig config;

		[Resource("Regions/Tiling")]
		public Tiling tiling;

		[Resource("PrunedRivers")]
		public TileData<RiverTile> rivers;

		[Resource("RiverTileMetrics")]
		public TileData<RiverBasinTile> basins;

		private MutableTileData<CuriosityData> data;

		public TileData<InertFloat> Compute()
		{
			data = tiling.CreateData<CuriosityData>();
			MutableTileData<InertFloat> mutableTileData = tiling.CreateData<InertFloat>();
			TileList tileList = FindMouths();
			foreach (Tile item in tileList)
			{
				MarkLengths(item);
			}
			foreach (Tile item2 in tileList)
			{
				MarkScores(item2);
			}
			foreach (Tile item3 in tileList)
			{
				SmoothScores(item3, mutableTileData);
			}
			return mutableTileData;
		}

		private TileList FindMouths()
		{
			TileList tileList = new TileList(tiling);
			foreach (Tile tile in tiling.Tiles)
			{
				if (tile.Data(rivers) != null && tile.Data(rivers).isRiverMouth)
				{
					tileList.Add(tile);
				}
			}
			return tileList;
		}

		private void MarkLengths(Tile tile, Dictionary<int, double> parentLengths = null, double segmentLength = 0.0)
		{
			tile.Data(data) = new CuriosityData(parentLengths);
			Dictionary<int, double> lengthsById = tile.Data(data).lengthsById;
			int riverId = tile.Data(rivers).riverId;
			if (!lengthsById.ContainsKey(riverId))
			{
				lengthsById[riverId] = segmentLength;
			}
			else
			{
				lengthsById[riverId] += segmentLength;
			}
			foreach (Tile inlet in GetInlets(tile))
			{
				MarkLengths(inlet, lengthsById, Rfloat2.Distance(tile.Center, inlet.Center));
			}
		}

		private void MarkScores(Tile tile)
		{
			double num = tile.Data(data).score;
			num += config.sourceMult * ComputeSourceScore(tile);
			num += config.proximityMult * ComputeProximityScore(tile);
			num += config.angleMult * ComputeAngleScore(tile);
			tile.Data(data).score = num;
			foreach (Tile inlet in GetInlets(tile))
			{
				MarkScores(inlet);
			}
		}

		private double ComputeSourceScore(Tile tile)
		{
			if (!tile.Data(rivers).IsRiverTermination())
			{
				return 0.0;
			}
			return Rmath.InverseLerp(tile.Data(data).Length, config.sourceMinLength, config.sourceMaxLength);
		}

		private double ComputeProximityScore(Tile tile)
		{
			double num = 0.0;
			foreach (Tile item in tile.Scan(config.proximityScanRange))
			{
				if (item.Data(data) != null)
				{
					double num2 = Rfloat2.Distance(tile.Center, item.Center);
					double num3 = GraphDistance(tile, item);
					double num4 = ((tile.Data(basins).basinId != item.Data(basins).basinId) ? config.proximityDifferentBasinsMultiplier : 1.0);
					double num5 = Rmath.InverseLerp(num3 / num2 * num4, config.proximityMinRatio, config.proximityMaxRatio);
					num += num5 * Functions.Gauss(num2 / config.proximityScanRange, 3.0);
				}
			}
			double num6 = commonConfig.tileSpacing / config.proximityScanRange;
			return num * num6 * num6;
		}

		private double ComputeAngleScore(Tile tile)
		{
			double num = 0.0;
			Direction direction = tile.DirectionTo(GetOutlet(tile));
			foreach (Tile inlet in GetInlets(tile))
			{
				double num2 = Rmath.Abs(tile.DirectionTo(inlet) - direction);
				double num3 = 1.0 - Rmath.InverseLerp(num2 * (180.0 / Math.PI), config.angleMinDegrees, config.angleMaxDegrees);
				num += num3 * (double)tile.Data(basins).width / config.angleNormWidth;
			}
			return num;
		}

		private void SmoothScores(Tile tile, MutableTileData<InertFloat> result)
		{
			double smoothingRange = config.smoothingRange;
			InertFloat score = tile.Data(data).score;
			if ((double)score > 0.0)
			{
				ref InertFloat reference = ref tile.Data(result);
				reference = (double)reference + (double)score;
				foreach (Tile item in tile.Scan(smoothingRange))
				{
					Rfloat2 rfloat = (tile.Center - item.Center) / smoothingRange;
					ref InertFloat reference2 = ref item.Data(result);
					reference2 = (double)reference2 + (double)score * Functions.Gauss2(rfloat.SqrLength, 3.0);
				}
			}
			foreach (Tile inlet in GetInlets(tile))
			{
				SmoothScores(inlet, result);
			}
		}

		private double GraphDistance(Tile from, Tile to)
		{
			Dictionary<int, double> dictionary = new Dictionary<int, double>(from.Data(data).lengthsById);
			foreach (KeyValuePair<int, double> item in to.Data(data).lengthsById)
			{
				if (!dictionary.ContainsKey(item.Key))
				{
					dictionary[item.Key] = 0.0 - item.Value;
				}
				else
				{
					dictionary[item.Key] -= item.Value;
				}
			}
			double num = 0.0;
			foreach (double value in dictionary.Values)
			{
				num += Rmath.Abs(value);
			}
			return num;
		}

		private Tile GetOutlet(Tile tile)
		{
			return tiling.GetTile(tile.Data(rivers).outletId);
		}

		private IEnumerable<Tile> GetInlets(Tile tile)
		{
			return tile.Data(rivers).inletIds.Select((int inletId) => tiling.GetTile(inletId));
		}
	}
	public class RiverFillTile
	{
		public readonly Tile outlet;

		public readonly Couple<Tile> inlets;

		public readonly InertFloat loss;

		public RiverFillTile(Tile outlet, Couple<Tile> inlets, double loss)
		{
			this.outlet = outlet;
			this.inlets = inlets;
			this.loss = loss;
		}
	}
	[Producer(new string[] { "Rivers/FloodFill" })]
	[Fork("Region")]
	public class RiverFillStage : IGeneratorStage<TileData<RiverFillTile>>
	{
		[Serializable]
		public struct Config
		{
			public double temperature;

			public double maxOriginRandomLoss;

			public RiverFillGuide.Config guide;
		}

		private class GraphNode
		{
			public readonly Tile outlet;

			public readonly TileList inlets;

			public readonly InertFloat loss;

			public GraphNode(Tile outlet, double loss)
			{
				this.outlet = outlet;
				this.loss = loss;
				inlets = new TileList(outlet.Tiling);
			}

			public RiverFillTile ToRiverFillTile()
			{
				return new RiverFillTile(outlet, Couple<Tile>.MakeCouple(inlets.ToArray()), loss);
			}
		}

		public const string OUTPUT = "Rivers/FloodFill";

		[Random("Rivers/FloodFill")]
		public RandomDirectory random;

		[LaneConfig]
		public Config config;

		[Resource("Regions/Tiling")]
		public Tiling tiling;

		[Resource("Flags/Base")]
		public TileData<TileFlags> flags;

		[Resource("LandmassIds")]
		public TileData<int> landmassIds;

		[Resource("Landmasses/Artifacts")]
		public TileData<LandmassArtifacts> artifacts;

		[Resource("Precipitation")]
		public TileData<InertFloat> precipitationMap;

		[Resource("Rivers/Origins")]
		public List<RiverOrigin> riverOrigins;

		private double Loss(TileData<GraphNode> graph, Tile tile)
		{
			return (double)graph[tile].loss - (double)precipitationMap[tile];
		}

		public TileData<RiverFillTile> Compute()
		{
			MutableTileData<GraphNode> mutableTileData = tiling.CreateData<GraphNode>();
			RiverFillGuide guide = new RiverFillGuide(random.Indexer("Guide"), config.guide);
			Dictionary<int, TileList> dictionary = InitializeOrigins(mutableTileData);
			foreach (int key in dictionary.Keys)
			{
				ProcessLandmass(mutableTileData, guide, dictionary[key]);
			}
			return mutableTileData.Transform((GraphNode node) => node?.ToRiverFillTile());
		}

		private Dictionary<int, TileList> InitializeOrigins(MutableTileData<GraphNode> graph)
		{
			RandomIndexer randomIndexer = random.Indexer("OriginRandomLoss");
			Dictionary<int, TileList> dictionary = new Dictionary<int, TileList>();
			foreach (RiverOrigin riverOrigin in riverOrigins)
			{
				int key = tiling.GetTile(riverOrigin.tileId).Data(landmassIds);
				if (!dictionary.ContainsKey(key))
				{
					dictionary[key] = new TileList(tiling);
				}
				Tile tile = tiling.GetTile(riverOrigin.tileId);
				double loss = randomIndexer.Single(riverOrigin.tileId).Range(config.maxOriginRandomLoss);
				tile.Data(graph) = new GraphNode(tiling.GetTile(riverOrigin.seawardTileId), loss);
				dictionary[key].Add(tile);
			}
			return dictionary;
		}

		private void ProcessLandmass(MutableTileData<GraphNode> graph, RiverFillGuide guide, TileList origins)
		{
			RandomTileHeap randomTileHeap = new RandomTileHeap(tiling, random.Indexer("TileSelection"), config.temperature);
			foreach (Tile origin in origins)
			{
				randomTileHeap.AddOrUpdate(origin, Loss(graph, origin));
			}
			while (randomTileHeap.Count > 0)
			{
				Tile tile = randomTileHeap.Pop();
				foreach (RiverFillGuide.Continuation item in SelectInlets(graph, guide, tile))
				{
					graph[tile].inlets.Add(item.tile);
					graph[item.tile] = new GraphNode(tile, (double)graph[tile].loss + item.loss);
					randomTileHeap.AddOrUpdate(item.tile, Loss(graph, item.tile));
				}
			}
		}

		private Couple<RiverFillGuide.Continuation> SelectInlets(TileData<GraphNode> graph, RiverFillGuide guide, Tile tile)
		{
			List<Tile> list = new List<Tile>();
			foreach (Tile neighbor in tile.GetNeighbors())
			{
				if (!(neighbor == tile.Data(graph).outlet) && RiverUtility.IsSuitableLand(flags[neighbor], artifacts[neighbor]) && neighbor.Data(graph) == null)
				{
					list.Add(neighbor);
				}
			}
			return guide.SelectConfiguration(tile, list, graph[tile].outlet);
		}
	}
	public class RiverFillGuide
	{
		[Serializable]
		public struct Config
		{
			public double temperature;

			public RiverFillGuideLossCalculator.Config loss;
		}

		public struct Continuation
		{
			public Tile tile;

			public double loss;
		}

		private class Configuration
		{
			public int id;

			public double loss;

			public Couple<Continuation> inlets;
		}

		private readonly RandomIndexer random;

		private readonly Config config;

		private readonly RiverFillGuideLossCalculator lossGuide;

		public RiverFillGuide(RandomIndexer random, Config config)
		{
			this.random = random;
			this.config = config;
			lossGuide = new RiverFillGuideLossCalculator(config.loss);
		}

		public Couple<Continuation> SelectConfiguration(Tile tile, List<Tile> candidates, Tile outlet)
		{
			if (candidates.Count == 0)
			{
				return default(Couple<Continuation>);
			}
			IEnumerable<Configuration> candidates2 = from inlet in candidates.AllPairings()
				select MakeConfiguration(tile, inlet, outlet);
			return RandomSelect(candidates2).inlets;
		}

		private Configuration MakeConfiguration(Tile tile, Couple<Tile> inlets, Tile outlet)
		{
			Direction outlet2 = outlet.DirectionTo(tile);
			Couple<Direction> inlets2 = inlets.Transform((Tile inlet) => tile.DirectionTo(inlet));
			Couple<double> inletLosses;
			return new Configuration
			{
				id = ConfigurationId(tile, inlets),
				loss = lossGuide.ConfigurationLoss(inlets2, outlet2, out inletLosses),
				inlets = inlets.Transform((int i) => new Continuation
				{
					tile = inlets[i],
					loss = inletLosses[i]
				})
			};
		}

		private static int ConfigurationId(Tile origin, Couple<Tile> inlets)
		{
			int id = origin.Id;
			int x = ((inlets.Count > 0) ? inlets[0].Id : (-1));
			int y = ((inlets.Count > 1) ? inlets[1].Id : (-1));
			return (int)HashTools.IntHash32((uint)id, x, y);
		}

		private Configuration RandomSelect(IEnumerable<Configuration> candidates)
		{
			Configuration result = null;
			double num = double.MinValue;
			foreach (Configuration candidate in candidates)
			{
				double num2 = random.Single(candidate.id).Gumbel(0.0 - candidate.loss, config.temperature);
				if (num2 > num)
				{
					num = num2;
					result = candidate;
				}
			}
			return result;
		}
	}
	public class RiverFillGuideLossCalculator
	{
		[Serializable]
		public struct Config
		{
			public double energyPenaltyPow;

			public double aestheticPenaltySteepness;

			public double haltConfigurationLoss;

			public double bendConfigurationLoss;

			public double bendDeflectionLoss;

			public double bendAestheticLoss;

			public double forkDeflectionLoss;

			public double forkImbalanceLoss;

			public double forkAestheticLoss;

			public double energyMult;

			public double continuationLoss;
		}

		private readonly Config config;

		public RiverFillGuideLossCalculator(Config config)
		{
			this.config = config;
		}

		public double ConfigurationLoss(Couple<Direction> inlets, Direction outlet, out Couple<double> inletLosses)
		{
			return inlets.Count switch
			{
				0 => HaltLoss(out inletLosses), 
				1 => BendLoss(inlets[0], outlet, out inletLosses), 
				2 => ForkLoss(inlets[0], inlets[1], outlet, out inletLosses), 
				_ => throw new IndexOutOfRangeException(), 
			};
		}

		private double HaltLoss(out Couple<double> losses)
		{
			losses = default(Couple<double>);
			return config.haltConfigurationLoss;
		}

		private double BendLoss(Directi