Decompiled source of Riverheim Unstable v0.15.0

BepInEx/plugins/Riverheim.dll

Decompiled a day ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Numerics;
using System.Reflection;
using System.Reflection.Emit;
using System.Resources;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Cryptography;
using System.Security.Permissions;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using BepInEx;
using DelaunatorSharp;
using FxResources.Microsoft.Bcl.HashCode;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Riverheim.Configuration;
using Riverheim.Configuration.Extensions;
using Riverheim.Pipeline;
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(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyCompany("Riverheim")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Comprehensive terrain generator for Valheim")]
[assembly: AssemblyFileVersion("0.15.0.0")]
[assembly: AssemblyInformationalVersion("0.15.0+0679edb60f99efdae1d9deef078828ceaf44a3a8")]
[assembly: AssemblyProduct("Riverheim")]
[assembly: AssemblyTitle("Riverheim")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.15.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
internal static class PluginInfo
{
	public const string PLUGIN_GUID = "Riverheim";

	public const string PLUGIN_NAME = "Riverheim";

	public const string PLUGIN_VERSION = "0.15.0";
}
namespace Riverheim.Plugin
{
	[BepInPlugin("dev.gurebu.riverheim.unstable", "Riverheim.Unstable", "0.15.0")]
	[BepInIncompatibility("dev.gurebu.riverheim")]
	public class RiverheimPlugin : BaseUnityPlugin
	{
		public class ValheimWorldRenderer
		{
			[CompilerGenerated]
			private WorldRenderer <renderer>P;

			[CompilerGenerated]
			private float <heightOffset>P;

			private const double FOREST_FACTOR_MULTIPLIER = 2.15;

			private readonly float baseHeightScale;

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

			public ValheimWorldRenderer(WorldRenderer renderer, float baseHeightMultiplier, float heightOffset)
			{
				<renderer>P = renderer;
				<heightOffset>P = heightOffset;
				baseHeightScale = 1f / baseHeightMultiplier;
				cache = new ThreadLocal<(Rfloat2, IWorldGen.PointData)>();
				base..ctor();
			}

			private static Biome ToValheim(Biome biome)
			{
				//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_0047: Unknown result type (might be due to invalid IL or missing references)
				//IL_004b: Unknown result type (might be due to invalid IL or missing references)
				//IL_0050: Unknown result type (might be due to invalid IL or missing references)
				//IL_0055: Unknown result type (might be due to invalid IL or missing references)
				//IL_005a: Unknown result type (might be due to invalid IL or missing references)
				//IL_0062: Unknown result type (might be due to invalid IL or missing references)
				//IL_006a: Unknown result type (might be due to invalid IL or missing references)
				//IL_006f: Unknown result type (might be due to invalid IL or missing references)
				//IL_006e: Unknown result type (might be due to invalid IL or missing references)
				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.BlackForest:
					return (Biome)8;
				case Biome.Plains:
					return (Biome)16;
				case Biome.AshLands:
				case Biome.BoilingOcean:
				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 GetBaseHeight(float wx, float wy)
			{
				return baseHeightScale * (<heightOffset>P + (float)<renderer>P.GetBaseHeight(new Rfloat2(wx, wy)));
			}

			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>P.RenderPoint(rfloat));
				}
				IWorldGen.PointData item = cache.Value.Item2;
				mask = new Color((float)item.dirt, (float)item.pave, 0f, (float)Math.Max(item.lava, item.fertility));
				return <heightOffset>P + (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>P.GetBiome(new Rfloat2(wx, wy)));
			}

			public float GetForestFactor(float wx, float wy)
			{
				return (float)(2.15 * (1.0 - <renderer>P.GetForestation(new Rfloat2(wx, wy))));
			}
		}

		[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)
			{
				GeneratorPipeline generatorPipeline = BuildPipeline();
				string name = __instance.m_world.m_name;
				string text = "builtin.amur";
				if (name.StartsWith("RHEXP"))
				{
					text = "builtin.experimental";
				}
				Logger.LogInfo("Generating world '" + name + "' with preset: '" + text + "'...");
				WorldSettings settings = new WorldSettings(text, 0);
				generatorPipeline.Compute(__instance.GetSeed(), ConfigManager.GetConfig(settings));
				__instance.SetRenderer(new ValheimWorldRenderer(generatorPipeline.GetResult<WorldRenderer>("Rendering/WorldRenderer"), WorldGenerator.GetHeightMultiplier(), 30f));
				return false;
			}

			[HarmonyPrefix]
			[HarmonyPatch("GetBaseHeight")]
			private static bool GetBaseHeight(WorldGenerator __instance, float wx, float wy, bool menuTerrain, ref float __result)
			{
				if (!menuTerrain)
				{
					ValheimWorldRenderer renderer = __instance.GetRenderer();
					if (renderer != null)
					{
						__result = renderer.GetBaseHeight(wx, wy);
						return false;
					}
				}
				return true;
			}

			[HarmonyPrefix]
			[HarmonyPatch("GetBiomeHeight")]
			private static bool GetBiomeHeight(WorldGenerator __instance, Biome biome, float wx, float wy, out Color mask, ref float __result, World ___m_world)
			{
				//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;
				if (!___m_world.m_menu)
				{
					ValheimWorldRenderer renderer = __instance.GetRenderer();
					if (renderer != null)
					{
						__result = renderer.GetHeight(wx, wy, out mask);
						return false;
					}
				}
				return true;
			}

			[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, World ___m_world)
			{
				//IL_0019: Unknown result type (might be due to invalid IL or missing references)
				//IL_001f: Expected I4, but got Unknown
				if (!___m_world.m_menu)
				{
					ValheimWorldRenderer renderer = __instance.GetRenderer();
					if (renderer != null)
					{
						__result = (Biome)(int)renderer.GetBiome(wx, wy);
						return false;
					}
				}
				return true;
			}

			[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;
			}

			[HarmonyPostfix]
			[HarmonyPatch("Initialize")]
			private static void Initialize()
			{
			}
		}

		private static class ExpressPerfTest
		{
			private const int COUNT = 1000000;

			[MethodImpl(MethodImplOptions.NoOptimization)]
			public static void Measure(WorldGenerator worldGen)
			{
				//IL_006d: Unknown result type (might be due to invalid IL or missing references)
				//IL_0072: Unknown result type (might be due to invalid IL or missing references)
				//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
				//IL_00c6: Expected I4, but got Unknown
				Logger.LogInfo("Express testing performance...");
				float num = (float)ConfigManager.GetConfig(ConfigManager.DefaultWorldSettings()).world.common.worldSize;
				Vector2[] array = (Vector2[])(object)new Vector2[1000000];
				for (int i = 0; i < 1000000; i++)
				{
					float num2 = Mathf.Sqrt(Random.value * num);
					float num3 = Random.value * (float)Math.PI * 2f;
					array[i] = new Vector2(num2 * Mathf.Cos(num3), num2 * Mathf.Sin(num3));
				}
				Biome[] array2 = (Biome[])(object)new Biome[1000000];
				DateTime now = DateTime.Now;
				for (int j = 0; j < 1000000; j++)
				{
					array2[j] = (Biome)(int)worldGen.GetBiome(array[j].x, array[j].y, 0.02f, false);
				}
				float num4 = (float)(DateTime.Now - now).TotalMilliseconds;
				now = DateTime.Now;
				Color val = default(Color);
				for (int k = 0; k < 1000000; k++)
				{
					worldGen.GetBiomeHeight(array2[k], array[k].x, array[k].y, ref val, false);
				}
				float num5 = (float)(DateTime.Now - now).TotalMilliseconds;
				Logger.LogInfo($"GetBiome(): {(double)num4 * 1000000.0 / 1000000.0:0.} ns/call");
				Logger.LogInfo($"GetBiomeHeight(): {(double)num5 * 1000000.0 / 1000000.0:0.00} ns/call");
				Logger.LogInfo($"Combined: {(double)(num4 + num5) * 1000000.0 / 1000000.0:0.00} ns/call");
			}
		}

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

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

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

			private static WorldSettings DefaultWorldSettings()
			{
				return new WorldSettings("unstable", 0, Array.Empty<WorldSettings.Feature>(), Array.Empty<WorldSettings.Parameter>());
			}

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

			private static bool ReadCustomMetaData(ZPackage package)
			{
				try
				{
					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")
					{
						Log("Custom worldgen '" + text + "' does not match 'dev.gurebu.riverheim', skipping");
						return false;
					}
					package.ReadWorldSettings();
				}
				catch (WorldSettingSerialization.Error error)
				{
					Log("World settings malformed or unsupported: " + error.Message + ", skipping");
					return false;
				}
				return true;
			}

			[HarmonyTranspiler]
			[HarmonyPatch("SaveWorldMetaData")]
			[HarmonyPatch(/*Could not decode attribute arguments.*/)]
			private 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 pushZPackage;
				return CodeMatcherExtensions.AdvanceToAfterZPackageConstructor(new CodeMatcher(instructions, (ILGenerator)null), out pushZPackage).InsertAndAdvance((CodeInstruction[])(object)new CodeInstruction[1] { pushZPackage }).InsertAndAdvance((CodeInstruction[])(object)new CodeInstruction[1] { CodeInstruction.Call(typeof(WorldSaveLoadPatch), "WriteCustomMetaData", (Type[])null, (Type[])null) })
					.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
				Label label;
				CodeInstruction pushZPackage;
				return CodeMatcherExtensions.MatchBadVersionWorldConstructor(new CodeMatcher(instructions, generator), out label).Start().AdvanceToAfterZPackageConstructor(out pushZPackage)
					.InsertAndAdvance((CodeInstruction[])(object)new CodeInstruction[1] { pushZPackage })
					.InsertAndAdvance((CodeInstruction[])(object)new CodeInstruction[1] { CodeInstruction.Call(typeof(WorldSaveLoadPatch), "ReadCustomMetaData", (Type[])null, (Type[])null) })
					.InsertAndAdvance((CodeInstruction[])(object)new CodeInstruction[1]
					{
						new CodeInstruction(OpCodes.Brfalse_S, (object)label)
					})
					.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("0.15.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] { "0.15.0" });
			}

			[HarmonyPriority(700)]
			[HarmonyPrefix]
			[HarmonyPatch("RPC_ServerHandshake")]
			private static void RPC_ServerHandshake(ZNet __instance, ZRpc rpc)
			{
				rpc.Invoke("AssertRiverheimVersion", new object[1] { "0.15.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 class UnstablePresetHandler : IPresetHandler
		{
			public string ParentPreset()
			{
				return "builtin.amur";
			}

			public int ParentPresetVersion(int version)
			{
				return 0;
			}

			public int MaxSupportedVersion()
			{
				return 0;
			}

			public WorldGenerationConfig GetConfig(int version, WorldGenerationConfig parentConfig)
			{
				return parentConfig;
			}
		}

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

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

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

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

		private const string PLUGIN_NAME = "Riverheim.Unstable";

		private const string PRESET_UNSTABLE = "unstable";

		public void Awake()
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			new Harmony("dev.gurebu.riverheim.unstable").PatchAll();
			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);
				}
			});
			ConfigManager.RegisterPresetHandler("unstable", new UnstablePresetHandler());
		}
	}
	public static class WorldGeneratorExtensions
	{
		private static readonly ConditionalWeakTable<WorldGenerator, RiverheimPlugin.ValheimWorldRenderer> Renderers = new ConditionalWeakTable<WorldGenerator, RiverheimPlugin.ValheimWorldRenderer>();

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

		public static RiverheimPlugin.ValheimWorldRenderer GetRenderer(this WorldGenerator worldGenerator)
		{
			if (worldGenerator == null)
			{
				return null;
			}
			Renderers.TryGetValue(worldGenerator, out var value);
			return value;
		}
	}
	internal static class CodeMatcherExtensions
	{
		public static CodeMatcher AdvanceToAfterZPackageConstructor(this CodeMatcher matcher, out CodeInstruction pushZPackage)
		{
			//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_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: 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
			//IL_00de: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e4: Expected O, but got Unknown
			//IL_011d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0123: Expected O, but got Unknown
			//IL_0104: Unknown result type (might be due to invalid IL or missing references)
			//IL_010a: 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");
			}
			if (matcher.Instruction.opcode == OpCodes.Stloc_0)
			{
				pushZPackage = new CodeInstruction(OpCodes.Ldloc_0, (object)null);
			}
			else if (matcher.Instruction.opcode == OpCodes.Stloc_1)
			{
				pushZPackage = new CodeInstruction(OpCodes.Ldloc_1, (object)null);
			}
			else if (matcher.Instruction.opcode == OpCodes.Stloc_2)
			{
				pushZPackage = new CodeInstruction(OpCodes.Ldloc_2, (object)null);
			}
			else if (matcher.Instruction.opcode == OpCodes.Stloc_3)
			{
				pushZPackage = new CodeInstruction(OpCodes.Ldloc_3, (object)null);
			}
			else
			{
				pushZPackage = new CodeInstruction(OpCodes.Ldloc_S, matcher.Instruction.operand);
			}
			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;
		}
	}
	internal static class ZPackageExtensions
	{
		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 System.Runtime.CompilerServices
{
	[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
	internal sealed class IgnoresAccessChecksToAttribute : Attribute
	{
		public IgnoresAccessChecksToAttribute(string assemblyName)
		{
		}
	}
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.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]
	[Microsoft.CodeAnalysis.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
{
	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 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 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 Math.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 (Math.Abs(x) < 1E-06)
				{
					return Math.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 = Math.Sqrt(a.SqrLength * b.SqrLength);
			if (num < 1E-06)
			{
				return 0.0;
			}
			return Math.Acos(Rmath.Clamp(Dot(a, b) / num, -1.0, 1.0)) * (double)Math.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)
		{
			double num = x;
			if (num.Equals(other.x))
			{
				num = y;
				return num.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 <cea2f7d2-7d32-438d-9027-5fd2d8593fdd>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 (Math.Abs(lhs.x - rhs.x) < 1E-06)
			{
				return Math.Abs(lhs.y - rhs.y) < 1E-06;
			}
			return false;
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static bool operator !=(Rfloat2 lhs, Rfloat2 rhs)
		{
			if (!(Math.Abs(lhs.x - rhs.x) > 1E-06))
			{
				return Math.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 <cea2f7d2-7d32-438d-9027-5fd2d8593fdd>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
			{
				get
				{
					Range result = default(Range);
					result.min = -1.0;
					result.max = 1.0;
					return result;
				}
			}

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

		[Serializable]
		public struct RemapConfig
		{
			public double fromA;

			public double fromB;

			public double toA;

			public double toB;

			public Range From
			{
				get
				{
					Range result = default(Range);
					result.min = fromA;
					result.max = fromB;
					return result;
				}
				set
				{
					double min = value.min;
					double max = value.max;
					fromA = min;
					fromB = max;
				}
			}

			public Range To
			{
				get
				{
					Range result = default(Range);
					result.min = toA;
					result.max = toB;
					return result;
				}
				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 RAD_TO_DEG = 180.0 / Math.PI;

		public const double DEG_TO_RAD = Math.PI / 180.0;

		public const double EPSILON = 1E-06;

		[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 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 Bump(double value)
		{
			if (value <= -1.0 || value >= 1.0)
			{
				return 0.0;
			}
			return Math.Exp(1.0 + 1.0 / (value * value - 1.0));
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static double Bump(double value, double pow)
		{
			double num = Math.Abs(value);
			if (num >= 1.0)
			{
				return 0.0;
			}
			return Math.Exp(1.0 + 1.0 / (Math.Pow(num, pow) - 1.0));
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static double BumpRemap(double value, double pow, Range range)
		{
			return Bump(Remap(value, new RemapConfig
			{
				fromA = range.min,
				fromB = range.max,
				toA = -1.0,
				toB = 1.0
			}), pow);
		}

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

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static double Gauss(double x, double nSigmas)
		{
			return Gauss2(x * x, nSigmas);
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static double Gauss2(double x2, double nSigmas)
		{
			return Math.Exp(-0.5 * nSigmas * nSigmas * x2);
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		private static double PolySinglet(double x)
		{
			return -8.0 * x * x * (x - 1.0) * (x - 1.0) * (2.0 * x - 1.0);
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static double ShapeStep(double x, double start, double end, double magnitude = 1.0)
		{
			return x + (end - start) * magnitude * PolySinglet(InverseLerp(x, start, end));
		}

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

		public static double[] Softmax(double[] values, double temperature = 1.0)
		{
			double num = double.MinValue;
			foreach (double num2 in values)
			{
				if (num < num2)
				{
					num = num2;
				}
			}
			double num3 = 1.0 / temperature;
			double num4 = 0.0;
			double[] array = new double[values.Length];
			for (int j = 0; j < values.Length; j++)
			{
				array[j] = Math.Exp(num3 * (values[j] - num));
				num4 += array[j];
			}
			for (int k = 0; k < values.Length; k++)
			{
				array[k] /= num4;
			}
			return array;
		}
	}
	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 GetBaseHeight(Rfloat2 pos);

		double GetForestation(Rfloat2 pos);

		PointData RenderPoint(Rfloat2 pos);
	}
	[Serializable]
	[CompositeConfig]
	public struct WorldGenerationConfig
	{
		public WorldStateGenerator.Config world;

		public RenderingConfig rendering;
	}
}
namespace Riverheim.World
{
	[Producer(new string[] { "CoastalProximity" })]
	public class CoastalProximityComponent : IGeneratorComponent<TileData<InertFloat>>
	{
		public const string OUTPUT = "CoastalProximity";

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

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

		public TileData<InertFloat> Compute()
		{
			return tiling.DistanceTransform((Tile tile) => (!tile.Data(flags).IsCoast) ? null : new double?(0.5 * tiling.Spacing));
		}
	}
	[Serializable]
	public struct CommonConfig
	{
		public double worldSize;

		public double tileSpacing;

		public double mountainHeight;

		public double oceanDepth;

		public double startingAreaRadius;
	}
	public readonly struct ExtendedTileFlags
	{
		public readonly Region region;

		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 ExtendedTileFlags(bool isCenter, bool isOcean, bool isLake, bool isCoast, Region region)
		{
			IsCenter = isCenter;
			IsOcean = isOcean;
			IsLake = isLake;
			IsCoast = isCoast;
			this.region = region;
		}
	}
	[Producer(new string[] { "Flags/Extended" })]
	public class ExtendedTileFlagComponent : IGeneratorComponent<TileData<ExtendedTileFlags>>
	{
		public const string OUTPUT = "Flags/Extended";

		[Resource("MainTiling")]
		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 || IsLakeShore(tile), baseFlags[tile].region));
		}

		private bool IsLakeShore(Tile tile)
		{
			if (!baseFlags[tile].IsLand)
			{
				return false;
			}
			bool isLake = lakes.Contains(tile);
			return tile.GetNeighbors().Any((Tile neighbor) => lakes.Contains(neighbor) != isLake);
		}
	}
	[Producer(new string[] { "Forestation" })]
	public class ForestationComponent : IGeneratorComponent<TileData<InertFloat>>
	{
		[Serializable]
		public struct Config
		{
			public double noiseScale;

			public NoiseField.FractalConfig noiseFractal;
		}

		public const string OUTPUT = "Forestation";

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

		[Config]
		public RegionalConfig<Config> config;

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

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

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

		public TileData<InertFloat> Compute()
		{
			Dictionary<Region, NoiseField> noise = new Dictionary<Region, NoiseField>();
			foreach (var (region, config) in this.config.All())
			{
				noise[region] = random.BiasedNoise2D($"Forestation/{region}", config.noiseScale).NormalizedFractal(config.noiseFractal);
			}
			return tiling.CreateData((Func<Tile, InertFloat>)delegate(Tile tile)
			{
				if (biomes[tile] == Biome.Mistglade)
				{
					return 0.0;
				}
				return (biomes[tile] == Biome.Marsh) ? ((InertFloat)1.0) : ((InertFloat)(1.0 - noise[flags[tile].region].Sample(tile.Center)));
			});
		}
	}
	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 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();
			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 : IGeneratorComponent<WorldMetrics>
	{
		public const string OUTPUT = "WorldMetrics";

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

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

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

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

		public WorldMetrics Compute()
		{
			WorldMetrics result = default(WorldMetrics);
			result.landmassMetrics = ComputeLandmassMetrics();
			result.riverMetrics = ComputeRiverMetrics();
			result.biomeMetrics = ComputeBiomeMetrics();
			return result;
		}

		private List<LandmassInfo> ComputeLandmassInfos()
		{
			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 = ComputeLandmassInfos();
			LandmassMetrics result = default(LandmassMetrics);
			result.count = list.Count;
			result.totalArea = list.Sum((LandmassInfo x) => x.area) / tiling.TotalArea;
			result.maxArea = list.Max((LandmassInfo x) => x.area) / tiling.TotalArea;
			result.avgArea = list.Average((LandmassInfo x) => x.area) / tiling.TotalArea;
			result.avgCompactness = list.Average((LandmassInfo x) => x.Compactness);
			return result;
		}

		private RiverMetrics ComputeRiverMetrics()
		{
			List<double> source = rivers.Select(RiverLength).ToList();
			RiverMetrics result = default(RiverMetrics);
			result.count = rivers.Length;
			result.maxLength = ((rivers.Length != 0) ? source.Max() : 0.0);
			result.avgLength = ((rivers.Length != 0) ? source.Average() : 0.0);
			return result;
		}

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

		private Dictionary<Biome, BiomeMetrics> ComputeBiomeMetrics()
		{
			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 PolarProximityComponent : IGeneratorComponent<PolarProximityMap>
	{
		public const string OUTPUT = "PolarProximity";

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

		[Config]
		public PolarBeltConfig config;

		public PolarProximityMap Compute()
		{
			NoiseField noise = random.Noise2D("PolarProximity", config.noiseScale);
			return delegate(Rfloat2 pos, PolarProximityType type)
			{
				double num = Math.Atan2(pos.x, (pos.y < 0.0) ? (pos.y - config.offset) : pos.y);
				double num2 = config.radius + Math.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 => Math.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>
	{
		[CompilerGenerated]
		private sealed class <All>d__4 : IEnumerable<(Region, T)>, IEnumerable, IEnumerator<(Region, T)>, IDisposable, IEnumerator
		{
			private int <>1__state;

			private (Region, T) <>2__current;

			private int <>l__initialThreadId;

			public RegionalConfig<T> <>4__this;

			public RegionalConfig<T> <>3__<>4__this;

			(Region, T) IEnumerator<(Region, T)>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <All>d__4(int <>1__state)
			{
				this.<>1__state = <>1__state;
				<>l__initialThreadId = Environment.CurrentManagedThreadId;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				switch (<>1__state)
				{
				default:
					return false;
				case 0:
					<>1__state = -1;
					<>2__current = (Region.Main, <>4__this.main);
					<>1__state = 1;
					return true;
				case 1:
					<>1__state = -1;
					<>2__current = (Region.SouthPole, <>4__this.southPole);
					<>1__state = 2;
					return true;
				case 2:
					<>1__state = -1;
					<>2__current = (Region.NorthPole, <>4__this.northPole);
					<>1__state = 3;
					return true;
				case 3:
					<>1__state = -1;
					return false;
				}
			}

			bool IEnumerator.MoveNext()
			{
				//ILSpy generated this explicit interface implementation from .override directive in MoveNext
				return this.MoveNext();
			}

			[DebuggerHidden]
			void IEnumerator.Reset()
			{
				throw new NotSupportedException();
			}

			[DebuggerHidden]
			IEnumerator<(Region, T)> IEnumerable<(Region, T)>.GetEnumerator()
			{
				<All>d__4 <All>d__;
				if (<>1__state == -2 && <>l__initialThreadId == Environment.CurrentManagedThreadId)
				{
					<>1__state = 0;
					<All>d__ = this;
				}
				else
				{
					<All>d__ = new <All>d__4(0);
				}
				<All>d__.<>4__this = <>3__<>4__this;
				return <All>d__;
			}

			[DebuggerHidden]
			IEnumerator IEnumerable.GetEnumerator()
			{
				return ((IEnumerable<(Region, T)>)this).GetEnumerator();
			}
		}

		public T main;

		public T southPole;

		public T northPole;

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

		[IteratorStateMachine(typeof(RegionalConfig<>.<All>d__4))]
		public IEnumerable<(Region, T)> All()
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <All>d__4(-2)
			{
				<>3__<>4__this = this
			};
		}
	}
	[Producer(new string[] { "Regions" })]
	public class RegionComponent : IGeneratorComponent<TileData<Region>[]>
	{
		public const string OUTPUT = "Regions";

		[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 record RegionInfo
	{
		public int tileCount;

		public double area;
	}
	[Producer(new string[] { "RegionInfo" })]
	public class RegionInfoComponent : IGeneratorComponent<Dictionary<Region, RegionInfo>>
	{
		public const string OUTPUT = "RegionInfo";

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

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

		public Dictionary<Region, RegionInfo> Compute()
		{
			Dictionary<Region, RegionInfo> dictionary = new Dictionary<Region, RegionInfo>();
			foreach (Region value in Enum.GetValues(typeof(Region)))
			{
				dictionary[value] = new RegionInfo();
			}
			foreach (Tile tile in tiling.Tiles)
			{
				Region key2 = regions[0][tile];
				dictionary[key2].tileCount++;
				dictionary[key2].area += tile.Area;
			}
			return dictionary;
		}
	}
	public readonly struct TileFlags
	{
		public readonly Region region;

		public bool IsCenter { get; }

		public bool IsLand { get; }

		public bool IsOcean => !IsLand;

		public bool IsCoast { get; }

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

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

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

		[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), regions[0][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" })]
	public class TravelDistanceComponent : IGeneratorComponent<TileData<InertFloat>>
	{
		[Serializable]
		public struct Config
		{
			public double riverCost;

			public double oceanCost;

			public double embarkCost;
		}

		public const string OUTPUT = "TravelDistance";

		[Config]
		public Config config;

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

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

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

		public TileData<InertFloat> Compute()
		{
			Tile? tile2 = tiling.GetTile(Rfloat2.Zero);
			if (tile2.HasValue)
			{
				Tile center = tile2.GetValueOrDefault();
				return tiling.DistanceTransform((Tile tile) => (!(tile == center)) ? null : new double?(0.0), (Tile _) => true, (Tile t1, Tile t2) => Rfloat2.Distance(t1.Center, t2.Center) * EdgeCostMultiplier(t1, t2));
			}
			return tiling.CreateData((InertFloat)0.0);
		}

		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 1.0;
		}
	}
	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 : IGeneratorComponent<WorldState>
	{
		public const string OUTPUT = "WorldState";

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

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

		[Resource("Biomes/PostprocessedHeightMap")]
		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, biomes, forestation, rivers, splats);
		}
	}
	public class WorldStateGenerator
	{
		[Serializable]
		[CompositeConfig]
		public record Config
		{
			public CommonConfig common;

			public WorldVerticesCalculator.Config tiling;

			public PolarBeltConfig polar;

			public TravelDistanceComponent.Config travel;

			public LandmassConfig landmass;

			public RiverConfig rivers;

			public HeightConfig height;

			public BiomeConfig biomes;

			public Riverheim.World.Splats.SplatConfig splats;

			public RegionalConfig<ForestationComponent.Config> forestation;
		}
	}
}
namespace Riverheim.World.Tilings
{
	[Producer(new string[] { "MainTiling", "MainInterpolationMesh" })]
	public class MainTiling : IGeneratorComponent<(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[] { "TilingLods" })]
	public class TilingLodComponent : IGeneratorComponent<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 class TilingLodIndex
	{
		private readonly Tiling[] lods;

		private readonly TileData<int>[] indices;

		public TilingLodIndex(Tiling tiling, Tiling[] lods)
		{
			this.lods = lods;
			indices = new TileData<int>[lods.Length];
			for (int i = 0; i < lods.Length; i++)
			{
				indices[i] = lods[i].CreateData<int>();
			}
			foreach (Tile tile in tiling.Tiles)
			{
				_ = tile;
			}
		}

		public Tile GetLodTile(Tile tile, int lod)
		{
			Tile tile2 = tile;
			for (int i = 0; i < lod; i++)
			{
				tile2 = lods[i].GetTile(indices[i][tile2]);
			}
			return tile2;
		}
	}
	public struct WorldVertices
	{
		public double spacing;

		public double boundingRadius;

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

			public double lodScale;
		}

		public const string OUTPUT = "WorldVertices";

		[Random("WorldVertices")]
		public RandomEngine 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 * Math.Pow(config.lodScale, i);
				double boundingRadius = commonConfig.worldSize + Math.Sqrt(3.0) * num;
				InertFloat2[] vertices = (from point in new PoissonDiscSampler(random.Sequence(i.ToString()), boundingRadius, num / Math.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 record Pond : Splat
	{
		public InertFloat radius;
	}
	[Producer(new string[] { "Splats/RiverSources" })]
	public class RiverSourceSplatComponent : IGeneratorComponent<Splat[]>
	{
		[Serializable]
		public struct Config
		{
			public AllBiomeConfig<bool> allowedBiomes;

			public CragConfig crags;

			public PondConfig ponds;
		}

		[Serializable]
		public struct SpawnFeatureConfig
		{
			public Rmath.Range range;

			public double bumpPower;
		}

		[Serializable]
		public struct CragConfig
		{
			public double frequency;

			public SpawnFeatureConfig height;

			public SpawnFeatureConfig riverLength;

			public CragRandomizer.Config random;
		}

		[Serializable]
		public struct PondConfig
		{
			public double frequency;

			public SpawnFeatureConfig height;

			public SpawnFeatureConfig riverLength;

			public double stepback;

			public PondRandomizer.Config random;
		}

		public const string OUTPUT = "Splats/RiverSources";

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

		[Config]
		public Config config;

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

		[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.CreateEngine("Crags"), config.crags.random);
			RandomIndexer randomIndexer2 = random.Indexer("Ponds/Spawn");
			PondRandomizer pondRandomizer = new PondRandomizer(random.CreateEngine("Ponds"), config.ponds.random);
			BarycentricInterpolator barycentricInterpolator = new BarycentricInterpolator(tiling, 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.Interpolate(pos, heights);
					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, SpawnFeatureConfig config)
		{
			if (config.range.IsEmpty || config.bumpPower <= 0.0)
			{
				return 1.0;
			}
			return Rmath.BumpRemap(value, config.bumpPower, config.range);
		}
	}
	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(RandomEngine 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)
			};
		}
	}
	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(RandomEngine 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 seasideCliffs;

		public PlateauHillConfig hills;

		public double swampDitchHeight;

		public int swampDitchTileCount;
	}
	[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;
	}
	[Serializable]
	public struct PlateauSeasideCliffConfig
	{
		public double spawnRate;

		public double minHeight;

		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;
	}
	[Serializable]
	public struct ClampingConfig
	{
		public enum Mode
		{
			Linear,
			Asymptotic
		}

		public Mode mode;

		public double min;

		public double max;

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

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

		[Config]
		public PlateauConfig config;

		[Resource("MainTiling")]
		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);
			AddSeasideCliffs(list, occupied);
			AddHills(list, occupied);
			AddSwampDitches(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);
			RandomField randomField = random.Field("Size");
			foreach (Tile tile in tiling.Tiles)
			{
				if (tile.Data(biomes) == Biome.Mountain && !(noiseField.Sample(tile.Center) > cfg.spawnRate) && !tile.GetNeighbors().Any((Tile neighbor) => neighbor.Data(biomes) != Biome.Mountain) && !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) + randomField.Single(tile.Center).Range(cfg.cragHeight),
							radius = cfg.cragRadius,
							terrace = cfg.cragTerrace
						});
					}
					else
					{
						result.Add(new Plateau
						{
							pos = tile.Center,
							height = tile.Data(heights),
							radius = Rmath.Lerp(randomField.Single(tile.Center).Value, cfg.size),
							incline = Clamp(gradient, cfg.gradientClamping),
							bulgeDirection = new Direction(gradient),
							bulgeMagnitude = cfg.bulge
						});
					}
				}
			}
		}

		private void AddSeasideCliffs(List<Splat> result, TileSet occupied)
		{
			NoiseField noiseField = random.BiasedNoise2D("SpawnRate/SeasideCliffs", config.spawnNoiseScale);
			RandomField randomField = random.Field("Size");
			RandomField randomField2 = random.Field("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.seasideCliffs.minHeight || noiseField.Sample(tile2.Center) > config.seasideCliffs.spawnRate || tile2.GetNeighbors().All((Tile neighbor) => (double)neighbor.Data(heights) >= 0.0) || !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 rfloat = 0.5 * (item.Center + item2.Center);
				double num = 0.5 * ((double)item.Data(heights) + (double)item2.Data(heights));
				double num2 = Rmath.Lerp(randomField.Single(rfloat).Value, config.seasideCliffs.size);
				Rfloat2 rfloat2 = 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) >= 0.0);
				if (num3 != 0)
				{
					if (num3 != 1)
					{
						continue;
					}
					Tile tile = list2.First((Tile neighbor) => (double)neighbor.Data(heights) >= 0.0);
					rfloat2 += Clamp(HeightUtility.InclineVector(rfloat, num, tile.Center, tile.Data(heights)), config.seasideCliffs.gradientClamping);
				}
				else
				{
					int num4 = Math.Sign(Rfloat2.Dot(Normal(item.Center, item2.Center), list2[1].Center - list2[0].Center));
					rfloat2 += num4 * heights.InclineVector(list2[0], list2[1]);
				}
				Rfloat2 rfloat3 = 0.5 * (item.Center + item2.Center);
				result.Add(new Plateau
				{
					pos = rfloat3,
					p1 = item.Center - rfloat3,
					p2 = item2.Center - rfloat3,
					height = num,
					radius = num2,
					incline = rfloat2,
					bulgeDirection = new Direction(rfloat2),
					bulgeMagnitude = randomField2.Double(rfloat).Range(config.seasideCliffs.bulge)
				});
			}
		}

		private void AddHills(List<Splat> result, TileSet occupied)
		{
			NoiseField noiseField = random.BiasedNoise2D("SpawnRate/Hills", config.spawnNoiseScale);
			RandomField randomField = random.Field("Size");
			RandomField randomField2 = random.Field("Length");
			HashSet<Biome> hashSet = new HashSet<Biome>
			{
				Biome.Meadows,
				Biome.BlackForest,
				Biome.Plains
			};
			foreach (Tile tile in tiling.Tiles)
			{
				if (hashSet.Contains(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 * randomField2.Single(tile.Center).Value * 0.5 * config.hills.maxLength;
					result.Add(new Plateau
					{
						pos = tile.Center,
						p1 = rfloat2,
						p2 = -rfloat2,
						height = tile.Data(heights),
						radius = Rmath.Lerp(randomField.Single(tile.Center).Value, config.hills.size),
						incline = rfloat,
						bulgeDirection = new Direction(rfloat),
						bulgeMagnitude = config.hills.bulge
					});
				}
			}
		}

		private void AddSwampDitches(List<Splat> result, TileSet occupied)
		{
			TileSet tileSet = new TileSet(tiling);
			TileQueue tileQueue = new TileQueue(tiling);
			foreach (Tile tile2 in tiling.Tiles)
			{
				if (tile2.Data(biomes) != Biome.Swamp || tileSet.Contains(tile2))
				{
					continue;
				}
				TileList tileList = new TileList(tiling);
				tileSet.Add(tile2);
				tileQueue.Enqueue(tile2);
				while (tileQueue.Count > 0)
				{
					Tile tile = tileQueue.Dequeue();
					tileList.Add(tile);
					foreach (Tile neighbor in tile.GetNeighbors())
					{
						if (!tileSet.Contains(neighbor) && neighbor.Data(biomes) == Biome.Swamp)
						{
							tileSet.Add(neighbor);
							tileQueue.Enqueue(neighbor);
						}
					}
				}
				if (tileList.Count > config.swampDitchTileCount)
				{
					continue;
				}
				foreach (Tile item in tileList)
				{
					if (occupied.Add(item))
					{
						result.Add(new Plateau
						{
							pos = item.Center,
							height = config.swampDitchHeight,
							radius = item.Tiling.Spacing / 2.0,
							maxNeighborHeight = item.Scan(item.Tiling.Spacing).Append(item).Max((Func<Tile, double>)((Tile neighbor) => neighbor.Data(heights))),
							inverted = true
						});
					}
				}
			}
		}

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

		private static Rfloat2 Clamp(Rfloat2 vector, ClampingConfig config)
		{
			if (vector.IsNegligible)
			{
				return config.min * new Rfloat2(-1.0, 0.0);
			}
			double length = vector.Length;
			double num = config.max - config.min;
			return config.mode switch
			{
				ClampingConfig.Mode.Linear => Rmath.Clamp(length, config.min, config.max), 
				ClampingConfig.Mode.Asymptotic => config.min + num * Functions.Tanh(length * config.rate / num), 
				_ => throw new ArgumentOutOfRangeException(), 
			} / length * vector;
		}
	}
	public abstract record Splat
	{
		public InertFloat2 pos;

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

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

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

		public Splat[] Compute()
		{
			List<Splat> list = new List<Splat>();
			list.AddRange(plateaus);
			list.AddRange(riverSourceSplats);
			return list.ToArray();
		}
	}
	[Serializable]
	[CompositeConfig]
	public record SplatConfig
	{
		public PlateauConfig plateaus;

		public RiverSourceSplatComponent.Config riverSourceSplats;
	}
}
namespace Riverheim.World.Rivers
{
	public struct Channel
	{
		public TileList path;

		public double width;
	}
	[Producer(new string[] { "Rivers/Channels" })]
	public class ChannelComponent : IGeneratorComponent<Channel[]>
	{
		[Serializable]
		public struct Config
		{
			public RegionalConfig<bool> allowedRegions;

			public double width;
		}

		public const string OUTPUT = "Rivers/Channels";

		[Config]
		public Config config;

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

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

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

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

		public Channel[] Compute()
		{
			List<Channel> list = new List<Channel>();
			RandomIndexer selectionRandom = random.Indexer("Selection");
			foreach (TileSet faultLine in GetFaultLines())
			{
				if (!faultLine.Unordered().Any((Tile tile) => !config.allowedRegions.Get(flags[tile].region)) && MakeChannel(faultLine, selectionRandom, out var channel))
				{
					list.Add(channel);
				}
			}
			return list.ToArray();
		}

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

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

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

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

		public double variability;

		public double spawnMinRadius;

		public double spawnMaxRadius;

		public double basinContribution;

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

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

		[Config]
		public RegionalConfig<PrecipitationConfig> config;

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

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

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

		public TileData<InertFloat> Compute()
		{
			Dictionary<Region, NoiseField> noise = new Dictionary<Region, NoiseField>();
			foreach (var (region, precipitationConfig) in config.All())
			{
				noise[region] = random.Noise2D($"Precipitation/{region}", precipitationConfig.noiseScale);
			}
			return tiling.CreateData((Func<Tile, InertFloat>)delegate(Tile tile)
			{
				Region region2 = flags[tile].region;
				PrecipitationConfig precipitationConfig2 = config.Get(region2);
				double num = Rmath.Clamp(precipitationConfig2.variability * noise[region2].Sample(tile.Center), Rmath.Lerp(Rmath.InverseLerp(tile.Center.Length, precipitationConfig2.spawnMinRadius, precipitationConfig2.spawnMaxRadius), 1.0, -1.0), 1.0);
				if (tile.Data(artifacts).isBasin)
				{
					num += precipitationConfig2.basinContribution;
				}
				return Math.Max(precipitationConfig2.minimum, 1.0 + num);
			});
		}
	}
	[Serializable]
	[CompositeConfig]
	public record RiverConfig
	{
		public RegionalConfig<PrecipitationConfig> precipitation;

		public RiverOriginFinder.Config origins;

		public RiverFillComponent.Config floodfill;

		public RiverWidthConfig width;

		public RiverPruneComponent.Config prune;

		public RiverCuriosityConfig curiosities;

		public ChannelComponent.Config channels;

		public RiverPolylineIndexBuilder.Config polylineIndex;

		public RiverMeanderer.Config meander;

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

		public double sourceMinLength;

		public double sourceMaxLength;

		public double proximityMult;

		public double proximityScanRange;

		public double proximityDifferentBasinsMultiplier;

		public double proximityMinRatio;

		public double proximityMaxRatio;

		public double angleMult;

		public double angleMinDegrees;

		public double angleMaxDegrees;

		public double angleNormWidth;

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

			public InertFloat score;

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

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

		public const string OUTPUT = "Rivers/Curiosities";

		[Config]
		public CommonConfig commonConfig;

		[Config]
		public RiverCuriosityConfig config;

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

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

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

		private MutableTileData<CuriosityData> data;

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

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

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

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

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

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

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

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

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

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

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

		public readonly Couple<Tile> inlets;

		public readonly InertFloat loss;

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

			public double maxOriginRandomLoss;

			public RiverFillGuide.Config guide;
		}

		private class GraphNode
		{
			public readonly Tile outlet;

			public readonly TileList inlets;

			public readonly InertFloat loss;

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

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

		public const string OUTPUT = "Rivers/FloodFill";

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

		[Config]
		public Config config;

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

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

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

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

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

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

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

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

		private Dictionary<int, TileList> InitializeOrigins(MutableTileData<GraphNode> g