Decompiled source of SonsRealisticWinter v0.3.0

Realistic Wethers/BepInEx/plugins/SotfRealisticWinter/SotfRealisticWinter.dll

Decompiled 9 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Core.Logging.Interpolation;
using BepInEx.Logging;
using BepInEx.Unity.IL2CPP;
using Il2CppInterop.Runtime.Injection;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using Microsoft.CodeAnalysis;
using UnityEngine;
using UnityEngine.SceneManagement;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: AssemblyCompany("SotfRealisticWinter")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.3.0.0")]
[assembly: AssemblyInformationalVersion("0.3.0")]
[assembly: AssemblyProduct("SotfRealisticWinter")]
[assembly: AssemblyTitle("SotfRealisticWinter")]
[assembly: AssemblyVersion("0.3.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace SotfRealisticWinter
{
	internal sealed class GrainRenderer
	{
		private const int BatchSize = 1000;

		private static readonly string[] AutoShaders = new string[7] { "HDRP/Lit", "Universal Render Pipeline/Lit", "Standard", "HDRP/Unlit", "Universal Render Pipeline/Unlit", "Unlit/Color", "Sprites/Default" };

		private readonly List<Il2CppStructArray<Matrix4x4>> _batches = new List<Il2CppStructArray<Matrix4x4>>();

		private Mesh _mesh;

		private Material _mat;

		private MethodInfo _draw;

		private object[] _args;

		public string Description = "not initialised";

		public bool Ready
		{
			get
			{
				if ((Object)(object)_mesh != (Object)null)
				{
					return (Object)(object)_mat != (Object)null;
				}
				return false;
			}
		}

		public bool Init(WinterConfig c)
		{
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Expected O, but got Unknown
			try
			{
				_mesh = MakeDiscMesh();
				if ((Object)(object)_mesh == (Object)null)
				{
					Plugin.Logger.LogError((object)"Grains: could not get a disc mesh.");
					return false;
				}
				Shader val = PickShader(c.GrainShader.Value);
				if ((Object)(object)val == (Object)null)
				{
					Plugin.Logger.LogError((object)("Grains: no usable shader found (tried '" + c.GrainShader.Value + "'). Set 6. Grains / Shader to a shader name from the probe file."));
					return false;
				}
				_mat = new Material(val);
				ApplyColor(_mat, c.GrainColor.Value);
				if (_mat.HasProperty("_Smoothness"))
				{
					_mat.SetFloat("_Smoothness", 0.15f);
				}
				if (_mat.HasProperty("_Metallic"))
				{
					_mat.SetFloat("_Metallic", 0f);
				}
				bool value = c.UseInstancing.Value;
				if (value)
				{
					_mat.enableInstancing = true;
					_draw = FindInstancedDraw();
					if (_draw != null)
					{
						ParameterInfo[] parameters = _draw.GetParameters();
						_args = new object[parameters.Length];
						for (int i = 5; i < parameters.Length; i++)
						{
							_args[i] = (parameters[i].ParameterType.IsValueType ? Activator.CreateInstance(parameters[i].ParameterType) : null);
						}
						_args[0] = _mesh;
						_args[1] = 0;
						_args[2] = _mat;
					}
				}
				Description = "shader=" + ((Object)val).name + ", mode=" + ((_draw != null) ? ("instanced (" + _draw.GetParameters().Length + " args)") : "DrawMesh");
				Plugin.Logger.LogInfo((object)("Grains renderer ready: " + Description));
				if (value && _draw == null)
				{
					Plugin.Logger.LogWarning((object)"Grains: DrawMeshInstanced overload not found in the interop, using slower DrawMesh.");
				}
				return true;
			}
			catch (Exception ex)
			{
				Plugin.Logger.LogError((object)("Grains renderer init failed: " + ex));
				return false;
			}
		}

		public void Set(int i, Matrix4x4 m)
		{
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			int num = i / 1000;
			while (_batches.Count <= num)
			{
				_batches.Add(new Il2CppStructArray<Matrix4x4>(1000L));
			}
			((Il2CppArrayBase<Matrix4x4>)(object)_batches[num])[i % 1000] = m;
		}

		public void Draw(int count)
		{
			//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
			if (!Ready || count <= 0)
			{
				return;
			}
			int num = (count + 1000 - 1) / 1000;
			for (int i = 0; i < num && i < _batches.Count; i++)
			{
				int num2 = Math.Min(1000, count - i * 1000);
				if (_draw != null)
				{
					try
					{
						_args[3] = _batches[i];
						_args[4] = num2;
						_draw.Invoke(null, _args);
					}
					catch (Exception ex)
					{
						Plugin.Logger.LogWarning((object)("Grains: instanced draw failed, switching to DrawMesh: " + ((ex.InnerException != null) ? ex.InnerException.Message : ex.Message)));
						_draw = null;
						goto IL_00b6;
					}
					continue;
				}
				goto IL_00b6;
				IL_00b6:
				for (int j = 0; j < num2; j++)
				{
					Graphics.DrawMesh(_mesh, ((Il2CppArrayBase<Matrix4x4>)(object)_batches[i])[j], _mat, 0);
				}
			}
		}

		private static Mesh MakeDiscMesh()
		{
			GameObject val = GameObject.CreatePrimitive((PrimitiveType)2);
			Mesh result = null;
			try
			{
				MeshFilter component = val.GetComponent<MeshFilter>();
				if ((Object)(object)component != (Object)null)
				{
					result = component.sharedMesh;
				}
			}
			finally
			{
				Object.Destroy((Object)(object)val);
			}
			return result;
		}

		private static Shader PickShader(string requested)
		{
			string[] array = ((string.Equals(requested, "Auto", StringComparison.OrdinalIgnoreCase) || string.IsNullOrWhiteSpace(requested)) ? AutoShaders : new string[1] { requested.Trim() });
			string[] array2 = array;
			for (int i = 0; i < array2.Length; i++)
			{
				Shader val = Shader.Find(array2[i]);
				if ((Object)(object)val != (Object)null)
				{
					return val;
				}
			}
			Il2CppArrayBase<Shader> val2 = Resources.FindObjectsOfTypeAll<Shader>();
			array2 = array;
			foreach (string text in array2)
			{
				for (int j = 0; j < val2.Length; j++)
				{
					if ((Object)(object)val2[j] != (Object)null && ((Object)val2[j]).name == text)
					{
						return val2[j];
					}
				}
			}
			return null;
		}

		private static MethodInfo FindInstancedDraw()
		{
			MethodInfo methodInfo = null;
			MethodInfo[] methods = typeof(Graphics).GetMethods(BindingFlags.Static | BindingFlags.Public);
			foreach (MethodInfo methodInfo2 in methods)
			{
				if (!(methodInfo2.Name != "DrawMeshInstanced"))
				{
					ParameterInfo[] parameters = methodInfo2.GetParameters();
					if (parameters.Length >= 5 && !(parameters[0].ParameterType != typeof(Mesh)) && !(parameters[1].ParameterType != typeof(int)) && !(parameters[2].ParameterType != typeof(Material)) && !(parameters[3].ParameterType != typeof(Il2CppStructArray<Matrix4x4>)) && !(parameters[4].ParameterType != typeof(int)) && (methodInfo == null || parameters.Length < methodInfo.GetParameters().Length))
					{
						methodInfo = methodInfo2;
					}
				}
			}
			return methodInfo;
		}

		private static void ApplyColor(Material m, string rgb)
		{
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			float num = 0.95f;
			float num2 = 0.97f;
			float num3 = 1f;
			try
			{
				string[] array = rgb.Split(',');
				if (array.Length >= 3)
				{
					num = float.Parse(array[0].Trim(), CultureInfo.InvariantCulture);
					num2 = float.Parse(array[1].Trim(), CultureInfo.InvariantCulture);
					num3 = float.Parse(array[2].Trim(), CultureInfo.InvariantCulture);
				}
			}
			catch (Exception)
			{
			}
			Color val = default(Color);
			((Color)(ref val))..ctor(num, num2, num3, 1f);
			if (m.HasProperty("_BaseColor"))
			{
				m.SetColor("_BaseColor", val);
			}
			if (m.HasProperty("_Color"))
			{
				m.SetColor("_Color", val);
			}
			if (m.HasProperty("_UnlitColor"))
			{
				m.SetColor("_UnlitColor", val);
			}
		}
	}
	internal sealed class GrainSystem
	{
		private sealed class Cell
		{
			public long Key;

			public Vector3 Pos;

			public Vector3 Normal;

			public float Depth;

			public float Drift = 1f;

			public float Jitter;

			public bool OnWater;

			public Matrix4x4 M;
		}

		private const byte SurfSolid = 0;

		private const byte SurfWater = 1;

		private const byte SurfIce = 2;

		private const byte SurfIgnore = 3;

		private const float CellHeight = 0.5f;

		private const float RemoveBelow = 0.0008f;

		private readonly WinterConfig _c;

		private readonly Random _rng = new Random(4242);

		private readonly List<Cell> _cells = new List<Cell>();

		private readonly Dictionary<long, int> _index = new Dictionary<long, int>();

		private readonly Dictionary<int, byte> _surfCache = new Dictionary<int, byte>();

		private readonly GrainRenderer _render = new GrainRenderer();

		private readonly string[] _waterKw;

		private readonly string[] _iceKw;

		private readonly string[] _ignoreKw;

		private readonly string[] _ignoreSceneKw;

		private readonly float _depthPerFlake;

		private readonly Vector3 _upwind;

		private int _solidMask = -1;

		private int _waterMask;

		private float _minNormalY;

		private bool _rendererTried;

		private bool _disabled;

		private bool _forced;

		private bool _inputBroken;

		private bool _skipScene;

		private KeyCode _forceKey;

		private KeyCode _clearKey;

		private string _sceneName = "";

		private float _rayDebt;

		private float _tickAcc;

		private float _statAcc;

		private float _camWarnAt;

		private float _nextRebuildAt;

		private Vector3 _camPos;

		private Vector3 _lastRebuildCam;

		private int _stRays;

		private int _stMiss;

		private int _stLanded;

		private int _stWaterMelted;

		private int _stSlope;

		private int _stMoving;

		private int _stIgnored;

		private int _stCap;

		private readonly int[] _stSurf = new int[4];

		public GrainSystem(WinterConfig c)
		{
			//IL_0107: Unknown result type (might be due to invalid IL or missing references)
			//IL_0152: Unknown result type (might be due to invalid IL or missing references)
			//IL_0157: Unknown result type (might be due to invalid IL or missing references)
			//IL_0127: Unknown result type (might be due to invalid IL or missing references)
			_c = c;
			_waterKw = Split(c.WaterKeywords.Value);
			_iceKw = Split(c.IceKeywords.Value);
			_ignoreKw = Split(c.IgnoreKeywords.Value);
			_ignoreSceneKw = Split(c.IgnoreSceneKeywords.Value);
			_minNormalY = Mathf.Cos(c.MaxSlopeDeg.Value * ((float)Math.PI / 180f));
			_forced = c.DebugForceSnow.Value;
			if (!Enum.TryParse<KeyCode>(c.DebugForceKey.Value, ignoreCase: true, out _forceKey))
			{
				_forceKey = (KeyCode)0;
			}
			if (!Enum.TryParse<KeyCode>(c.ClearKey.Value, ignoreCase: true, out _clearKey))
			{
				_clearKey = (KeyCode)0;
			}
			float num = c.WindDirectionDeg.Value * ((float)Math.PI / 180f);
			_upwind = new Vector3(0f - Mathf.Cos(num), 0f, 0f - Mathf.Sin(num));
			string[] array = Split(c.WaterLayers.Value);
			foreach (string text in array)
			{
				int num2 = LayerMask.NameToLayer(text);
				if (num2 >= 0)
				{
					_waterMask |= 1 << num2;
				}
				else
				{
					Plugin.Logger.LogInfo((object)("Grains: layer '" + text + "' does not exist, ignored."));
				}
			}
			if (_waterMask != 0)
			{
				_solidMask = ~_waterMask;
			}
			float num3 = MathF.Max(1f, c.VirtualDayMinutes.Value * 60f);
			float num4 = (float)Math.PI * c.GrainRadius * c.GrainRadius;
			float y = c.FlakesPerSecond * c.CellSize * c.CellSize / num4;
			_depthPerFlake = c.EffDepthPerDay / num3 / MathF.Max(0.0001f, y);
			Plugin.Logger.LogInfo((object)("Grains: preset " + c.Preset.Value.ToString() + ", " + c.EffDepthPerDay + " m/day, max depth " + ((c.EffMaxDepth > 0f) ? (c.EffMaxDepth + " m") : "unlimited") + ", radius " + c.GrainRadius + " m, " + c.FlakesPerSecond + " flakes/s, up to " + c.MaxCells + " patches, " + c.MaxRaysPerFrame + " rays/frame, " + _depthPerFlake.ToString("0.0000") + " m per flake, waterMask=" + _waterMask));
		}

		public void Frame(float dt, in WeatherSample w, float ice)
		{
			//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			if (_disabled)
			{
				return;
			}
			HandleKeys();
			CheckScene();
			WeatherSample ws = w;
			if (_forced)
			{
				ws.Snowfall = 1f;
				ws.TempC = -5f;
				ice = MathF.Max(ice, 1f);
			}
			Camera main = Camera.main;
			if ((Object)(object)main == (Object)null)
			{
				if (Time.time >= _camWarnAt)
				{
					_camWarnAt = Time.time + 20f;
					Plugin.Logger.LogInfo((object)"Grains: no main camera right now.");
				}
			}
			else
			{
				if (_skipScene)
				{
					return;
				}
				if (!_rendererTried)
				{
					_rendererTried = true;
					if (!_render.Init(_c))
					{
						_disabled = true;
						return;
					}
				}
				_camPos = ((Component)main).transform.position;
				Spawn(dt, in ws, ice);
				float num = _camPos.x - _lastRebuildCam.x;
				float num2 = _camPos.y - _lastRebuildCam.y;
				float num3 = _camPos.z - _lastRebuildCam.z;
				if (_c.TrenchRadius.Value > 0f && num * num + num2 * num2 + num3 * num3 > 0.0625f && Time.time >= _nextRebuildAt)
				{
					_nextRebuildAt = Time.time + 0.1f;
					RebuildAll();
				}
				_tickAcc += dt;
				if (_tickAcc >= 0.5f)
				{
					Tick(_tickAcc, in ws, ice);
					_tickAcc = 0f;
				}
				_render.Draw(_cells.Count);
				Stats(dt);
			}
		}

		private void HandleKeys()
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			if (_inputBroken)
			{
				return;
			}
			try
			{
				if ((int)_forceKey != 0 && Input.GetKeyDown(_forceKey))
				{
					_forced = !_forced;
					Plugin.Logger.LogInfo((object)("Grains: test snowfall " + (_forced ? "ON" : "OFF")));
				}
				if ((int)_clearKey != 0 && Input.GetKeyDown(_clearKey))
				{
					ClearAll();
					Plugin.Logger.LogInfo((object)"Grains: cleared");
				}
			}
			catch (Exception)
			{
				_inputBroken = true;
			}
		}

		private void CheckScene()
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			Scene activeScene = SceneManager.GetActiveScene();
			string text = ((Scene)(ref activeScene)).name ?? "";
			if (!(text == _sceneName))
			{
				_sceneName = text;
				_skipScene = _ignoreSceneKw.Length != 0 && ContainsAny(text.ToLowerInvariant(), _ignoreSceneKw);
				Plugin.Logger.LogInfo((object)("Grains: active scene '" + text + "' -> " + (_skipScene ? "grains paused (IgnoreSceneKeywords)" : "grains active")));
			}
		}

		private void Spawn(float dt, in WeatherSample ws, float ice)
		{
			float num = ((ws.TempC <= 0f) ? 1f : Mathf.Clamp01(1f - ws.TempC / 1.5f));
			float num2 = _c.FlakesPerSecond * ws.Snowfall * num * _c.SnowIntensity.Value;
			if (num2 <= 0f)
			{
				_rayDebt = 0f;
				return;
			}
			int maxRaysPerFrame = _c.MaxRaysPerFrame;
			_rayDebt += num2 * dt;
			int num3 = (int)_rayDebt;
			int num4 = Math.Min(num3, maxRaysPerFrame);
			float mult = 1f;
			if (num3 > maxRaysPerFrame)
			{
				mult = MathF.Min(8f, (float)num3 / (float)maxRaysPerFrame);
				_rayDebt -= num3;
			}
			else
			{
				_rayDebt -= num4;
			}
			for (int i = 0; i < num4; i++)
			{
				OneFlake(ice, mult);
			}
		}

		private void OneFlake(float ice, float mult)
		{
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_010c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0111: Unknown result type (might be due to invalid IL or missing references)
			//IL_0113: Unknown result type (might be due to invalid IL or missing references)
			//IL_0118: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_019a: Unknown result type (might be due to invalid IL or missing references)
			double num = _rng.NextDouble() * Math.PI * 2.0;
			float num2 = _c.GrainRadius * (float)Math.Sqrt(_rng.NextDouble());
			float value = _c.RayStartHeight.Value;
			Vector3 val = default(Vector3);
			((Vector3)(ref val))..ctor(_camPos.x + num2 * (float)Math.Cos(num), _camPos.y + value, _camPos.z + num2 * (float)Math.Sin(num));
			_stRays++;
			RaycastHit val2 = default(RaycastHit);
			if (!Physics.Raycast(val, Vector3.down, ref val2, value + 60f, _solidMask, (QueryTriggerInteraction)1))
			{
				_stMiss++;
				return;
			}
			Vector3 point = ((RaycastHit)(ref val2)).point;
			Vector3 val3 = ((RaycastHit)(ref val2)).normal;
			Collider collider = ((RaycastHit)(ref val2)).collider;
			RaycastHit val4 = default(RaycastHit);
			byte b;
			if (_waterMask != 0 && Physics.Raycast(val, Vector3.down, ref val4, ((RaycastHit)(ref val2)).distance, _waterMask, (QueryTriggerInteraction)2))
			{
				b = 1;
				point = ((RaycastHit)(ref val4)).point;
				val3 = Vector3.up;
			}
			else
			{
				b = Classify(collider);
			}
			_stSurf[b]++;
			bool onWater = false;
			switch (b)
			{
			case 3:
				_stIgnored++;
				return;
			case 1:
				if (ice < _c.WaterFreezeThreshold.Value && !_c.SnowOnLiquidWater.Value)
				{
					_stWaterMelted++;
					return;
				}
				onWater = true;
				break;
			default:
				if (val3.y < _minNormalY)
				{
					_stSlope++;
					return;
				}
				if (_c.SkipMoving.Value && (Object)(object)collider != (Object)null && (Object)(object)collider.attachedRigidbody != (Object)null)
				{
					_stMoving++;
					return;
				}
				break;
			case 2:
				break;
			}
			AddFlake(point, val3, onWater, mult);
		}

		private void AddFlake(Vector3 point, Vector3 normal, bool onWater, float mult)
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0102: Unknown result type (might be due to invalid IL or missing references)
			//IL_0125: Unknown result type (might be due to invalid IL or missing references)
			float cellSize = _c.CellSize;
			long num = Pack(Mathf.FloorToInt(point.x / cellSize), Mathf.FloorToInt(point.y / 0.5f), Mathf.FloorToInt(point.z / cellSize));
			float effMaxDepth = _c.EffMaxDepth;
			if (_index.TryGetValue(num, out var value))
			{
				Cell cell = _cells[value];
				cell.Depth += _depthPerFlake * mult * cell.Drift;
				if (effMaxDepth > 0f && cell.Depth > effMaxDepth)
				{
					cell.Depth = effMaxDepth;
				}
				cell.OnWater = onWater;
				Rebuild(cell, value);
				_stLanded++;
				return;
			}
			if (_cells.Count >= _c.MaxCells)
			{
				_stCap++;
				return;
			}
			Cell cell2 = new Cell
			{
				Key = num,
				Pos = point,
				Normal = ((Vector3)(ref normal)).normalized,
				OnWater = onWater,
				Jitter = Hash01(num) * 0.004f
			};
			cell2.Drift = ComputeDrift(point);
			cell2.Depth = MathF.Max(0.0015f, _depthPerFlake * mult * cell2.Drift * 0.6f);
			if (effMaxDepth > 0f && cell2.Depth > effMaxDepth)
			{
				cell2.Depth = effMaxDepth;
			}
			int count = _cells.Count;
			_cells.Add(cell2);
			_index[num] = count;
			Rebuild(cell2, count);
			_stLanded++;
		}

		private float ComputeDrift(Vector3 point)
		{
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: 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_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: 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)
			float num = Mathf.Lerp(0.65f, 1.45f, Noise2(point.x / 6f, point.z / 6f));
			float value = _c.DriftStrength.Value;
			if (value <= 0f)
			{
				return num;
			}
			float value2 = _c.ShelterMeters.Value;
			RaycastHit val = default(RaycastHit);
			if (Physics.Raycast(point + Vector3.up * 0.8f, _upwind, ref val, value2, _solidMask, (QueryTriggerInteraction)1))
			{
				float num2 = 1f - ((RaycastHit)(ref val)).distance / value2;
				num *= 1f + value * 2.5f * num2;
			}
			else
			{
				num *= Mathf.Lerp(1f, 0.85f, Mathf.Clamp01(value));
			}
			return MathF.Max(0.05f, num);
		}

		private void Tick(float dt, in WeatherSample ws, float ice)
		{
			//IL_0140: Unknown result type (might be due to invalid IL or missing references)
			float num = MathF.Max(1f, _c.VirtualDayMinutes.Value * 60f);
			float num2 = 0f;
			if (ws.TempC > 0f)
			{
				num2 = ws.TempC * _c.EffMelt * _c.MeltSpeed.Value / num * dt;
			}
			else if (ws.Snowfall < 0.05f)
			{
				num2 = 0.01f / num * dt;
			}
			float num3 = _c.WaterFreezeThreshold.Value * 0.8f;
			for (int num4 = _cells.Count - 1; num4 >= 0; num4--)
			{
				Cell cell = _cells[num4];
				float num5 = num2;
				if (cell.OnWater && ice < num3)
				{
					num5 += (0.05f + 0.5f * cell.Depth) * dt;
				}
				if (!(num5 <= 0f))
				{
					cell.Depth -= num5;
					if (cell.Depth <= 0.0008f)
					{
						RemoveAt(num4);
					}
					else
					{
						Rebuild(cell, num4);
					}
				}
			}
			int maxCells = _c.MaxCells;
			if ((float)_cells.Count > (float)maxCells * 0.95f)
			{
				PruneFarthest(_camPos, (int)((float)maxCells * 0.85f));
			}
		}

		private void PruneFarthest(Vector3 camPos, int keep)
		{
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			int num = _cells.Count - keep;
			if (num <= 0)
			{
				return;
			}
			List<KeyValuePair<float, Cell>> list = new List<KeyValuePair<float, Cell>>(_cells.Count);
			foreach (Cell cell in _cells)
			{
				float num2 = cell.Pos.x - camPos.x;
				float num3 = cell.Pos.y - camPos.y;
				float num4 = cell.Pos.z - camPos.z;
				list.Add(new KeyValuePair<float, Cell>(num2 * num2 + num3 * num3 + num4 * num4, cell));
			}
			list.Sort((KeyValuePair<float, Cell> x, KeyValuePair<float, Cell> y) => y.Key.CompareTo(x.Key));
			for (int num5 = 0; num5 < num && num5 < list.Count; num5++)
			{
				if (_index.TryGetValue(list[num5].Value.Key, out var value))
				{
					RemoveAt(value);
				}
			}
		}

		private void RemoveAt(int i)
		{
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			int num = _cells.Count - 1;
			Cell cell = _cells[i];
			_index.Remove(cell.Key);
			if (i != num)
			{
				Cell cell2 = _cells[num];
				_cells[i] = cell2;
				_index[cell2.Key] = i;
				_render.Set(i, cell2.M);
			}
			_cells.RemoveAt(num);
		}

		private void ClearAll()
		{
			_cells.Clear();
			_index.Clear();
		}

		private float VisualDepth(Cell c)
		{
			float value = _c.TrenchRadius.Value;
			if (value <= 0f)
			{
				return c.Depth;
			}
			if (MathF.Abs(c.Pos.y - _camPos.y) > 4f)
			{
				return c.Depth;
			}
			float num = c.Pos.x - _camPos.x;
			float num2 = c.Pos.z - _camPos.z;
			float num3 = MathF.Sqrt(num * num + num2 * num2);
			float num4 = Mathf.SmoothStep(0f, 1f, (num3 - value) / value);
			return Mathf.Lerp(MathF.Min(c.Depth, _c.TrenchDepth.Value), c.Depth, num4);
		}

		private void RebuildAll()
		{
			//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)
			_lastRebuildCam = _camPos;
			for (int i = 0; i < _cells.Count; i++)
			{
				Rebuild(_cells[i], i);
			}
		}

		private void Rebuild(Cell c, int slot)
		{
			//IL_00b3: 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_00c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00da: Unknown result type (might be due to invalid IL or missing references)
			//IL_00df: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_0104: Unknown result type (might be due to invalid IL or missing references)
			//IL_0106: Unknown result type (might be due to invalid IL or missing references)
			//IL_010b: Unknown result type (might be due to invalid IL or missing references)
			//IL_010d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0112: Unknown result type (might be due to invalid IL or missing references)
			//IL_0115: Unknown result type (might be due to invalid IL or missing references)
			//IL_0117: Unknown result type (might be due to invalid IL or missing references)
			//IL_012f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0134: Unknown result type (might be due to invalid IL or missing references)
			//IL_0139: Unknown result type (might be due to invalid IL or missing references)
			//IL_0146: Unknown result type (might be due to invalid IL or missing references)
			float cellSize = _c.CellSize;
			float num = MathF.Max(0.0005f, VisualDepth(c) + c.Jitter);
			float num2 = Mathf.SmoothStep(0f, 1f, num / 0.02f);
			float num3 = cellSize * Mathf.Lerp(0.09f, 0.72f, num2);
			float num4 = Mathf.Clamp(c.Normal.y, 0.2f, 1f);
			float num5 = MathF.Sqrt(MathF.Max(0f, 1f - num4 * num4)) / num4;
			float num6 = MathF.Min(1.2f, 0.06f + num3 * num5);
			float num7 = Mathf.SmoothStep(0f, 1f, num / 0.25f);
			Vector3 val = c.Normal * (1f - num7) + Vector3.up * num7;
			Vector3 normalized = ((Vector3)(ref val)).normalized;
			float num8 = num + num6;
			Vector3 val2 = c.Pos + normalized * (num8 * 0.5f - num6);
			Quaternion val3 = Quaternion.FromToRotation(Vector3.up, normalized);
			c.M = Matrix4x4.TRS(val2, val3, new Vector3(2f * num3, num8 * 0.5f, 2f * num3));
			_render.Set(slot, c.M);
		}

		private byte Classify(Collider col)
		{
			if ((Object)(object)col == (Object)null)
			{
				return 0;
			}
			int instanceID = ((Object)col).GetInstanceID();
			if (_surfCache.TryGetValue(instanceID, out var value))
			{
				return value;
			}
			value = ClassifyUncached(col);
			if (_surfCache.Count > 20000)
			{
				_surfCache.Clear();
			}
			_surfCache[instanceID] = value;
			return value;
		}

		private byte ClassifyUncached(Collider col)
		{
			string text = "";
			try
			{
				text = ((Object)col).name + "|" + LayerMask.LayerToName(((Component)col).gameObject.layer);
				Renderer componentInParent = ((Component)col).GetComponentInParent<Renderer>();
				if ((Object)(object)componentInParent != (Object)null)
				{
					Material sharedMaterial = componentInParent.sharedMaterial;
					if ((Object)(object)sharedMaterial != (Object)null)
					{
						text = text + "|" + ((Object)sharedMaterial).name;
						if ((Object)(object)sharedMaterial.shader != (Object)null)
						{
							text = text + "|" + ((Object)sharedMaterial.shader).name;
						}
					}
				}
			}
			catch (Exception)
			{
			}
			text = text.ToLowerInvariant();
			if (ContainsAny(text, _ignoreKw))
			{
				return 3;
			}
			if (ContainsAny(text, _iceKw))
			{
				return 2;
			}
			if (ContainsAny(text, _waterKw))
			{
				return 1;
			}
			return 0;
		}

		private void Stats(float dt)
		{
			//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c4: Expected O, but got Unknown
			float value = _c.StatsEverySeconds.Value;
			if (value <= 0f)
			{
				return;
			}
			_statAcc += dt;
			if (_statAcc < value)
			{
				return;
			}
			float num = 0f;
			float num2 = 0f;
			foreach (Cell cell in _cells)
			{
				if (cell.Depth > num)
				{
					num = cell.Depth;
				}
				num2 += cell.Depth;
			}
			float num3 = ((_cells.Count > 0) ? (num2 / (float)_cells.Count) : 0f);
			ManualLogSource logger = Plugin.Logger;
			bool flag = default(bool);
			BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(156, 16, ref flag);
			if (flag)
			{
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Grains stats /");
				((BepInExLogInterpolatedStringHandler)val).AppendFormatted<float>(_statAcc, "0");
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral("s: patches=");
				((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(_cells.Count);
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" depth max=");
				((BepInExLogInterpolatedStringHandler)val).AppendFormatted<float>(num, "0.00");
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" m mean=");
				((BepInExLogInterpolatedStringHandler)val).AppendFormatted<float>(num3, "0.00");
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" m | rays=");
				((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(_stRays);
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" landed=");
				((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(_stLanded);
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" ");
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral("missed=");
				((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(_stMiss);
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" water-melted=");
				((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(_stWaterMelted);
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" too-steep=");
				((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(_stSlope);
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" moving=");
				((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(_stMoving);
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" ignored=");
				((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(_stIgnored);
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" at-cap=");
				((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(_stCap);
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" ");
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral("surfaces solid/water/ice/ignore=");
				((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(_stSurf[0]);
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral("/");
				((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(_stSurf[1]);
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral("/");
				((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(_stSurf[2]);
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral("/");
				((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(_stSurf[3]);
			}
			logger.LogInfo(val);
			_statAcc = 0f;
			_stRays = (_stMiss = (_stLanded = (_stWaterMelted = (_stSlope = (_stMoving = (_stIgnored = (_stCap = 0)))))));
			Array.Clear(_stSurf, 0, _stSurf.Length);
		}

		private static long Pack(int x, int y, int z)
		{
			return ((long)(x & 0x1FFFFF) << 42) | ((long)(y & 0x1FFFFF) << 21) | (z & 0x1FFFFF);
		}

		private static float Hash01(long k)
		{
			long num = k * -7046029254386353131L;
			long num2 = (num ^ (num >>> 29)) * -4658895280553007687L;
			return (float)(ulong)((num2 ^ (num2 >>> 32)) & 0xFFFFFF) / 16777215f;
		}

		private static float Hash2(int x, int z)
		{
			return Hash01(((long)x << 32) ^ (uint)z);
		}

		private static float Noise2(float x, float z)
		{
			int num = Mathf.FloorToInt(x);
			int num2 = Mathf.FloorToInt(z);
			float num3 = x - (float)num;
			float num4 = z - (float)num2;
			num3 = num3 * num3 * (3f - 2f * num3);
			num4 = num4 * num4 * (3f - 2f * num4);
			float num5 = Hash2(num, num2);
			float num6 = Hash2(num + 1, num2);
			float num7 = Hash2(num, num2 + 1);
			float num8 = Hash2(num + 1, num2 + 1);
			return Mathf.Lerp(Mathf.Lerp(num5, num6, num3), Mathf.Lerp(num7, num8, num3), num4);
		}

		private static string[] Split(string s)
		{
			if (string.IsNullOrWhiteSpace(s))
			{
				return new string[0];
			}
			string[] array = s.Split(new char[2] { ',', ';' }, StringSplitOptions.RemoveEmptyEntries);
			for (int i = 0; i < array.Length; i++)
			{
				array[i] = array[i].Trim().ToLowerInvariant();
			}
			return array;
		}

		private static bool ContainsAny(string text, string[] kws)
		{
			for (int i = 0; i < kws.Length; i++)
			{
				if (kws[i].Length > 0 && text.Contains(kws[i]))
				{
					return true;
				}
			}
			return false;
		}
	}
	public struct WeatherSample
	{
		public float TempC;

		public float Snowfall;
	}
	public struct ModelSettings
	{
		public float SnowIntensity;

		public float MeltSpeed;

		public float MaxGround;

		public float MaxRoof;

		public float RoofAccumulation;

		public float RoofMelt;

		public float IceFreezeDays;

		public float IceThawDays;
	}
	public sealed class SnowModel
	{
		public const float AccumPerDay = 0.75f;

		public const float MeltPerDegreeDay = 0.25f;

		public const float SublimationPerDay = 0.004f;

		private const float MaxSubStepDays = 0.05f;

		public float Ground;

		public float Roof;

		public float Ice;

		public SnowModel(float ground, float roof)
		{
			Ground = Math.Clamp(ground, 0f, 1f);
			Roof = Math.Clamp(roof, 0f, 1f);
		}

		public void Step(float dtDays, in WeatherSample w, in ModelSettings s)
		{
			if (!(dtDays <= 0f))
			{
				int num = Math.Max(1, (int)MathF.Ceiling(dtDays / 0.05f));
				float num2 = dtDays / (float)num;
				for (int i = 0; i < num; i++)
				{
					Ground = Advance(Ground, s.MaxGround, num2, in w, 1f, 1f, in s);
					Roof = Advance(Roof, s.MaxRoof, num2, in w, s.RoofAccumulation, s.RoofMelt, in s);
					float num3 = Math.Clamp((0f - (w.TempC + 1f)) / 4f, 0f, 1f);
					float num4 = Math.Clamp(w.TempC / 3f, 0f, 1f);
					Ice = Math.Clamp(Ice + num2 * num3 / MathF.Max(0.05f, s.IceFreezeDays) - num2 * num4 / MathF.Max(0.05f, s.IceThawDays), 0f, 1f);
				}
			}
		}

		private static float Advance(float cover, float max, float h, in WeatherSample w, float accumMul, float meltMul, in ModelSettings s)
		{
			if (max <= 0.0001f)
			{
				return 0f;
			}
			float num = ((w.TempC <= 0f) ? 1f : Math.Clamp(1f - w.TempC / 1.5f, 0f, 1f));
			float num2 = w.Snowfall * num * 0.75f * s.SnowIntensity * accumMul * h;
			num2 *= 1f - 0.5f * (cover / max);
			float num3 = MathF.Max(0f, w.TempC) * 0.25f * s.MeltSpeed * meltMul * h;
			float num4 = ((w.TempC <= 0f && w.Snowfall < 0.05f) ? (0.004f * h) : 0f);
			return Math.Clamp(cover + num2 - num3 - num4, 0f, max);
		}
	}
	[BepInPlugin("com.local.sotf.realisticwinter", "Sons Realistic Winter", "0.3.0")]
	public sealed class Plugin : BasePlugin
	{
		public const string Guid = "com.local.sotf.realisticwinter";

		public const string Name = "Sons Realistic Winter";

		public const string Version = "0.3.0";

		internal static ManualLogSource Logger;

		internal static WinterConfig Cfg;

		public override void Load()
		{
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Expected O, but got Unknown
			Logger = ((BasePlugin)this).Log;
			Cfg = new WinterConfig(((BasePlugin)this).Config);
			if (!Cfg.Enabled.Value)
			{
				((BasePlugin)this).Log.LogInfo((object)"Disabled in config (General.Enabled = false).");
				return;
			}
			ClassInjector.RegisterTypeInIl2Cpp<WinterRunner>();
			((BasePlugin)this).AddComponent<WinterRunner>();
			ManualLogSource log = ((BasePlugin)this).Log;
			bool flag = default(bool);
			BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(56, 5, ref flag);
			if (flag)
			{
				((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>("Sons Realistic Winter");
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" ");
				((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>("0.3.0");
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" loaded. PerfProfile=");
				((BepInExLogInterpolatedStringHandler)val).AppendFormatted<PerfProfile>(Cfg.Profile.Value);
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral(", ");
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral("update every ");
				((BepInExLogInterpolatedStringHandler)val).AppendFormatted<float>(Cfg.UpdateInterval, "0.0");
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral("s, ");
				((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(Cfg.MaterialsPerTick);
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" materials/tick.");
			}
			log.LogInfo(val);
			if (string.IsNullOrWhiteSpace(Cfg.GroundProperty.Value) && string.IsNullOrWhiteSpace(Cfg.RoofProperty.Value))
			{
				((BasePlugin)this).Log.LogWarning((object)"Targets.GroundProperty / RoofProperty are empty: the simulation runs but nothing is drawn yet. Run the probe (see README) and fill them in.");
			}
		}
	}
	public static class HookProbe
	{
		private static readonly string[] SkipAssemblyPrefixes = new string[14]
		{
			"System", "mscorlib", "netstandard", "Microsoft", "Mono.", "BepInEx", "0Harmony", "HarmonyX", "Il2CppInterop", "Il2Cpp",
			"MonoMod", "Newtonsoft", "UnityEngine", "SotfRealisticWinter"
		};

		public static void Run(WinterConfig c)
		{
			//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
			string text = Path.Combine(Paths.BepInExRootPath, "SotfRealisticWinter_probe.txt");
			string[] array = c.ProbeKeywords.Value.Split(new char[2] { ',', ';' }, StringSplitOptions.RemoveEmptyEntries);
			for (int i = 0; i < array.Length; i++)
			{
				array[i] = array[i].Trim().ToLowerInvariant();
			}
			int max = c.ProbeMaxLines.Value;
			StreamWriter w;
			int lines;
			try
			{
				w = new StreamWriter(text, append: false);
				try
				{
					lines = 0;
					L("# SotfRealisticWinter probe  " + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
					string[] obj = new string[6]
					{
						"unity=",
						Application.unityVersion,
						"  game=",
						Application.version,
						"  scene=",
						null
					};
					Scene activeScene = SceneManager.GetActiveScene();
					obj[5] = ((Scene)(ref activeScene)).name;
					L(string.Concat(obj));
					L("keywords=" + string.Join(",", array));
					L("");
					DumpTypes(L, array);
					DumpShaders(L, array);
					w.Flush();
					Plugin.Logger.LogInfo((object)("Probe written: " + text + " (" + Math.Min(lines, max) + " lines)"));
				}
				finally
				{
					if (w != null)
					{
						((IDisposable)w).Dispose();
					}
				}
			}
			catch (Exception ex)
			{
				Plugin.Logger.LogError((object)("Probe failed: " + ex));
			}
			void L(string s)
			{
				if (lines++ < max)
				{
					w.WriteLine(s);
				}
				else if (lines == max + 1)
				{
					w.WriteLine("... (cut: MaxLines reached)");
				}
			}
		}

		private static void DumpTypes(Action<string> L, string[] kw)
		{
			L("## TYPES (name contains a keyword)");
			int num = 0;
			Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
			foreach (Assembly assembly in assemblies)
			{
				string name = assembly.GetName().Name;
				if (StartsWithAny(name, SkipAssemblyPrefixes))
				{
					continue;
				}
				Type[] types;
				try
				{
					types = assembly.GetTypes();
				}
				catch (ReflectionTypeLoadException ex)
				{
					types = ex.Types;
				}
				catch (Exception)
				{
					continue;
				}
				if (types == null)
				{
					continue;
				}
				Type[] array = types;
				foreach (Type type in array)
				{
					if (!(type == null))
					{
						string text;
						try
						{
							text = type.FullName ?? type.Name;
						}
						catch (Exception)
						{
							continue;
						}
						if (ContainsAny(text, kw))
						{
							num++;
							L("[" + name + "] " + text);
							DumpMembers(L, type, kw);
						}
					}
				}
			}
			L("types listed: " + num);
			L("");
		}

		private static void DumpMembers(Action<string> L, Type t, string[] kw)
		{
			try
			{
				List<string> list = new List<string>();
				int num = 0;
				MemberInfo[] members = t.GetMembers(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public);
				foreach (MemberInfo memberInfo in members)
				{
					if (memberInfo.MemberType != MemberTypes.Constructor)
					{
						num++;
						if (ContainsAny(memberInfo.Name, kw) && list.Count < 20)
						{
							list.Add(memberInfo.MemberType.ToString()[0] + ":" + memberInfo.Name);
						}
					}
				}
				if (list.Count > 0)
				{
					L("    matching members: " + string.Join(", ", list) + "  (of " + num + ")");
				}
			}
			catch (Exception)
			{
			}
		}

		private static void DumpShaders(Action<string> L, string[] kw)
		{
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			L("## SHADERS (name or a property contains a keyword)");
			HashSet<string> hashSet = new HashSet<string>();
			Il2CppArrayBase<Shader> val = Resources.FindObjectsOfTypeAll<Shader>();
			int num = 0;
			for (int i = 0; i < val.Length; i++)
			{
				Shader val2 = val[i];
				if ((Object)(object)val2 == (Object)null)
				{
					continue;
				}
				string name = ((Object)val2).name;
				if (!hashSet.Add(name))
				{
					continue;
				}
				bool flag = ContainsAny(name, kw);
				List<string> list = new List<string>();
				try
				{
					int propertyCount = val2.GetPropertyCount();
					for (int j = 0; j < propertyCount; j++)
					{
						string propertyName = val2.GetPropertyName(j);
						if (ContainsAny(propertyName, kw))
						{
							list.Add(propertyName + " (" + ((object)val2.GetPropertyType(j)/*cast due to .constrained prefix*/).ToString() + ")");
						}
					}
				}
				catch (Exception)
				{
				}
				if (!flag && list.Count == 0)
				{
					continue;
				}
				num++;
				L((flag ? "[name] " : "       ") + name);
				foreach (string item in list)
				{
					L("      prop: " + item);
				}
			}
			L("shaders listed: " + num + " of " + hashSet.Count);
			L("");
			L("## MATERIALS per matching shader (count)");
			Dictionary<string, int> dictionary = new Dictionary<string, int>();
			Il2CppArrayBase<Material> val3 = Resources.FindObjectsOfTypeAll<Material>();
			for (int k = 0; k < val3.Length; k++)
			{
				Material val4 = val3[k];
				if (!((Object)(object)val4 == (Object)null) && !((Object)(object)val4.shader == (Object)null))
				{
					string name2 = ((Object)val4.shader).name;
					if (ContainsAny(name2, kw))
					{
						dictionary[name2] = ((!dictionary.TryGetValue(name2, out var value)) ? 1 : (value + 1));
					}
				}
			}
			foreach (KeyValuePair<string, int> item2 in dictionary)
			{
				L(item2.Value + " x " + item2.Key);
			}
		}

		private static bool ContainsAny(string s, string[] kw)
		{
			if (string.IsNullOrEmpty(s))
			{
				return false;
			}
			string text = s.ToLowerInvariant();
			for (int i = 0; i < kw.Length; i++)
			{
				if (kw[i].Length > 0 && text.Contains(kw[i]))
				{
					return true;
				}
			}
			return false;
		}

		private static bool StartsWithAny(string s, string[] prefixes)
		{
			for (int i = 0; i < prefixes.Length; i++)
			{
				if (s.StartsWith(prefixes[i], StringComparison.Ordinal))
				{
					return true;
				}
			}
			return false;
		}
	}
	public sealed class WinterRunner : MonoBehaviour
	{
		private WinterConfig _c;

		private IWeatherSource _weather;

		private SnowModel _model;

		private bool _init;

		private bool _failed;

		private float _acc;

		private float _shownGround;

		private float _shownRoof;

		private float _sentGroundGlobal = -1f;

		private float _sentRoofGlobal = -1f;

		private float _matGround = -1f;

		private float _matRoof = -1f;

		private string _groundName = "";

		private string _roofName = "";

		private int _groundId;

		private int _roofId;

		private readonly List<Material> _mats = new List<Material>();

		private readonly List<byte> _matFlags = new List<byte>();

		private int _cursor;

		private bool _matDirty;

		private int _sceneHandle = int.MinValue;

		private float _rescanAt = -1f;

		private KeyCode _probeKey;

		private bool _inputBroken;

		private bool _autoProbeDone;

		private float _startTime;

		private float _nextProbeAt;

		private GrainSystem _grains;

		public WinterRunner(IntPtr ptr)
			: base(ptr)
		{
		}

		private void EnsureInit()
		{
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			if (!_init)
			{
				_init = true;
				_c = Plugin.Cfg;
				_weather = new StandaloneWeather(_c);
				_model = new SnowModel(_c.StartGroundCover.Value, _c.StartRoofCover.Value);
				_shownGround = _model.Ground;
				_shownRoof = _model.Roof;
				_startTime = Time.time;
				if (!Enum.TryParse<KeyCode>(_c.ProbeHotkey.Value, ignoreCase: true, out _probeKey))
				{
					_probeKey = (KeyCode)0;
				}
				if (_c.GrainsEnabled.Value)
				{
					_grains = new GrainSystem(_c);
				}
			}
		}

		private void Update()
		{
			if (_failed)
			{
				return;
			}
			try
			{
				Tick();
			}
			catch (Exception ex)
			{
				_failed = true;
				Plugin.Logger.LogError((object)("WinterRunner stopped after an error (mod disabled until restart): " + ex));
			}
		}

		private void Tick()
		{
			EnsureInit();
			float num = Mathf.Min(Time.deltaTime, 0.25f);
			if (_c.ProbeEnabled.Value)
			{
				HandleProbe();
			}
			if (_grains != null)
			{
				try
				{
					_grains.Frame(num, _weather.Current, _model.Ice);
				}
				catch (Exception ex)
				{
					Plugin.Logger.LogError((object)("Grains stopped after an error (the rest of the mod keeps running): " + ex));
					_grains = null;
				}
			}
			_acc += num;
			if (_acc >= _c.UpdateInterval)
			{
				SimulationTick(_acc);
				_acc = 0f;
			}
			EaseAndApplyGlobals(num);
		}

		private void SimulationTick(float realSeconds)
		{
			float num = Mathf.Max(1f, _c.VirtualDayMinutes.Value * 60f);
			float dtDays = realSeconds / num;
			_weather.Advance(dtDays);
			_model.Step(dtDays, _weather.Current, BuildSettings());
			RefreshTargetNames();
			CheckScene();
			if (_c.ApplyGlobals.Value && _c.ReapplyGlobalsEveryTick.Value)
			{
				_sentGroundGlobal = -1f;
				_sentRoofGlobal = -1f;
			}
			if (_c.ApplyMaterials.Value)
			{
				MaterialSlice();
			}
		}

		private ModelSettings BuildSettings()
		{
			return new ModelSettings
			{
				SnowIntensity = _c.SnowIntensity.Value,
				MeltSpeed = _c.MeltSpeed.Value,
				MaxGround = _c.MaxGroundCover.Value,
				MaxRoof = _c.MaxRoofCover.Value,
				RoofAccumulation = _c.RoofAccumulation.Value,
				RoofMelt = _c.RoofMelt.Value,
				IceFreezeDays = _c.WaterFreezeDays.Value,
				IceThawDays = _c.WaterThawDays.Value
			};
		}

		private void RefreshTargetNames()
		{
			string text = (_c.GroundProperty.Value ?? "").Trim();
			string text2 = (_c.RoofProperty.Value ?? "").Trim();
			if (text != _groundName)
			{
				_groundName = text;
				_groundId = ((text.Length > 0) ? Shader.PropertyToID(text) : 0);
				_sentGroundGlobal = -1f;
				_rescanAt = Time.time;
			}
			if (text2 != _roofName)
			{
				_roofName = text2;
				_roofId = ((text2.Length > 0) ? Shader.PropertyToID(text2) : 0);
				_sentRoofGlobal = -1f;
				_rescanAt = Time.time;
			}
		}

		private void EaseAndApplyGlobals(float dt)
		{
			float value = _c.SmoothingSeconds.Value;
			float num = ((value <= 0.01f) ? 1f : (1f - Mathf.Exp((0f - dt) / value)));
			_shownGround += (_model.Ground - _shownGround) * num;
			_shownRoof += (_model.Roof - _shownRoof) * num;
			float num2 = _c.ChangeEpsilon * 0.5f;
			if (_c.ApplyGlobals.Value)
			{
				if (_groundName.Length > 0 && Mathf.Abs(_shownGround - _sentGroundGlobal) > num2)
				{
					Shader.SetGlobalFloat(_groundId, _shownGround);
					_sentGroundGlobal = _shownGround;
				}
				if (_roofName.Length > 0 && Mathf.Abs(_shownRoof - _sentRoofGlobal) > num2)
				{
					Shader.SetGlobalFloat(_roofId, _shownRoof);
					_sentRoofGlobal = _shownRoof;
				}
			}
			if (_c.ApplyMaterials.Value && (Mathf.Abs(_shownGround - _matGround) > _c.ChangeEpsilon || Mathf.Abs(_shownRoof - _matRoof) > _c.ChangeEpsilon))
			{
				_matGround = _shownGround;
				_matRoof = _shownRoof;
				_matDirty = true;
				_cursor = 0;
			}
		}

		private void CheckScene()
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			Scene activeScene = SceneManager.GetActiveScene();
			int handle = ((Scene)(ref activeScene)).handle;
			if (handle != _sceneHandle)
			{
				_sceneHandle = handle;
				_rescanAt = Time.time + 5f;
			}
			float value = _c.MaterialRescanSeconds.Value;
			if (value > 0f && _rescanAt < 0f)
			{
				_rescanAt = Time.time + value;
			}
		}

		private void MaterialSlice()
		{
			if (_rescanAt >= 0f && Time.time >= _rescanAt)
			{
				RescanMaterials();
				float value = _c.MaterialRescanSeconds.Value;
				_rescanAt = ((value > 0f) ? (Time.time + value) : (-1f));
				_matDirty = true;
				_cursor = 0;
			}
			if (!_matDirty)
			{
				return;
			}
			int materialsPerTick = _c.MaterialsPerTick;
			while (materialsPerTick-- > 0 && _cursor < _mats.Count)
			{
				Material val = _mats[_cursor];
				byte b = _matFlags[_cursor];
				_cursor++;
				if (!((Object)(object)val == (Object)null))
				{
					if ((b & 1) != 0)
					{
						val.SetFloat(_groundId, _matGround);
					}
					if ((b & 2) != 0)
					{
						val.SetFloat(_roofId, _matRoof);
					}
				}
			}
			if (_cursor >= _mats.Count)
			{
				_matDirty = false;
			}
		}

		private void RescanMaterials()
		{
			//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c9: Expected O, but got Unknown
			_mats.Clear();
			_matFlags.Clear();
			if (_groundName.Length == 0 && _roofName.Length == 0)
			{
				return;
			}
			Il2CppArrayBase<Material> val = Resources.FindObjectsOfTypeAll<Material>();
			for (int i = 0; i < val.Length; i++)
			{
				Material val2 = val[i];
				if (!((Object)(object)val2 == (Object)null))
				{
					byte b = 0;
					if (_groundName.Length > 0 && val2.HasProperty(_groundId))
					{
						b |= 1;
					}
					if (_roofName.Length > 0 && val2.HasProperty(_roofId))
					{
						b |= 2;
					}
					if (b != 0)
					{
						_mats.Add(val2);
						_matFlags.Add(b);
					}
				}
			}
			ManualLogSource logger = Plugin.Logger;
			bool flag = default(bool);
			BepInExInfoLogInterpolatedStringHandler val3 = new BepInExInfoLogInterpolatedStringHandler(57, 2, ref flag);
			if (flag)
			{
				((BepInExLogInterpolatedStringHandler)val3).AppendLiteral("Material scan: ");
				((BepInExLogInterpolatedStringHandler)val3).AppendFormatted<int>(_mats.Count);
				((BepInExLogInterpolatedStringHandler)val3).AppendLiteral(" of ");
				((BepInExLogInterpolatedStringHandler)val3).AppendFormatted<int>(val.Length);
				((BepInExLogInterpolatedStringHandler)val3).AppendLiteral(" materials have the target properties.");
			}
			logger.LogInfo(val3);
		}

		private void HandleProbe()
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			bool flag = false;
			if (!_inputBroken && (int)_probeKey != 0)
			{
				try
				{
					flag = Input.GetKeyDown(_probeKey);
				}
				catch (Exception)
				{
					_inputBroken = true;
					Plugin.Logger.LogWarning((object)"Legacy Input is not available; hotkey disabled. Use Probe.AutoRunDelaySeconds instead.");
				}
			}
			float value = _c.ProbeAutoDelaySeconds.Value;
			if (!_autoProbeDone && value > 0f && Time.time - _startTime >= value)
			{
				_autoProbeDone = true;
				flag = true;
			}
			if (flag && Time.time < _nextProbeAt)
			{
				flag = false;
			}
			if (flag)
			{
				_nextProbeAt = Time.time + 3f;
				HookProbe.Run(_c);
			}
		}
	}
	public interface IWeatherSource
	{
		WeatherSample Current { get; }

		float Days { get; }

		void Advance(float dtDays);
	}
	public sealed class StandaloneWeather : IWeatherSource
	{
		private readonly WinterConfig _c;

		private readonly Random _rng;

		private readonly int _seed;

		private float _t;

		private float _episodeLeft;

		private float _episodeLen = 1f;

		private float _episodePeak;

		private float _gapLeft;

		private float _snow;

		public WeatherSample Current { get; private set; }

		public float Days => _t;

		public StandaloneWeather(WinterConfig c)
		{
			_c = c;
			_seed = c.Seed.Value;
			_rng = new Random(_seed);
			_gapLeft = Range(0.1f, 0.6f) * c.EffGapScale;
			Current = new WeatherSample
			{
				TempC = Temperature(0f),
				Snowfall = 0f
			};
		}

		public void Advance(float dtDays)
		{
			if (dtDays <= 0f)
			{
				return;
			}
			_t += dtDays;
			float num = Temperature(_t);
			bool flag = num < 1f;
			if (_episodeLeft > 0f)
			{
				_episodeLeft -= dtDays;
				if (_episodeLeft <= 0f)
				{
					_episodeLeft = 0f;
					_gapLeft = Range(0.12f, 0.8f) * _c.EffGapScale;
				}
			}
			else
			{
				_gapLeft -= dtDays;
				if (_gapLeft <= 0f)
				{
					if (flag)
					{
						_episodeLen = Range(0.3f, 1f) * _c.EffEpisodeScale;
						_episodeLeft = _episodeLen;
						_episodePeak = Range(_c.EffPeakMin, 1f);
					}
					else
					{
						_gapLeft = 0.1f;
					}
				}
			}
			float num2 = 0f;
			if (_episodeLeft > 0f && flag)
			{
				float num3 = 1f - _episodeLeft / _episodeLen;
				num2 = _episodePeak * Smooth01(MathF.Sin((float)Math.PI * num3));
			}
			_snow += (num2 - _snow) * MathF.Min(1f, dtDays * 12f);
			Current = new WeatherSample
			{
				TempC = num,
				Snowfall = _snow
			};
		}

		private float Temperature(float t)
		{
			float effColdDays = _c.EffColdDays;
			float num = effColdDays + _c.EffThawDays;
			bool value = _c.LoopCycle.Value;
			float num2 = (value ? (t % num) : MathF.Min(t, num - 0.001f));
			float num3 = ((value && t >= num && num2 < 1f) ? (1f - Smooth01(num2)) : ((!(num2 < effColdDays)) ? Smooth01((num2 - effColdDays) / 1f) : 0f));
			float num4 = _c.EffColdMeanC + (_c.EffThawMeanC - _c.EffColdMeanC) * num3;
			float num5 = _c.DailySwingC.Value * MathF.Sin((float)Math.PI * 2f * (t - 0.3f));
			float num6 = Noise(t * 0.8f) * 2.5f;
			return num4 + num5 + num6;
		}

		private float Range(float a, float b)
		{
			return a + (float)_rng.NextDouble() * (b - a);
		}

		private static float Smooth01(float x)
		{
			x = Math.Clamp(x, 0f, 1f);
			return x * x * (3f - 2f * x);
		}

		private float Noise(float x)
		{
			int num = (int)MathF.Floor(x);
			float num2 = x - (float)num;
			float num3 = Hash(num);
			float num4 = Hash(num + 1);
			num2 = num2 * num2 * (3f - 2f * num2);
			return num3 + (num4 - num3) * num2;
		}

		private float Hash(int i)
		{
			int num = i * 374761393 + _seed * 668265263;
			int num2 = (num ^ (num >>> 13)) * 1274126177;
			return (float)(uint)((num2 ^ (num2 >>> 16)) & 0xFFFFFF) / 8388607f - 1f;
		}
	}
	public enum PerfProfile
	{
		Low,
		Medium,
		High,
		Custom
	}
	public enum WinterPreset
	{
		Custom,
		Normal,
		Heavy,
		Kamchatka
	}
	public sealed class WinterConfig
	{
		public readonly ConfigEntry<bool> Enabled;

		public readonly ConfigEntry<int> Seed;

		public readonly ConfigEntry<float> VirtualDayMinutes;

		public readonly ConfigEntry<float> ColdDays;

		public readonly ConfigEntry<float> ThawDays;

		public readonly ConfigEntry<bool> LoopCycle;

		public readonly ConfigEntry<float> ColdMeanC;

		public readonly ConfigEntry<float> ThawMeanC;

		public readonly ConfigEntry<float> DailySwingC;

		public readonly ConfigEntry<WinterPreset> Preset;

		public readonly ConfigEntry<float> SnowIntensity;

		public readonly ConfigEntry<float> MeltSpeed;

		public readonly ConfigEntry<float> MaxGroundCover;

		public readonly ConfigEntry<float> MaxRoofCover;

		public readonly ConfigEntry<float> RoofAccumulation;

		public readonly ConfigEntry<float> RoofMelt;

		public readonly ConfigEntry<float> StartGroundCover;

		public readonly ConfigEntry<float> StartRoofCover;

		public readonly ConfigEntry<PerfProfile> Profile;

		public readonly ConfigEntry<float> CustomUpdateInterval;

		public readonly ConfigEntry<int> CustomMaterialsPerTick;

		public readonly ConfigEntry<float> CustomChangeEpsilon;

		public readonly ConfigEntry<float> MaterialRescanSeconds;

		public readonly ConfigEntry<float> SmoothingSeconds;

		public readonly ConfigEntry<string> GroundProperty;

		public readonly ConfigEntry<string> RoofProperty;

		public readonly ConfigEntry<bool> ApplyGlobals;

		public readonly ConfigEntry<bool> ApplyMaterials;

		public readonly ConfigEntry<bool> ReapplyGlobalsEveryTick;

		public readonly ConfigEntry<bool> ProbeEnabled;

		public readonly ConfigEntry<string> ProbeHotkey;

		public readonly ConfigEntry<float> ProbeAutoDelaySeconds;

		public readonly ConfigEntry<string> ProbeKeywords;

		public readonly ConfigEntry<int> ProbeMaxLines;

		public readonly ConfigEntry<bool> GrainsEnabled;

		public readonly ConfigEntry<float> CustomGrainRadius;

		public readonly ConfigEntry<float> CustomFlakesPerSecond;

		public readonly ConfigEntry<int> CustomMaxCells;

		public readonly ConfigEntry<int> CustomMaxRaysPerFrame;

		public readonly ConfigEntry<float> CustomCellSize;

		public readonly ConfigEntry<float> DepthPerVirtualDay;

		public readonly ConfigEntry<float> MaxDepthMeters;

		public readonly ConfigEntry<float> MeltMetersPerDegreeDay;

		public readonly ConfigEntry<float> DriftStrength;

		public readonly ConfigEntry<float> WindDirectionDeg;

		public readonly ConfigEntry<float> ShelterMeters;

		public readonly ConfigEntry<float> TrenchRadius;

		public readonly ConfigEntry<float> TrenchDepth;

		public readonly ConfigEntry<float> MaxSlopeDeg;

		public readonly ConfigEntry<bool> SkipMoving;

		public readonly ConfigEntry<float> RayStartHeight;

		public readonly ConfigEntry<string> WaterLayers;

		public readonly ConfigEntry<string> WaterKeywords;

		public readonly ConfigEntry<string> IceKeywords;

		public readonly ConfigEntry<string> IgnoreKeywords;

		public readonly ConfigEntry<bool> SnowOnLiquidWater;

		public readonly ConfigEntry<float> WaterFreezeDays;

		public readonly ConfigEntry<float> WaterThawDays;

		public readonly ConfigEntry<float> WaterFreezeThreshold;

		public readonly ConfigEntry<string> GrainColor;

		public readonly ConfigEntry<string> GrainShader;

		public readonly ConfigEntry<bool> UseInstancing;

		public readonly ConfigEntry<string> IgnoreSceneKeywords;

		public readonly ConfigEntry<bool> DebugForceSnow;

		public readonly ConfigEntry<string> DebugForceKey;

		public readonly ConfigEntry<string> ClearKey;

		public readonly ConfigEntry<float> StatsEverySeconds;

		public float UpdateInterval => Profile.Value switch
		{
			PerfProfile.Low => 2f, 
			PerfProfile.Medium => 1f, 
			PerfProfile.High => 0.4f, 
			_ => CustomUpdateInterval.Value, 
		};

		public int MaterialsPerTick => Profile.Value switch
		{
			PerfProfile.Low => 6, 
			PerfProfile.Medium => 24, 
			PerfProfile.High => 96, 
			_ => CustomMaterialsPerTick.Value, 
		};

		public float EffDepthPerDay => Preset.Value switch
		{
			WinterPreset.Normal => 0.35f, 
			WinterPreset.Heavy => 0.6f, 
			WinterPreset.Kamchatka => 1f, 
			_ => DepthPerVirtualDay.Value, 
		};

		public float EffMaxDepth => Preset.Value switch
		{
			WinterPreset.Normal => 3f, 
			WinterPreset.Heavy => 8f, 
			WinterPreset.Kamchatka => 0f, 
			_ => MaxDepthMeters.Value, 
		};

		public float EffMelt => Preset.Value switch
		{
			WinterPreset.Normal => 0.6f, 
			WinterPreset.Heavy => 1.5f, 
			WinterPreset.Kamchatka => 2.5f, 
			_ => MeltMetersPerDegreeDay.Value, 
		};

		public float EffColdDays => Preset.Value switch
		{
			WinterPreset.Heavy => 8f, 
			WinterPreset.Kamchatka => 12f, 
			_ => ColdDays.Value, 
		};

		public float EffThawDays => Preset.Value switch
		{
			WinterPreset.Heavy => 3f, 
			WinterPreset.Kamchatka => 3f, 
			_ => ThawDays.Value, 
		};

		public float EffColdMeanC => Preset.Value switch
		{
			WinterPreset.Heavy => -8f, 
			WinterPreset.Kamchatka => -12f, 
			_ => ColdMeanC.Value, 
		};

		public float EffThawMeanC => Preset.Value switch
		{
			WinterPreset.Heavy => 3f, 
			WinterPreset.Kamchatka => 4f, 
			_ => ThawMeanC.Value, 
		};

		public float EffGapScale => Preset.Value switch
		{
			WinterPreset.Heavy => 0.7f, 
			WinterPreset.Kamchatka => 0.5f, 
			_ => 1f, 
		};

		public float EffEpisodeScale => Preset.Value switch
		{
			WinterPreset.Heavy => 1.2f, 
			WinterPreset.Kamchatka => 1.4f, 
			_ => 1f, 
		};

		public float EffPeakMin => Preset.Value switch
		{
			WinterPreset.Heavy => 0.6f, 
			WinterPreset.Kamchatka => 0.7f, 
			_ => 0.5f, 
		};

		public float GrainRadius => Profile.Value switch
		{
			PerfProfile.Low => 7f, 
			PerfProfile.Medium => 10f, 
			PerfProfile.High => 14f, 
			_ => CustomGrainRadius.Value, 
		};

		public float FlakesPerSecond => Profile.Value switch
		{
			PerfProfile.Low => 40f, 
			PerfProfile.Medium => 110f, 
			PerfProfile.High => 260f, 
			_ => CustomFlakesPerSecond.Value, 
		};

		public int MaxCells => Profile.Value switch
		{
			PerfProfile.Low => 900, 
			PerfProfile.Medium => 2500, 
			PerfProfile.High => 6000, 
			_ => CustomMaxCells.Value, 
		};

		public int MaxRaysPerFrame => Profile.Value switch
		{
			PerfProfile.Low => 3, 
			PerfProfile.Medium => 8, 
			PerfProfile.High => 16, 
			_ => CustomMaxRaysPerFrame.Value, 
		};

		public float CellSize => Profile.Value switch
		{
			PerfProfile.Low => 0.5f, 
			PerfProfile.Medium => 0.45f, 
			PerfProfile.High => 0.4f, 
			_ => CustomCellSize.Value, 
		};

		public float ChangeEpsilon => Profile.Value switch
		{
			PerfProfile.Low => 0.02f, 
			PerfProfile.Medium => 0.01f, 
			PerfProfile.High => 0.005f, 
			_ => CustomChangeEpsilon.Value, 
		};

		public WinterConfig(ConfigFile c)
		{
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Expected O, but got Unknown
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00af: Expected O, but got Unknown
			//IL_00de: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e8: Expected O, but got Unknown
			//IL_0133: Unknown result type (might be due to invalid IL or missing references)
			//IL_013d: Expected O, but got Unknown
			//IL_016c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0176: Expected O, but got Unknown
			//IL_01a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01af: Expected O, but got Unknown
			//IL_01fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_0204: Expected O, but got Unknown
			//IL_0233: Unknown result type (might be due to invalid IL or missing references)
			//IL_023d: Expected O, but got Unknown
			//IL_026c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0276: Expected O, but got Unknown
			//IL_02a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_02af: Expected O, but got Unknown
			//IL_02de: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e8: Expected O, but got Unknown
			//IL_0317: Unknown result type (might be due to invalid IL or missing references)
			//IL_0321: Expected O, but got Unknown
			//IL_0350: Unknown result type (might be due to invalid IL or missing references)
			//IL_035a: Expected O, but got Unknown
			//IL_0389: Unknown result type (might be due to invalid IL or missing references)
			//IL_0393: Expected O, but got Unknown
			//IL_03de: Unknown result type (might be due to invalid IL or missing references)
			//IL_03e8: Expected O, but got Unknown
			//IL_0410: Unknown result type (might be due to invalid IL or missing references)
			//IL_041a: Expected O, but got Unknown
			//IL_0449: Unknown result type (might be due to invalid IL or missing references)
			//IL_0453: Expected O, but got Unknown
			//IL_0482: Unknown result type (might be due to invalid IL or missing references)
			//IL_048c: Expected O, but got Unknown
			//IL_04bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_04c5: Expected O, but got Unknown
			//IL_05c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_05ce: Expected O, but got Unknown
			//IL_061a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0624: Expected O, but got Unknown
			//IL_066f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0679: Expected O, but got Unknown
			//IL_06a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_06b2: Expected O, but got Unknown
			//IL_06de: Unknown result type (might be due to invalid IL or missing references)
			//IL_06e8: Expected O, but got Unknown
			//IL_070c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0716: Expected O, but got Unknown
			//IL_0745: Unknown result type (might be due to invalid IL or missing references)
			//IL_074f: Expected O, but got Unknown
			//IL_077e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0788: Expected O, but got Unknown
			//IL_07b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_07c1: Expected O, but got Unknown
			//IL_07f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_07fa: Expected O, but got Unknown
			//IL_0829: Unknown result type (might be due to invalid IL or missing references)
			//IL_0833: Expected O, but got Unknown
			//IL_0862: Unknown result type (might be due to invalid IL or missing references)
			//IL_086c: Expected O, but got Unknown
			//IL_089b: Unknown result type (might be due to invalid IL or missing references)
			//IL_08a5: Expected O, but got Unknown
			//IL_08d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_08de: Expected O, but got Unknown
			//IL_090d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0917: Expected O, but got Unknown
			//IL_0946: Unknown result type (might be due to invalid IL or missing references)
			//IL_0950: Expected O, but got Unknown
			//IL_099b: Unknown result type (might be due to invalid IL or missing references)
			//IL_09a5: Expected O, but got Unknown
			//IL_0a70: Unknown result type (might be due to invalid IL or missing references)
			//IL_0a7a: Expected O, but got Unknown
			//IL_0aa9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0ab3: Expected O, but got Unknown
			//IL_0ae2: Unknown result type (might be due to invalid IL or missing references)
			//IL_0aec: Expected O, but got Unknown
			//IL_0bf3: Unknown result type (might be due to invalid IL or missing references)
			//IL_0bfd: Expected O, but got Unknown
			Enabled = c.Bind<bool>("1. General", "Enabled", true, "Master switch for the whole mod.");
			Seed = c.Bind<int>("1. General", "Seed", 1337, "Seed of the standalone weather generator (same seed = same winter).");
			VirtualDayMinutes = c.Bind<float>("1. General", "VirtualDayMinutes", 20f, new ConfigDescription("Real minutes per simulated day of the mod's own weather clock. Not read from the game yet.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 120f), Array.Empty<object>()));
			ColdDays = c.Bind<float>("1. General", "ColdDays", 6f, new ConfigDescription("Length of the cold/snowy phase, in simulated days.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 60f), Array.Empty<object>()));
			ThawDays = c.Bind<float>("1. General", "ThawDays", 3f, new ConfigDescription("Length of the warm phase when snow melts, in simulated days.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 60f), Array.Empty<object>()));
			LoopCycle = c.Bind<bool>("1. General", "LoopCycle", true, "Repeat cold -> thaw. If false: one winter, then permanent thaw.");
			ColdMeanC = c.Bind<float>("1. General", "ColdMeanC", -5f, new ConfigDescription("Mean temperature in the cold phase (C).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(-30f, 0f), Array.Empty<object>()));
			ThawMeanC = c.Bind<float>("1. General", "ThawMeanC", 4f, new ConfigDescription("Mean temperature in the thaw phase (C).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 20f), Array.Empty<object>()));
			DailySwingC = c.Bind<float>("1. General", "DailySwingC", 4f, new ConfigDescription("Day/night temperature swing (+/- C).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 15f), Array.Empty<object>()));
			Preset = c.Bind<WinterPreset>("2. Intensity", "WinterPreset", WinterPreset.Kamchatka, "Normal = about half a metre at peak. Heavy = about 2 m. Kamchatka = about 5-6 m, 12 days of hard frost, frequent storms, NO depth limit. Custom = use the values in 1. General and 6. Grains. (Peaks measured on a Python mirror of the model, not in the game.)");
			SnowIntensity = c.Bind<float>("2. Intensity", "SnowIntensity", 1f, new ConfigDescription("Multiplier on top of the preset. 0.3 = light, 1 = as the preset says, 5 = five times more snow.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.05f, 50f), Array.Empty<object>()));
			MeltSpeed = c.Bind<float>("2. Intensity", "MeltSpeed", 1f, new ConfigDescription("How fast snow melts when it is above 0 C.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.05f, 20f), Array.Empty<object>()));
			MaxGroundCover = c.Bind<float>("2. Intensity", "MaxGroundCover", 1f, new ConfigDescription("Cap for ground coverage, 0..1.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
			MaxRoofCover = c.Bind<float>("2. Intensity", "MaxRoofCover", 1f, new ConfigDescription("Cap for roof coverage, 0..1.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
			RoofAccumulation = c.Bind<float>("2. Intensity", "RoofAccumulation", 1f, new ConfigDescription("Roof build-up relative to ground.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 3f), Array.Empty<object>()));
			RoofMelt = c.Bind<float>("2. Intensity", "RoofMelt", 0.7f, new ConfigDescription("Roof melt relative to ground. <1 = snow stays on roofs longer than on the ground.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 3f), Array.Empty<object>()));
			StartGroundCover = c.Bind<float>("2. Intensity", "StartGroundCover", 0f, new ConfigDescription("Ground coverage at game start, 0..1.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
			StartRoofCover = c.Bind<float>("2. Intensity", "StartRoofCover", 0f, new ConfigDescription("Roof coverage at game start, 0..1.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
			Profile = c.Bind<PerfProfile>("3. Performance", "Profile", PerfProfile.Low, "Low = weakest PCs (rare, small updates). Medium / High = smoother. Custom = use the Custom* values below.");
			CustomUpdateInterval = c.Bind<float>("3. Performance", "CustomUpdateInterval", 1f, new ConfigDescription("Seconds between simulation ticks (Custom only).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 10f), Array.Empty<object>()));
			CustomMaterialsPerTick = c.Bind<int>("3. Performance", "CustomMaterialsPerTick", 24, new ConfigDescription("Max materials written per tick (Custom only).", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 1000), Array.Empty<object>()));
			CustomChangeEpsilon = c.Bind<float>("3. Performance", "CustomChangeEpsilon", 0.01f, new ConfigDescription("Min coverage change before anything is re-applied (Custom only).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.001f, 0.2f), Array.Empty<object>()));
			MaterialRescanSeconds = c.Bind<float>("3. Performance", "MaterialRescanSeconds", 180f, new ConfigDescription("How often to re-search the scene for materials that have the target properties. 0 = only after scene change.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 3600f), Array.Empty<object>()));
			SmoothingSeconds = c.Bind<float>("3. Performance", "SmoothingSeconds", 4f, new ConfigDescription("Easing of the displayed value towards the simulated one.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 60f), Array.Empty<object>()));
			GroundProperty = c.Bind<string>("4. Targets", "GroundProperty", "", "Shader property (float, 0..1) that controls ground snow. EMPTY until you found the real name with the probe.");
			RoofProperty = c.Bind<string>("4. Targets", "RoofProperty", "", "Shader property (float, 0..1) that controls roof/structure snow. EMPTY until found with the probe. May equal GroundProperty if the game has only one.");
			ApplyGlobals = c.Bind<bool>("4. Targets", "ApplyGlobals", true, "Write the values with Shader.SetGlobalFloat.");
			ApplyMaterials = c.Bind<bool>("4. Targets", "ApplyMaterials", false, "Also write the values into every material that has these properties (slower, time-sliced).");
			ReapplyGlobalsEveryTick = c.Bind<bool>("4. Targets", "ReapplyGlobalsEveryTick", true, "Re-send globals each tick in case the game resets them.");
			ProbeEnabled = c.Bind<bool>("5. Probe", "Enabled", true, "Allow the recon probe (writes BepInEx/SotfRealisticWinter_probe.txt).");
			ProbeHotkey = c.Bind<string>("5. Probe", "Hotkey", "F8", "Unity KeyCode name that runs the probe in game. Ignored if the game does not use legacy Input.");
			ProbeAutoDelaySeconds = c.Bind<float>("5. Probe", "AutoRunDelaySeconds", 0f, new ConfigDescription("If > 0: run the probe once this many seconds after start (use ~120 and load a world). 0 = off.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1800f), Array.Empty<object>()));
			ProbeKeywords = c.Bind<string>("5. Probe", "Keywords", "snow,season,winter,weather,frozen,freeze,ice,wet,temperature,cold,melt,frost", "Comma-separated, case-insensitive. Types, shaders and shader properties containing any of these are listed.");
			ProbeMaxLines = c.Bind<int>("5. Probe", "MaxLines", 1500, new ConfigDescription("Hard cap for the probe file.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(100, 20000), Array.Empty<object>()));
			GrainsEnabled = c.Bind<bool>("6. Grains", "Enabled", true, "Snow built flake by flake around the player: where a flake lands a small grain appears, more flakes make it grow until it merges into cover. Needs no game hook.");
			CustomGrainRadius = c.Bind<float>("6. Grains", "CustomRadius", 10f, new ConfigDescription("Metres around the camera where grains are made (Profile = Custom only).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(3f, 30f), Array.Empty<object>()));
			CustomFlakesPerSecond = c.Bind<float>("6. Grains", "CustomFlakesPerSecond", 110f, new ConfigDescription("Flake landings per second at full snowfall (Custom only).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(5f, 1000f), Array.Empty<object>()));
			CustomMaxCells = c.Bind<int>("6. Grains", "CustomMaxCells", 2500, new ConfigDescription("Max snow patches kept at once (Custom only).", (AcceptableValueBase)(object)new AcceptableValueRange<int>(100, 20000), Array.Empty<object>()));
			CustomMaxRaysPerFrame = c.Bind<int>("6. Grains", "CustomMaxRaysPerFrame", 8, new ConfigDescription("Max physics rays per frame (Custom only).", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 64), Array.Empty<object>()));
			CustomCellSize = c.Bind<float>("6. Grains", "CustomCellSize", 0.45f, new ConfigDescription("Patch size in metres (Custom only).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.2f, 1.5f), Array.Empty<object>()));
			MaxSlopeDeg = c.Bind<float>("6. Grains", "MaxSlopeDeg", 55f, new ConfigDescription("Snow does not stay on surfaces steeper than this.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(5f, 89f), Array.Empty<object>()));
			DepthPerVirtualDay = c.Bind<float>("6. Grains", "DepthPerVirtualDay", 1f, new ConfigDescription("Custom preset: metres of snow per simulated day at full snowfall (Kamchatka preset = 1.0). Not limited by the mod.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.001f, 1000f), Array.Empty<object>()));
			MaxDepthMeters = c.Bind<float>("6. Grains", "MaxDepthMeters", 0f, new ConfigDescription("Custom preset: deepest snow in metres. 0 = NO LIMIT.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 10000f), Array.Empty<object>()));
			MeltMetersPerDegreeDay = c.Bind<float>("6. Grains", "MeltMetersPerDegreeDay", 1.5f, new ConfigDescription("Custom preset: metres melted per degree above 0 C per simulated day.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.001f, 1000f), Array.Empty<object>()));
			DriftStrength = c.Bind<float>("6. Grains", "DriftStrength", 1f, new ConfigDescription("Wind drifts: snow piles up behind walls and rocks (0 = off, 2 = extreme), open ground gets a bit less.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 5f), Array.Empty<object>()));
			WindDirectionDeg = c.Bind<float>("6. Grains", "WindDirectionDeg", 45f, new ConfigDescription("Direction the wind blows TOWARDS, degrees in the world X/Z plane.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 360f), Array.Empty<object>()));
			ShelterMeters = c.Bind<float>("6. Grains", "ShelterMeters", 8f, new ConfigDescription("How far upwind an obstacle can be and still make a drift behind it.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 40f), Array.Empty<object>()));
			TrenchRadius = c.Bind<float>("6. Grains", "TrenchRadius", 3f, new ConfigDescription("Snow is shown low around the player (like a dug path) because the mod cannot lift the player onto the snow. 0 = off (then deep snow can cover the camera).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 20f), Array.Empty<object>()));
			TrenchDepth = c.Bind<float>("6. Grains", "TrenchDepth", 0.6f, new ConfigDescription("Snow depth shown right next to the player, metres.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 3f), Array.Empty<object>()));
			SkipMoving = c.Bind<bool>("6. Grains", "SkipMovingObjects", true, "Do not put snow on colliders that belong to a Rigidbody (people, animals, logs, items).");
			RayStartHeight = c.Bind<float>("6. Grains", "RayStartHeight", 25f, new ConfigDescription("Flakes start this many metres above the camera.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(5f, 100f), Array.Empty<object>()));
			WaterLayers = c.Bind<string>("6. Grains", "WaterLayers", "Water", "Comma-separated Unity layer NAMES of water volumes. Empty = do not look for water by layer. Whether the game uses this layer is not confirmed.");
			WaterKeywords = c.Bind<string>("6. Grains", "WaterKeywords", "water,ocean,lake,river,swamp,pond", "A collider/layer/material whose name contains one of these counts as water.");
			IceKeywords = c.Bind<string>("6. Grains", "IceKeywords", "ice,frozen", "Names containing these count as solid ice (snow always settles).");
			IgnoreKeywords = c.Bind<string>("6. Grains", "IgnoreKeywords", "player,cannibal,mutant,enemy,creature,animal", "Names containing these never get snow.");
			SnowOnLiquidWater = c.Bind<bool>("6. Grains", "SnowOnLiquidWater", false, "If false, flakes melt on open water and settle only after the water has frozen.");
			WaterFreezeDays = c.Bind<float>("6. Grains", "WaterFreezeDays", 2f, new ConfigDescription("Days of hard frost for water to freeze solid (mod's own model).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 30f), Array.Empty<object>()));
			WaterThawDays = c.Bind<float>("6. Grains", "WaterThawDays", 1.5f, new ConfigDescription("Days of thaw for ice to melt completely.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 30f), Array.Empty<object>()));
			WaterFreezeThreshold = c.Bind<float>("6. Grains", "WaterFreezeThreshold", 0.6f, new ConfigDescription("Ice level (0..1) from which snow settles on water.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.05f, 1f), Array.Empty<object>()));
			GrainColor = c.Bind<string>("6. Grains", "Color", "0.95,0.97,1.0", "Grain colour as r,g,b (0..1).");
			GrainShader = c.Bind<string>("6. Grains", "Shader", "Auto", "Shader for the grains. Auto tries HDRP/Lit, URP/Lit, Standard, then unlit ones. Or type an exact shader name.");
			UseInstancing = c.Bind<bool>("6. Grains", "UseInstancing", true, "GPU instancing (few draw calls). If the grains are invisible or pink, set false.");
			IgnoreSceneKeywords = c.Bind<string>("6. Grains", "IgnoreSceneKeywords", "title", "No grains while the active scene name contains one of these. Empty = always on. The scene name is written to the log.");
			DebugForceSnow = c.Bind<bool>("6. Grains", "DebugForceSnow", false, "Test mode: constant heavy snowfall at -5 C, ignoring the mod's weather.");
			DebugForceKey = c.Bind<string>("6. Grains", "DebugForceKey", "F9", "Toggles test snowfall in game.");
			ClearKey = c.Bind<string>("6. Grains", "ClearKey", "F10", "Removes all grains.");
			StatsEverySeconds = c.Bind<float>("6. Grains", "StatsEverySeconds", 30f, new ConfigDescription("Write grain statistics to the log every N seconds. 0 = off.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 600f), Array.Empty<object>()));
		}
	}
}