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>()));
}
}
}