Decompiled source of Riverheim v1.0.0

BepInEx/plugins/Riverheim.dll

Decompiled 2 days ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using BepInEx;
using DelaunatorSharp;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Riverheim.Configuration;
using Riverheim.Configuration.Extensions;
using Riverheim.Pipeline;
using Riverheim.Pipeline.Extensions;
using Riverheim.Plugin.Rendering;
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 UnityEngine;

[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.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+979decd56006774960bb6ef32917a9b261131be4")]
[assembly: AssemblyProduct("Riverheim")]
[assembly: AssemblyTitle("Riverheim")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.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;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class ExtensionMarkerAttribute : Attribute
	{
		public ExtensionMarkerAttribute(string name)
		{
		}
	}
}
internal static class PluginInfo
{
	public const string PLUGIN_GUID = "Riverheim";

	public const string PLUGIN_NAME = "Riverheim";

	public const string PLUGIN_VERSION = "1.0.0";
}
namespace Riverheim.Plugin
{
	public static class WorldExtensions
	{
		private class ExtensionData
		{
			public WorldSettings settings;
		}

		[SpecialName]
		public sealed class <G>$7B5663A8641255449155D6EF66167945
		{
			[SpecialName]
			public static class <M>$45CD4584ACAF47103DDEBE68327735F9
			{
			}

			[ExtensionMarker("<M>$45CD4584ACAF47103DDEBE68327735F9")]
			private ExtensionData GetData()
			{
				throw null;
			}

			[ExtensionMarker("<M>$45CD4584ACAF47103DDEBE68327735F9")]
			public WorldSettings GetSettings()
			{
				throw null;
			}

			[ExtensionMarker("<M>$45CD4584ACAF47103DDEBE68327735F9")]
			public void SetSettings(WorldSettings settings)
			{
				throw null;
			}
		}

		private static readonly ConditionalWeakTable<World, ExtensionData> Storage = new ConditionalWeakTable<World, ExtensionData>();

		private static WorldSettings GetDefaultSettings(World world)
		{
			if (world.m_name.StartsWith("RHEXP"))
			{
				return new WorldSettings("builtin.experimental", 0);
			}
			return ConfigManager.DefaultWorldSettings();
		}

		private static ExtensionData GetData(this World world)
		{
			return Storage.GetValue(world, (World wld) => new ExtensionData
			{
				settings = GetDefaultSettings(wld)
			});
		}

		public static WorldSettings GetSettings(this World world)
		{
			return world.GetData().settings;
		}

		public static void SetSettings(this World world, WorldSettings settings)
		{
			world.GetData().settings = settings;
		}
	}
	[BepInPlugin("dev.gurebu.riverheim.stable", "Riverheim", "1.0.0")]
	[BepInIncompatibility("dev.gurebu.riverheim.unstable")]
	[BepInIncompatibility("BetterContinents")]
	public class RiverheimPlugin : BaseUnityPlugin
	{
		[HarmonyPatch(typeof(WorldGenerator))]
		private static class WorldGeneratorPatch
		{
			private static GeneratorPipeline BuildPipeline()
			{
				string[] resultResourceIds = new string[1] { "Rendering/WorldRenderer" };
				GeneratorPipeline pipeline = new GeneratorPipeline.Builder(resultResourceIds).SetParallel(value: true).SetClearUnneededCache(value: true).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;
			}

			[HarmonyPrefix]
			[HarmonyPatch("Pregenerate")]
			private static bool Pregenerate(WorldGenerator __instance)
			{
				World world = __instance.m_world;
				if (world.m_menu)
				{
					return true;
				}
				if (world.m_seed == 0)
				{
					Logger.LogWarning("Seed is 0 for world '" + world.m_name + "', but not a menu world, must be an error");
					Logger.LogWarning("Skipping world generation, treating as a vanilla world");
					return true;
				}
				GeneratorPipeline generatorPipeline = BuildPipeline();
				WorldSettings settings = world.GetSettings();
				Logger.LogInfo($"Generating world '{world.m_name}'/{world.m_seed} with preset: '{settings.Preset}' v.{settings.PresetVersion}...");
				generatorPipeline.Compute(world.m_seed, ConfigManager.GetConfig(settings));
				__instance.SetRenderer(new ValheimWorldRenderer(generatorPipeline.GetResult<WorldRenderer>("Rendering/WorldRenderer"), 30f));
				return false;
			}

			[HarmonyPrefix]
			[HarmonyPatch("GetBiomeHeight")]
			private static bool GetBiomeHeight(WorldGenerator __instance, Biome biome, float wx, float wy, out Color mask, ref float __result)
			{
				//IL_0002: Unknown result type (might be due to invalid IL or missing references)
				//IL_0007: Unknown result type (might be due to invalid IL or missing references)
				mask = Color.black;
				ValheimWorldRenderer renderer = __instance.GetRenderer();
				if (renderer == null)
				{
					return true;
				}
				__result = renderer.GetHeight(wx, wy, out mask);
				return false;
			}

			[HarmonyPrefix]
			[HarmonyPatch("GetHeight")]
			[HarmonyPatch(new Type[]
			{
				typeof(float),
				typeof(float)
			})]
			private static bool GetHeight(WorldGenerator __instance, float wx, float wy, ref float __result)
			{
				ValheimWorldRenderer renderer = __instance.GetRenderer();
				if (renderer == null)
				{
					return true;
				}
				__result = renderer.GetHeight(wx, wy, out var _);
				return false;
			}

			[HarmonyPrefix]
			[HarmonyPatch("GetHeight")]
			[HarmonyPatch(/*Could not decode attribute arguments.*/)]
			private static bool GetHeightWithMask(WorldGenerator __instance, float wx, float wy, out Color mask, ref float __result)
			{
				//IL_0001: Unknown result type (might be due to invalid IL or missing references)
				//IL_0006: Unknown result type (might be due to invalid IL or missing references)
				mask = Color.black;
				ValheimWorldRenderer renderer = __instance.GetRenderer();
				if (renderer == null)
				{
					return true;
				}
				__result = renderer.GetHeight(wx, wy, out mask);
				return false;
			}

			[HarmonyPrefix]
			[HarmonyPatch("GetBiome", new Type[]
			{
				typeof(float),
				typeof(float),
				typeof(float),
				typeof(bool)
			})]
			private static bool GetBiome(WorldGenerator __instance, float wx, float wy, ref Biome __result)
			{
				//IL_0010: Unknown result type (might be due to invalid IL or missing references)
				//IL_0016: Expected I4, but got Unknown
				ValheimWorldRenderer renderer = __instance.GetRenderer();
				if (renderer == null)
				{
					return true;
				}
				__result = (Biome)(int)renderer.GetBiome(wx, wy);
				return false;
			}

			[HarmonyPrefix]
			[HarmonyPatch("GetForestFactor")]
			private static bool GetForestFactor(ref Vector3 pos, ref float __result)
			{
				ValheimWorldRenderer renderer = WorldGenerator.instance.GetRenderer();
				if (renderer == null)
				{
					return true;
				}
				__result = renderer.GetForestFactor(pos.x, pos.z);
				return false;
			}

			[HarmonyPrefix]
			[HarmonyPatch("GetAshlandsHeight")]
			private static bool GetAshlandsHeight(WorldGenerator __instance, float wx, float wy, out Color mask, ref float __result)
			{
				//IL_0001: Unknown result type (might be due to invalid IL or missing references)
				//IL_0006: Unknown result type (might be due to invalid IL or missing references)
				mask = Color.black;
				ValheimWorldRenderer renderer = __instance.GetRenderer();
				if (renderer == null)
				{
					return true;
				}
				__result = renderer.GetHeight(wx, wy, out mask);
				return false;
			}
		}

		[HarmonyPatch(typeof(World))]
		private static class WorldSaveLoadPatch
		{
			private const int CUSTOM_WORLDGEN_MARKER = -1;

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

			private static void Log(string message)
			{
				Logger.LogInfo(message);
			}

			private static void WriteHeader(ZPackage package)
			{
				package.Write(-1);
				package.Write("dev.gurebu.riverheim.stable");
			}

			private static bool ReadHeader(ZPackage package)
			{
				int num = package.ReadInt();
				if (num != -1)
				{
					Log($"World doesn't have custom worldgen marker {-1}, instead got: {num}, skipping");
					return false;
				}
				string text = package.ReadString();
				if (text != "dev.gurebu.riverheim.stable")
				{
					Log("Custom worldgen '" + text + "' does not match 'dev.gurebu.riverheim.stable', skipping");
					return false;
				}
				return true;
			}

			private static void WriteWorldSettings(ZPackage package, World world)
			{
				package.Write(world.GetSettings());
			}

			private static bool ReadWorldSettings(ZPackage package, World world)
			{
				try
				{
					world.SetSettings(package.ReadWorldSettings());
				}
				catch (WorldSettingSerialization.Error error)
				{
					Log("World settings malformed or unsupported: " + error.Message + ", skipping");
					return false;
				}
				return true;
			}

			public static IEnumerable<CodeInstruction> TranspileWorldSave(IEnumerable<CodeInstruction> instructions)
			{
				//IL_0002: Unknown result type (might be due to invalid IL or missing references)
				//IL_000e: Expected O, but got Unknown
				CodeInstruction ld_ZPackage;
				CodeInstruction ld_World;
				return CodeMatcherExtensions.AdvanceToAfterZPackageConstructor(new CodeMatcher(instructions, (ILGenerator)null), out ld_ZPackage).InsertAndAdvance((CodeInstruction[])(object)new CodeInstruction[1] { ld_ZPackage }).InsertAndAdvance((CodeInstruction[])(object)new CodeInstruction[1] { CodeInstruction.Call(typeof(WorldSaveLoadPatch), "WriteHeader", (Type[])null, (Type[])null) })
					.AdvanceToAfterWorldVersionWrite(out ld_World)
					.InsertAndAdvance((CodeInstruction[])(object)new CodeInstruction[1] { ld_ZPackage })
					.InsertAndAdvance((CodeInstruction[])(object)new CodeInstruction[1] { ld_World })
					.InsertAndAdvance((CodeInstruction[])(object)new CodeInstruction[1] { CodeInstruction.Call(typeof(WorldSaveLoadPatch), "WriteWorldSettings", (Type[])null, (Type[])null) })
					.DebugLog()
					.InstructionEnumeration();
			}

			[HarmonyTranspiler]
			[HarmonyPatch("LoadWorld")]
			private static IEnumerable<CodeInstruction> TranspileWorldLoad(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
			{
				//IL_0002: Unknown result type (might be due to invalid IL or missing references)
				//IL_000e: Expected O, but got Unknown
				//IL_0060: Unknown result type (might be due to invalid IL or missing references)
				//IL_0066: Expected O, but got Unknown
				//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
				//IL_00cd: Expected O, but got Unknown
				Label label;
				CodeInstruction ld_ZPackage;
				CodeInstruction ld_World;
				return CodeMatcherExtensions.MatchBadVersionWorldConstructor(new CodeMatcher(instructions, generator), out label).Start().AdvanceToAfterZPackageConstructor(out ld_ZPackage)
					.InsertAndAdvance((CodeInstruction[])(object)new CodeInstruction[1] { ld_ZPackage })
					.InsertAndAdvance((CodeInstruction[])(object)new CodeInstruction[1] { CodeInstruction.Call(typeof(WorldSaveLoadPatch), "ReadHeader", (Type[])null, (Type[])null) })
					.InsertAndAdvance((CodeInstruction[])(object)new CodeInstruction[1]
					{
						new CodeInstruction(OpCodes.Brfalse_S, (object)label)
					})
					.AdvanceToAfterDefaultWorldConstructor(out ld_World)
					.InsertAndAdvance((CodeInstruction[])(object)new CodeInstruction[1] { ld_ZPackage })
					.InsertAndAdvance((CodeInstruction[])(object)new CodeInstruction[1] { ld_World })
					.InsertAndAdvance((CodeInstruction[])(object)new CodeInstruction[1] { CodeInstruction.Call(typeof(WorldSaveLoadPatch), "ReadWorldSettings", (Type[])null, (Type[])null) })
					.InsertAndAdvance((CodeInstruction[])(object)new CodeInstruction[1]
					{
						new CodeInstruction(OpCodes.Brfalse_S, (object)label)
					})
					.DebugLog()
					.InstructionEnumeration();
			}
		}

		[HarmonyPatch(typeof(World))]
		private static class WorldServerLoadPatch
		{
			[HarmonyPrefix]
			[HarmonyPatch("GetCreateWorld")]
			private static void GetCreateWorld(ref World __result, string name, FileSource source)
			{
				//IL_001e: Unknown result type (might be due to invalid IL or missing references)
				//IL_002e: Unknown result type (might be due to invalid IL or missing references)
				SaveWithBackups val = default(SaveWithBackups);
				if (SaveSystem.TryGetSaveByName(name, (SaveDataType)0, ref val) && !val.IsDeleted)
				{
					__result = World.LoadWorld(val);
					if ((int)__result.m_dataError != 0)
					{
						throw new Exception($"Failed to load world with name \"{name}\", data error {__result.m_dataError}.");
					}
				}
			}
		}

		[HarmonyPatch(typeof(ZNet))]
		private static class ZNetPatch
		{
			private static readonly Version LocalVersion = new Version("1.0.0");

			private static readonly ConditionalWeakTable<ZRpc, Version> PeerVersions = new ConditionalWeakTable<ZRpc, Version>();

			private static bool VersionIsCompatible(Version remoteVersion)
			{
				if (LocalVersion.Major == remoteVersion.Major)
				{
					return LocalVersion.Minor == remoteVersion.Minor;
				}
				return false;
			}

			private static void RPC_AssertModVersion(ZRpc sender, string versionString)
			{
				Version version = new Version(versionString);
				PeerVersions.Add(sender, version);
				if (!VersionIsCompatible(version))
				{
					Logger.LogWarning($"Riverheim plugin version mismatch! Local version: {LocalVersion}, remote: {version}");
					sender.Invoke("Error", new object[1] { (object)(ConnectionStatus)3 });
				}
			}

			[HarmonyPriority(700)]
			[HarmonyPrefix]
			[HarmonyPatch("OnNewConnection")]
			private static void OnNewConnection(ZNet __instance, ZNetPeer peer)
			{
				peer.m_rpc.Register<string>("AssertRiverheimVersion", (Action<ZRpc, string>)RPC_AssertModVersion);
			}

			[HarmonyPriority(700)]
			[HarmonyPrefix]
			[HarmonyPatch("RPC_ClientHandshake")]
			private static void RPC_ClientHandshake(ZNet __instance, ZRpc rpc)
			{
				rpc.Invoke("AssertRiverheimVersion", new object[1] { "1.0.0" });
			}

			[HarmonyPriority(700)]
			[HarmonyPrefix]
			[HarmonyPatch("RPC_ServerHandshake")]
			private static void RPC_ServerHandshake(ZNet __instance, ZRpc rpc)
			{
				rpc.Invoke("AssertRiverheimVersion", new object[1] { "1.0.0" });
			}

			[HarmonyPriority(700)]
			[HarmonyPrefix]
			[HarmonyPatch("SendPeerInfo")]
			private static bool SendPeerInfo(ZNet __instance, ZRpc rpc)
			{
				if (ZNet.instance.IsServer())
				{
					return true;
				}
				if (PeerVersions.TryGetValue(rpc, out var value) && VersionIsCompatible(value))
				{
					return true;
				}
				Logger.LogWarning($"Riverheim plugin version mismatch! Local version: {LocalVersion}, remote: {value}");
				rpc.Invoke("Disconnect", Array.Empty<object>());
				return false;
			}

			[HarmonyPriority(700)]
			[HarmonyPrefix]
			[HarmonyPatch("RPC_PeerInfo")]
			private static bool RPC_PeerInfo(ZNet __instance, ZRpc rpc)
			{
				if (!ZNet.instance.IsServer())
				{
					return true;
				}
				if (PeerVersions.TryGetValue(rpc, out var value) && VersionIsCompatible(value))
				{
					return true;
				}
				Logger.LogWarning($"Riverheim plugin version mismatch! Local version: {LocalVersion}, remote: {value}");
				rpc.Invoke("Error", new object[1] { (object)(ConnectionStatus)3 });
				return false;
			}
		}

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

		private const string GUID_UNSTABLE = "dev.gurebu.riverheim.unstable";

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

		private const string GUID_INCOMPATIBLE = "dev.gurebu.riverheim.unstable";

		private const string PLUGIN_NAME = "Riverheim";

		public void Awake()
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Expected O, but got Unknown
			Harmony val = new Harmony("dev.gurebu.riverheim.stable");
			val.PatchAll();
			val.Patch((MethodBase)FindWorldSaveMethod(), (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(ReplaceWorldSaveMethod()), (HarmonyMethod)null, (HarmonyMethod)null);
			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);
				}
			});
		}

		private MethodInfo FindWorldSaveMethod()
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			GameVersion currentVersion = Version.CurrentVersion;
			string text;
			Type[] array;
			if (currentVersion >= new GameVersion(0, 221, 13))
			{
				text = "SaveWorldFWLData";
				array = new Type[2]
				{
					typeof(DateTime),
					typeof(FileWriter).MakeByRefType()
				};
			}
			else
			{
				text = "SaveWorldMetaData";
				array = new Type[4]
				{
					typeof(DateTime),
					typeof(bool),
					typeof(bool).MakeByRefType(),
					typeof(FileWriter).MakeByRefType()
				};
			}
			((BaseUnityPlugin)this).Logger.LogDebug((object)$"Patching world load method: '{text}' for game version {currentVersion}");
			return AccessTools.Method(typeof(World), text, array, (Type[])null);
		}

		private static MethodInfo ReplaceWorldSaveMethod()
		{
			return AccessTools.Method(typeof(WorldSaveLoadPatch), "TranspileWorldSave", (Type[])null, (Type[])null);
		}
	}
	public static class WorldGeneratorExtensions
	{
		[SpecialName]
		public sealed class <G>$6FCB1B340A09A2B299219C5F5C371FD8
		{
			[SpecialName]
			public static class <M>$6F6DF1476D6644A49741E96742C809A0
			{
			}

			[ExtensionMarker("<M>$6F6DF1476D6644A49741E96742C809A0")]
			public void SetRenderer(ValheimWorldRenderer renderer)
			{
				throw null;
			}

			[ExtensionMarker("<M>$6F6DF1476D6644A49741E96742C809A0")]
			public ValheimWorldRenderer GetRenderer()
			{
				throw null;
			}
		}

		private static readonly ConditionalWeakTable<WorldGenerator, ValheimWorldRenderer> Renderers = new ConditionalWeakTable<WorldGenerator, ValheimWorldRenderer>();

		public static void SetRenderer(this WorldGenerator worldGenerator, ValheimWorldRenderer renderer)
		{
			Renderers.AddOrUpdate(worldGenerator, renderer);
		}

		public static ValheimWorldRenderer GetRenderer(this WorldGenerator worldGenerator)
		{
			if (worldGenerator == null)
			{
				return null;
			}
			Renderers.TryGetValue(worldGenerator, out var value);
			return value;
		}
	}
	internal static class CodeMatcherExtensions
	{
		[SpecialName]
		public sealed class <G>$6C7DE3593F499BD1D1FE1E0A0EDB9649
		{
			[SpecialName]
			public static class <M>$F04B44C5FDA9C4CCFD20224FF826B3CB
			{
			}

			[ExtensionMarker("<M>$F04B44C5FDA9C4CCFD20224FF826B3CB")]
			public CodeMatcher DebugLog()
			{
				throw null;
			}

			[ExtensionMarker("<M>$F04B44C5FDA9C4CCFD20224FF826B3CB")]
			public CodeMatcher AdvanceToAfterZPackageConstructor(out CodeInstruction ld_ZPackage)
			{
				throw null;
			}

			[ExtensionMarker("<M>$F04B44C5FDA9C4CCFD20224FF826B3CB")]
			public CodeMatcher AdvanceToAfterWorldVersionWrite(out CodeInstruction ld_World)
			{
				throw null;
			}

			[ExtensionMarker("<M>$F04B44C5FDA9C4CCFD20224FF826B3CB")]
			public CodeMatcher MatchBadVersionWorldConstructor(out Label label)
			{
				throw null;
			}

			[ExtensionMarker("<M>$F04B44C5FDA9C4CCFD20224FF826B3CB")]
			public CodeMatcher AdvanceToAfterDefaultWorldConstructor(out CodeInstruction ld_World)
			{
				throw null;
			}
		}

		private static CodeInstruction MatchStoreWithLoad(CodeInstruction instruction)
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Expected O, but got Unknown
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Expected O, but got Unknown
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Expected O, but got Unknown
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Expected O, but got Unknown
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Expected O, but got Unknown
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00be: Expected O, but got Unknown
			if (instruction.opcode == OpCodes.Stloc_0)
			{
				return new CodeInstruction(OpCodes.Ldloc_0, (object)null);
			}
			if (instruction.opcode == OpCodes.Stloc_1)
			{
				return new CodeInstruction(OpCodes.Ldloc_1, (object)null);
			}
			if (instruction.opcode == OpCodes.Stloc_2)
			{
				return new CodeInstruction(OpCodes.Ldloc_2, (object)null);
			}
			if (instruction.opcode == OpCodes.Stloc_3)
			{
				return new CodeInstruction(OpCodes.Ldloc_3, (object)null);
			}
			if (instruction.opcode == OpCodes.Stloc_S)
			{
				return new CodeInstruction(OpCodes.Ldloc_S, instruction.operand);
			}
			if (instruction.opcode == OpCodes.Stloc)
			{
				return new CodeInstruction(OpCodes.Ldloc, instruction.operand);
			}
			throw new InvalidOperationException($"Not a store instruction: {instruction.opcode}");
		}

		public static CodeMatcher DebugLog(this CodeMatcher matcher)
		{
			return matcher;
		}

		public static CodeMatcher AdvanceToAfterZPackageConstructor(this CodeMatcher matcher, out CodeInstruction ld_ZPackage)
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Expected O, but got Unknown
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Expected O, but got Unknown
			matcher.MatchForward(true, (CodeMatch[])(object)new CodeMatch[2]
			{
				new CodeMatch((Func<CodeInstruction, bool>)((CodeInstruction instruction) => !(instruction.opcode != OpCodes.Newobj) && ((ConstructorInfo)instruction.operand).DeclaringType == typeof(ZPackage)), (string)null),
				new CodeMatch((Func<CodeInstruction, bool>)((CodeInstruction instruction) => CodeInstructionExtensions.IsStloc(instruction, (LocalBuilder)null)), (string)null)
			});
			if (matcher.IsInvalid)
			{
				throw new InvalidOperationException("Error patching, unable to find zPackage instancing");
			}
			ld_ZPackage = MatchStoreWithLoad(matcher.Instruction);
			return matcher.Advance(1);
		}

		public static CodeMatcher AdvanceToAfterWorldVersionWrite(this CodeMatcher matcher, out CodeInstruction ld_World)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Expected O, but got Unknown
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Expected O, but got Unknown
			ld_World = new CodeInstruction(OpCodes.Ldarg_0, (object)null);
			matcher.MatchForward(true, (CodeMatch[])(object)new CodeMatch[1]
			{
				new CodeMatch((Func<CodeInstruction, bool>)delegate(CodeInstruction instruction)
				{
					if (instruction.opcode != OpCodes.Callvirt)
					{
						return false;
					}
					return ((MethodInfo)instruction.operand).DeclaringType == typeof(ZPackage) && ((MethodInfo)instruction.operand).Name == "Write" && ((MethodInfo)instruction.operand).GetParameters()[0].ParameterType == typeof(int);
				}, (string)null)
			});
			return matcher.Advance(1);
		}

		public static CodeMatcher MatchBadVersionWorldConstructor(this CodeMatcher matcher, out Label label)
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Expected O, but got Unknown
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Expected O, but got Unknown
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Expected O, but got Unknown
			matcher.MatchForward(false, (CodeMatch[])(object)new CodeMatch[3]
			{
				new CodeMatch((Func<CodeInstruction, bool>)((CodeInstruction instruction) => CodeInstructionExtensions.IsLdarg(instruction, (int?)null)), (string)null),
				new CodeMatch((Func<CodeInstruction, bool>)((CodeInstruction instruction) => instruction.opcode == OpCodes.Ldc_I4_1 || CodeInstructionExtensions.Is(instruction, OpCodes.Ldc_I4, (object)1) || CodeInstructionExtensions.Is(instruction, OpCodes.Ldc_I4_S, (object)1)), (string)null),
				new CodeMatch((Func<CodeInstruction, bool>)delegate(CodeInstruction instruction)
				{
					if (instruction.opcode != OpCodes.Newobj)
					{
						return false;
					}
					ConstructorInfo constructorInfo = (ConstructorInfo)instruction.operand;
					if (constructorInfo.DeclaringType != typeof(World))
					{
						return false;
					}
					ParameterInfo[] parameters = constructorInfo.GetParameters();
					if (parameters.Length != 2)
					{
						return false;
					}
					return parameters[0].ParameterType == typeof(SaveWithBackups) && parameters[1].ParameterType == typeof(SaveDataError);
				}, (string)null)
			});
			if (matcher.IsInvalid)
			{
				throw new InvalidOperationException("Error patching, unable to find World instancing");
			}
			matcher.CreateLabel(ref label);
			return matcher;
		}

		public static CodeMatcher AdvanceToAfterDefaultWorldConstructor(this CodeMatcher matcher, out CodeInstruction ld_World)
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Expected O, but got Unknown
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Expected O, but got Unknown
			matcher.MatchForward(true, (CodeMatch[])(object)new CodeMatch[2]
			{
				new CodeMatch((Func<CodeInstruction, bool>)delegate(CodeInstruction instruction)
				{
					if (instruction.opcode != OpCodes.Newobj)
					{
						return false;
					}
					ConstructorInfo constructorInfo = (ConstructorInfo)instruction.operand;
					return !(constructorInfo.DeclaringType != typeof(World)) && constructorInfo.GetParameters().Length == 0;
				}, (string)null),
				new CodeMatch((Func<CodeInstruction, bool>)((CodeInstruction instruction) => CodeInstructionExtensions.IsStloc(instruction, (LocalBuilder)null)), (string)null)
			});
			ld_World = MatchStoreWithLoad(matcher.Instruction);
			return matcher.Advance(1);
		}
	}
	internal static class ZPackageExtensions
	{
		[SpecialName]
		public sealed class <G>$3129DFD62690FF8C352496DD2A72ABBA
		{
			[SpecialName]
			public static class <M>$26CCCE991BE6EACF57A91F03469C03EA
			{
			}

			[ExtensionMarker("<M>$26CCCE991BE6EACF57A91F03469C03EA")]
			public void Write(WorldSettings settingsObject)
			{
				throw null;
			}

			[ExtensionMarker("<M>$26CCCE991BE6EACF57A91F03469C03EA")]
			public WorldSettings ReadWorldSettings()
			{
				throw null;
			}
		}

		public static void Write(this ZPackage package, WorldSettings settingsObject)
		{
			WorldSettingSerialization.Setting[] array = WorldSettingSerialization.Serialize(settingsObject);
			package.WriteNumItems(array.Length);
			WorldSettingSerialization.Setting[] array2 = array;
			for (int i = 0; i < array2.Length; i++)
			{
				WorldSettingSerialization.Setting setting = array2[i];
				package.Write(setting.name);
				switch (WorldSettingSerialization.GetSettingType(setting.name))
				{
				case WorldSettings.SettingType.String:
					package.Write((string)setting.value);
					break;
				case WorldSettings.SettingType.Int:
					package.Write((int)setting.value);
					break;
				case WorldSettings.SettingType.Bool:
					package.Write((bool)setting.value);
					break;
				case WorldSettings.SettingType.Float:
					package.Write((float)setting.value);
					break;
				case WorldSettings.SettingType.Double:
					package.Write((double)setting.value);
					break;
				default:
					throw new ArgumentOutOfRangeException();
				}
			}
		}

		public static WorldSettings ReadWorldSettings(this ZPackage package)
		{
			WorldSettingSerialization.Setting[] array = new WorldSettingSerialization.Setting[package.ReadNumItems()];
			for (int i = 0; i < array.Length; i++)
			{
				string text = package.ReadString();
				object value = WorldSettingSerialization.GetSettingType(text) switch
				{
					WorldSettings.SettingType.String => package.ReadString(), 
					WorldSettings.SettingType.Int => package.ReadInt(), 
					WorldSettings.SettingType.Bool => package.ReadBool(), 
					WorldSettings.SettingType.Float => package.ReadSingle(), 
					WorldSettings.SettingType.Double => package.ReadDouble(), 
					_ => throw new ArgumentOutOfRangeException(), 
				};
				array[i] = new WorldSettingSerialization.Setting
				{
					name = text,
					value = value
				};
			}
			return WorldSettingSerialization.Deserialize(array);
		}
	}
}
namespace Riverheim.Plugin.Rendering
{
	public class ValheimWorldRenderer(WorldRenderer renderer, float heightOffset)
	{
		private const double FOREST_FACTOR_MULTIPLIER = 2.15;

		private readonly ThreadLocal<(Rfloat2, IWorldGen.PointData)> cache = new ThreadLocal<(Rfloat2, IWorldGen.PointData)>();

		private static Biome ToValheim(Biome biome)
		{
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			switch (biome)
			{
			case Biome.Meadows:
			case Biome.Marsh:
				return (Biome)1;
			case Biome.Swamp:
				return (Biome)2;
			case Biome.Mountain:
				return (Biome)4;
			case Biome.Forest:
				return (Biome)8;
			case Biome.Plains:
				return (Biome)16;
			case Biome.Ashlands:
			case Biome.Lavaplain:
				return (Biome)32;
			case Biome.DeepNorth:
				return (Biome)64;
			case Biome.Ocean:
				return (Biome)256;
			case Biome.Mistlands:
			case Biome.Mistglade:
				return (Biome)512;
			default:
				return (Biome)0;
			}
		}

		public float GetHeight(float wx, float wy, out Color mask)
		{
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			Rfloat2 rfloat = new Rfloat2(wx, wy);
			if (!cache.IsValueCreated || cache.Value.Item1 != rfloat)
			{
				cache.Value = (rfloat, renderer.RenderPoint(rfloat));
			}
			IWorldGen.PointData item = cache.Value.Item2;
			mask = new Color((float)item.dirt, 0f, (float)item.pave, (float)Math.Max(item.lava, item.fertility));
			return heightOffset + (float)item.height;
		}

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

		public float GetForestFactor(float wx, float wy)
		{
			return (float)(2.15 * (1.0 - renderer.GetForestation(new Rfloat2(wx, wy))));
		}
	}
}
namespace Riverheim.Plugin.Compat
{
	public static class ModCompat
	{
		public const string GUID_BETTER_CONTINENTS = "BetterContinents";

		public const string GUID_EWS = "expand_world_size";

		public const string GUID_EWD = "expand_world_data";
	}
}
namespace System.Runtime.CompilerServices
{
	[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
	internal sealed class IgnoresAccessChecksToAttribute : Attribute
	{
		public IgnoresAccessChecksToAttribute(string assemblyName)
		{
		}
	}
	[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
{
	[Serializable]
	public struct CommonConfig
	{
		public double worldSize;

		public double tileSpacing;

		public double mountainHeight;

		public double oceanDepth;

		public double startingAreaRadius;
	}
	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);
		}
	}
	public interface IWorldGen
	{
		public struct PointData
		{
			public Biome biome;

			public double height;

			public double fertility;

			public double lava;

			public double dirt;

			public double pave;
		}

		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.