Decompiled source of ShadowTrim v0.1.0

ShadowTrim.dll

Decompiled 3 days ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using Unity.Profiling;
using UnityEngine;
using UnityEngine.Rendering;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: AssemblyVersion("0.0.0.0")]
namespace ShadowTrim;

[BepInPlugin("j1ga.shadowtrim", "ShadowTrim", "0.1.0")]
public class ShadowTrimPlugin : BaseUnityPlugin
{
	private sealed class BenchSide
	{
		public double Frames;

		public double Seconds;

		public double Casters;

		public double CasterFrames;
	}

	private sealed class LayerCount
	{
		public int Casting;

		public int Trimmed;
	}

	private sealed class Changed
	{
		public Renderer R;

		public ShadowCastingMode Mode;
	}

	[HarmonyPatch(typeof(Piece), "Awake")]
	private static class Piece_Awake_Patch
	{
		private static void Postfix(Piece __instance)
		{
			ShadowTrimPlugin instance = Instance;
			if ((Object)(object)instance != (Object)null && instance._flatOn && (Object)(object)__instance != (Object)null)
			{
				instance._queue.Add(__instance);
			}
		}
	}

	[HarmonyPatch(typeof(Piece), "OnDestroy")]
	private static class Piece_OnDestroy_Patch
	{
		private static void Postfix(Piece __instance)
		{
			ShadowTrimPlugin instance = Instance;
			if ((Object)(object)instance != (Object)null && (Object)(object)__instance != (Object)null)
			{
				instance._flatPieces.Remove(((Object)__instance).GetInstanceID());
			}
		}
	}

	private sealed class PresetValues
	{
		public float Buildings;

		public float Decor;

		public float Small;

		public float Items;

		public string Other;
	}

	private const float BenchPhase = 5f;

	private const float BenchSettle = 1f;

	private const int BenchPhases = 8;

	private const string SecGeneral = "01 General";

	private const string SecDistance = "02 Shadow distance by layer";

	private const string SecFlat = "03 Flat on the ground";

	public const string Guid = "j1ga.shadowtrim";

	public const string Name = "ShadowTrim";

	public const string Version = "0.1.0";

	private const double FlatBudgetMs = 1.0;

	private const string PresetNormal = "Normal";

	private const string PresetLow = "Low";

	private const string PresetCustom = "Custom";

	private bool _benching;

	private ConfigEntry<bool> _cfgEnabled;

	private ConfigEntry<float> _cfgBuildings;

	private ConfigEntry<float> _cfgDecor;

	private ConfigEntry<float> _cfgSmall;

	private ConfigEntry<float> _cfgItems;

	private ConfigEntry<string> _cfgOther;

	private ConfigEntry<bool> _cfgFlat;

	private ConfigEntry<float> _cfgFlatGap;

	private ConfigEntry<float> _cfgFlatTop;

	private readonly HashSet<string> _warned = new HashSet<string>();

	internal static ShadowTrimPlugin Instance;

	private Harmony _harmony;

	private readonly HashSet<string> _failed = new HashSet<string>();

	private readonly List<Piece> _queue = new List<Piece>();

	private readonly List<Piece> _later = new List<Piece>();

	private readonly List<Changed> _changed = new List<Changed>();

	private readonly HashSet<int> _flatPieces = new HashSet<int>();

	private int _queueCursor;

	private float _nextLater;

	private bool _flatDirty = true;

	private bool _flatOn;

	private readonly Stopwatch _sw = new Stopwatch();

	private static readonly List<MeshRenderer> s_renderers = new List<MeshRenderer>();

	private float _nextPrune;

	private Light _sun;

	private float[] _applied;

	private bool _layersDirty = true;

	private float _nextSunCheck;

	private static readonly PresetValues s_normal = new PresetValues
	{
		Buildings = 60f,
		Decor = 40f,
		Small = 30f,
		Items = 25f,
		Other = ""
	};

	private static readonly PresetValues s_low = new PresetValues
	{
		Buildings = 35f,
		Decor = 25f,
		Small = 20f,
		Items = 15f,
		Other = "static_solid=60,Default=60"
	};

	private ConfigEntry<string> _cfgPreset;

	private bool _applyingPreset;

	private bool FlatWanted
	{
		get
		{
			if (Active)
			{
				return _cfgFlat.Value;
			}
			return false;
		}
	}

	private bool Active
	{
		get
		{
			if (_cfgEnabled != null)
			{
				return _cfgEnabled.Value;
			}
			return false;
		}
	}

	private Light Sun
	{
		get
		{
			EnvMan instance = EnvMan.instance;
			if (!((Object)(object)instance != (Object)null))
			{
				return null;
			}
			return instance.m_dirLight;
		}
	}

	private void StartBench(ConsoleEventArgs args)
	{
		if (_benching)
		{
			Say(args, "A benchmark is already running.");
			return;
		}
		if ((Object)(object)Sun == (Object)null)
		{
			Say(args, "No sun here (indoors or in a dungeon); nothing to measure.");
			return;
		}
		Terminal term = args?.Context;
		Say(args, "ShadowTrim benchmark: " + 40f.ToString("0") + " s, switching every " + 5f.ToString("0") + " s. Stand still and keep the camera where it is.");
		((MonoBehaviour)this).StartCoroutine(Bench(term));
	}

	private IEnumerator Bench(Terminal term)
	{
		_benching = true;
		BenchSide on = new BenchSide();
		BenchSide off = new BenchSide();
		ProfilerRecorder casters = default(ProfilerRecorder);
		try
		{
			casters = ProfilerRecorder.StartNew(ProfilerCategory.Render, "Shadow Casters Count", 1, (ProfilerRecorderOptions)24);
		}
		catch (Exception)
		{
		}
		float[] trimmed = Distances();
		try
		{
			for (int phase = 0; phase < 8; phase++)
			{
				bool trim = phase % 2 == 1;
				ApplyBench(trim, trimmed);
				BenchSide side = (trim ? on : off);
				float t0 = Time.unscaledTime;
				while (Time.unscaledTime - t0 < 5f)
				{
					yield return null;
					if (!(Time.unscaledTime - t0 < 1f))
					{
						side.Frames += 1.0;
						side.Seconds += Time.unscaledDeltaTime;
						if (((ProfilerRecorder)(ref casters)).Valid)
						{
							side.Casters += ((ProfilerRecorder)(ref casters)).LastValue;
							side.CasterFrames += 1.0;
						}
					}
				}
			}
		}
		finally
		{
			if (((ProfilerRecorder)(ref casters)).Valid)
			{
				((ProfilerRecorder)(ref casters)).Dispose();
			}
			FlipFlat(_flatOn);
			_layersDirty = true;
			_sun = null;
			_benching = false;
		}
		string r = "ShadowTrim benchmark:\n" + BenchLine("game's shadows", off) + "\n" + BenchLine("ShadowTrim", on);
		if (off.Frames > 0.0 && on.Frames > 0.0)
		{
			double num = off.Seconds / off.Frames * 1000.0;
			double num2 = on.Seconds / on.Frames * 1000.0;
			string text = r;
			r = text + "\nFrame time " + ((num - num2 >= 0.0) ? "-" : "+") + Math.Abs(num - num2).ToString("0.00") + " ms (" + ((num - num2) / num * 100.0).ToString("0.0") + "% faster)";
		}
		Log(r.Replace("\n", " | "));
		if ((Object)(object)term != (Object)null)
		{
			term.AddString(r);
		}
	}

	private static string BenchLine(string name, BenchSide s)
	{
		if (s.Frames <= 0.0)
		{
			return "  " + name + ": no frames";
		}
		double num = s.Seconds / s.Frames * 1000.0;
		string text = "  " + name + ": " + num.ToString("0.00") + " ms (" + (1000.0 / num).ToString("0") + " fps)";
		if (s.CasterFrames > 0.0)
		{
			text = text + ", " + (s.Casters / s.CasterFrames).ToString("0") + " shadow casters";
		}
		return text;
	}

	private void ApplyBench(bool trim, float[] trimmed)
	{
		Light sun = Sun;
		if ((Object)(object)sun != (Object)null)
		{
			if (trim && trimmed != null)
			{
				sun.layerShadowCullDistances = trimmed;
			}
			else
			{
				Clear(sun);
			}
		}
		_sun = sun;
		_applied = (trim ? trimmed : null);
		FlipFlat(trim);
	}

	private void RegisterCommands()
	{
		//IL_0011: Unknown result type (might be due to invalid IL or missing references)
		//IL_0025: Expected O, but got Unknown
		//IL_0020: Unknown result type (might be due to invalid IL or missing references)
		new ConsoleCommand("shadowtrim", "ShadowTrim. 'shadowtrim' switches it on and off to compare; 'shadowtrim status' counts the shadows around you; 'shadowtrim bench' measures the gain where you stand", (ConsoleEvent)delegate(ConsoleEventArgs args)
		{
			RunCommand(args);
		}, false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
	}

	private static void Say(ConsoleEventArgs args, string text)
	{
		if (args != null && (Object)(object)args.Context != (Object)null)
		{
			args.Context.AddString(text);
		}
	}

	private void RunCommand(ConsoleEventArgs args)
	{
		try
		{
			string text = ((args.Length > 1) ? args[1].ToLowerInvariant() : "");
			switch (text)
			{
			case "status":
				Say(args, Status());
				break;
			case "bench":
				StartBench(args);
				break;
			case "on":
			case "off":
			case "":
			{
				bool flag = ((text == "") ? (!_cfgEnabled.Value) : (text == "on"));
				_cfgEnabled.Value = flag;
				Say(args, "ShadowTrim " + (flag ? "on" : "off") + ". 'shadowtrim status' counts the shadows around you.");
				break;
			}
			default:
				Say(args, "shadowtrim [on|off|status|bench]");
				break;
			}
		}
		catch (Exception ex)
		{
			Say(args, "shadowtrim failed: " + ex.Message);
		}
	}

	private string Status()
	{
		//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
		//IL_0123: Unknown result type (might be due to invalid IL or missing references)
		//IL_012d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0130: Unknown result type (might be due to invalid IL or missing references)
		//IL_0135: 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)
		StringBuilder stringBuilder = new StringBuilder();
		Light sun = Sun;
		stringBuilder.Append("ShadowTrim ").Append("0.1.0").Append(Active ? ": on" : ": off")
			.Append(", the game's shadow distance ")
			.Append(QualitySettings.shadowDistance.ToString("0"))
			.Append(" m")
			.Append(((Object)(object)sun == (Object)null) ? ", no sun here" : "")
			.Append('\n');
		Camera main = Camera.main;
		if ((Object)(object)main == (Object)null)
		{
			stringBuilder.Append("No camera.");
			return stringBuilder.ToString();
		}
		Vector3 position = ((Component)main).transform.position;
		float shadowDistance = QualitySettings.shadowDistance;
		Dictionary<int, LayerCount> dictionary = new Dictionary<int, LayerCount>();
		int num = 0;
		int num2 = 0;
		Renderer[] array = Object.FindObjectsByType<Renderer>((FindObjectsSortMode)0);
		foreach (Renderer val in array)
		{
			if ((Object)(object)val == (Object)null || !val.enabled || !((Component)val).gameObject.activeInHierarchy || (!(val is MeshRenderer) && !(val is SkinnedMeshRenderer)) || (int)val.shadowCastingMode == 0)
			{
				continue;
			}
			Bounds bounds = val.bounds;
			float num3 = Vector3.Distance(position, ((Bounds)(ref bounds)).center);
			if (!(num3 > shadowDistance))
			{
				int layer = ((Component)val).gameObject.layer;
				if (!dictionary.TryGetValue(layer, out var value))
				{
					value = (dictionary[layer] = new LayerCount());
				}
				value.Casting++;
				num++;
				if (Active && num3 > EffectiveDistance(layer))
				{
					value.Trimmed++;
					num2++;
				}
			}
		}
		stringBuilder.Append("Shadow casters within ").Append(shadowDistance.ToString("0")).Append(" m: ")
			.Append(num);
		if (Active)
		{
			stringBuilder.Append(", of them past their layer's distance (no shadow): ").Append(num2);
		}
		stringBuilder.Append('\n');
		List<KeyValuePair<int, LayerCount>> list = new List<KeyValuePair<int, LayerCount>>(dictionary);
		list.Sort((KeyValuePair<int, LayerCount> a, KeyValuePair<int, LayerCount> b) => b.Value.Casting.CompareTo(a.Value.Casting));
		foreach (KeyValuePair<int, LayerCount> item in list)
		{
			string value2 = LayerMask.LayerToName(item.Key);
			if (string.IsNullOrEmpty(value2))
			{
				value2 = "layer " + item.Key;
			}
			stringBuilder.Append("  ").Append(value2).Append(": ")
				.Append(item.Value.Casting);
			float num4 = EffectiveDistance(item.Key);
			if (Active && num4 < shadowDistance)
			{
				stringBuilder.Append(", shadow to ").Append(num4.ToString("0")).Append(" m, ")
					.Append(item.Value.Trimmed)
					.Append(" past it");
			}
			stringBuilder.Append('\n');
		}
		FlatCounts(out var pieces, out var renderers);
		stringBuilder.Append("Flat on the ground: ").Append(_flatOn ? (pieces + " pieces (" + renderers + " renderers) cast no shadow") : "off");
		if (_flatOn && _queue.Count - _queueCursor + _later.Count > 0)
		{
			stringBuilder.Append(", ").Append(_queue.Count - _queueCursor + _later.Count).Append(" still to look at");
		}
		return stringBuilder.ToString();
	}

	private void BindConfig()
	{
		_cfgEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("01 General", "Enabled", true, "Off: every shadow as in the game. The console command 'shadowtrim' switches it too, to compare on the spot.");
		_cfgBuildings = ((BaseUnityPlugin)this).Config.Bind<float>("02 Shadow distance by layer", "Buildings", 60f, "Building pieces (layer 'piece'): walls, floors, roofs, beams, stations. Metres from the camera; 0 = the game's own shadow distance (Graphics settings). The object is still drawn and still darkened by other shadows - only its own shadow ends there.");
		_cfgDecor = ((BaseUnityPlugin)this).Config.Bind<float>("02 Shadow distance by layer", "Decor", 40f, "Pieces you can walk through (layer 'piece_nonsolid'): banners, rugs, item stands, small decor. Metres from the camera; 0 = the game's own shadow distance (Graphics settings). The object is still drawn and still darkened by other shadows - only its own shadow ends there.");
		_cfgSmall = ((BaseUnityPlugin)this).Config.Bind<float>("02 Shadow distance by layer", "SmallObjects", 30f, "Small world objects (layer 'Default_small'): bushes, pickables, small stones and branches. Metres from the camera; 0 = the game's own shadow distance (Graphics settings). The object is still drawn and still darkened by other shadows - only its own shadow ends there.");
		_cfgItems = ((BaseUnityPlugin)this).Config.Bind<float>("02 Shadow distance by layer", "Items", 25f, "Dropped items and floating logs (layer 'item'). Metres from the camera; 0 = the game's own shadow distance (Graphics settings). The object is still drawn and still darkened by other shadows - only its own shadow ends there.");
		_cfgOther = ((BaseUnityPlugin)this).Config.Bind<string>("02 Shadow distance by layer", "OtherLayers", "", "Any other layer, as layer=metres separated by commas, e.g. static_solid=80,character=50. 'shadowtrim status' lists the layers that cast shadows around you and how many objects each has.");
		_cfgFlat = ((BaseUnityPlugin)this).Config.Bind<bool>("03 Flat on the ground", "Enabled", true, "Pieces lying flat on the ground (floors, path stones, rugs) stop casting a shadow: it falls into the ground right under them and cannot be seen anyway.");
		_cfgFlatGap = ((BaseUnityPlugin)this).Config.Bind<float>("03 Flat on the ground", "MaxGapBelow", 0.3f, "How far above the lowest ground under it a piece's underside may be and still count as lying on the ground, in metres. A floor over a dip or on posts is left alone.");
		_cfgFlatTop = ((BaseUnityPlugin)this).Config.Bind<float>("03 Flat on the ground", "MaxHeightAbove", 0.6f, "How far above the highest ground under it a piece's top may reach, in metres. Taller pieces cast a visible shadow beside themselves and are left alone.");
		_cfgEnabled.SettingChanged += delegate
		{
			_layersDirty = true;
			_flatDirty = true;
		};
		_cfgBuildings.SettingChanged += delegate
		{
			_layersDirty = true;
		};
		_cfgDecor.SettingChanged += delegate
		{
			_layersDirty = true;
		};
		_cfgSmall.SettingChanged += delegate
		{
			_layersDirty = true;
		};
		_cfgItems.SettingChanged += delegate
		{
			_layersDirty = true;
		};
		_cfgOther.SettingChanged += delegate
		{
			_layersDirty = true;
		};
		_cfgFlat.SettingChanged += delegate
		{
			_flatDirty = true;
		};
		_cfgFlatGap.SettingChanged += delegate
		{
			_flatDirty = true;
		};
		_cfgFlatTop.SettingChanged += delegate
		{
			_flatDirty = true;
		};
		BindPreset();
	}

	private Dictionary<string, float> LayerSettings()
	{
		Dictionary<string, float> dictionary = new Dictionary<string, float>(StringComparer.OrdinalIgnoreCase);
		dictionary["piece"] = _cfgBuildings.Value;
		dictionary["piece_nonsolid"] = _cfgDecor.Value;
		dictionary["Default_small"] = _cfgSmall.Value;
		dictionary["item"] = _cfgItems.Value;
		string[] array = (_cfgOther.Value ?? "").Split(',', ';');
		foreach (string text in array)
		{
			string text2 = text.Trim();
			if (text2.Length != 0)
			{
				int num = text2.IndexOf('=');
				if (num <= 0 || !float.TryParse(text2.Substring(num + 1).Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out var result))
				{
					WarnOnce("other:" + text2, "OtherLayers: '" + text2 + "' is not layer=metres; ignored.");
				}
				else
				{
					dictionary[text2.Substring(0, num).Trim()] = result;
				}
			}
		}
		return dictionary;
	}

	private void WarnOnce(string key, string text)
	{
		if (_warned.Add(key))
		{
			Warn(text);
		}
	}

	private void Awake()
	{
		//IL_0012: Unknown result type (might be due to invalid IL or missing references)
		//IL_001c: Expected O, but got Unknown
		Instance = this;
		BindConfig();
		_harmony = new Harmony("j1ga.shadowtrim");
		_harmony.PatchAll(typeof(ShadowTrimPlugin).Assembly);
		RegisterCommands();
		((BaseUnityPlugin)this).Logger.LogInfo((object)"ShadowTrim 0.1.0 loaded.");
	}

	private void OnDestroy()
	{
		try
		{
			RestoreLayers();
			RestoreFlat();
			if (_harmony != null)
			{
				_harmony.UnpatchSelf();
			}
		}
		catch (Exception ex)
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)("OnDestroy: " + ex.Message));
		}
	}

	private void Update()
	{
		try
		{
			UpdateLayers();
		}
		catch (Exception e)
		{
			Fail("layers", e);
		}
		try
		{
			UpdateFlat();
		}
		catch (Exception e2)
		{
			Fail("flat pieces", e2);
		}
	}

	private void Fail(string where, Exception e)
	{
		if (_failed.Add(where))
		{
			((BaseUnityPlugin)this).Logger.LogError((object)(where + ": " + e));
		}
	}

	internal void Log(string s)
	{
		((BaseUnityPlugin)this).Logger.LogInfo((object)s);
	}

	internal void Warn(string s)
	{
		((BaseUnityPlugin)this).Logger.LogWarning((object)s);
	}

	private void UpdateFlat()
	{
		if (Time.unscaledTime >= _nextPrune)
		{
			_nextPrune = Time.unscaledTime + 30f;
			_changed.RemoveAll((Changed ch) => (Object)(object)ch.R == (Object)null);
		}
		if (_flatDirty)
		{
			_flatDirty = false;
			RestoreFlat();
			_flatOn = FlatWanted;
			if (_flatOn)
			{
				QueueAll();
			}
		}
		if (!_flatOn)
		{
			return;
		}
		if (_later.Count > 0 && Time.unscaledTime >= _nextLater)
		{
			_nextLater = Time.unscaledTime + 2f;
			_queue.AddRange(_later);
			_later.Clear();
		}
		if (_queueCursor >= _queue.Count)
		{
			if (_queue.Count > 0)
			{
				_queue.Clear();
				_queueCursor = 0;
			}
			return;
		}
		_sw.Reset();
		_sw.Start();
		while (_queueCursor < _queue.Count && _sw.Elapsed.TotalMilliseconds < 1.0)
		{
			Look(_queue[_queueCursor++]);
		}
		if (_queueCursor >= _queue.Count)
		{
			_queue.Clear();
			_queueCursor = 0;
		}
	}

	private void QueueAll()
	{
		FieldInfo fieldInfo = AccessTools.Field(typeof(Piece), "s_allPieces");
		List<Piece> list = ((fieldInfo != null) ? (fieldInfo.GetValue(null) as List<Piece>) : null);
		if (list != null)
		{
			_queue.AddRange(list);
		}
	}

	private void Look(Piece piece)
	{
		//IL_0051: 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)
		//IL_00d9: Unknown result type (might be due to invalid IL or missing references)
		//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
		//IL_008b: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
		//IL_0097: 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_01b3: Unknown result type (might be due to invalid IL or missing references)
		//IL_01cf: Unknown result type (might be due to invalid IL or missing references)
		//IL_0209: Unknown result type (might be due to invalid IL or missing references)
		//IL_0212: Unknown result type (might be due to invalid IL or missing references)
		//IL_0218: Invalid comparison between Unknown and I4
		//IL_0234: Unknown result type (might be due to invalid IL or missing references)
		//IL_0239: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)piece == (Object)null)
		{
			return;
		}
		ZNetView component = ((Component)piece).GetComponent<ZNetView>();
		if ((Object)(object)component == (Object)null || !component.IsValid() || _flatPieces.Contains(((Object)piece).GetInstanceID()))
		{
			return;
		}
		s_renderers.Clear();
		((Component)piece).GetComponentsInChildren<MeshRenderer>(true, s_renderers);
		bool flag = false;
		Bounds val = default(Bounds);
		for (int i = 0; i < s_renderers.Count; i++)
		{
			MeshRenderer val2 = s_renderers[i];
			if (!((Object)(object)val2 == (Object)null) && ((Renderer)val2).enabled && ((Component)val2).gameObject.activeInHierarchy && (int)((Renderer)val2).shadowCastingMode != 0)
			{
				if (!flag)
				{
					val = ((Renderer)val2).bounds;
					flag = true;
				}
				else
				{
					((Bounds)(ref val)).Encapsulate(((Renderer)val2).bounds);
				}
			}
		}
		if (!flag)
		{
			return;
		}
		float gMin = float.MaxValue;
		float gMax = float.MinValue;
		Vector3 center = ((Bounds)(ref val)).center;
		Vector3 extents = ((Bounds)(ref val)).extents;
		if (!Ground(center.x, center.z, ref gMin, ref gMax) || !Ground(center.x - extents.x, center.z - extents.z, ref gMin, ref gMax) || !Ground(center.x + extents.x, center.z - extents.z, ref gMin, ref gMax) || !Ground(center.x - extents.x, center.z + extents.z, ref gMin, ref gMax) || !Ground(center.x + extents.x, center.z + extents.z, ref gMin, ref gMax))
		{
			_later.Add(piece);
		}
		else
		{
			if (((Bounds)(ref val)).min.y - gMin > _cfgFlatGap.Value || ((Bounds)(ref val)).max.y - gMax > _cfgFlatTop.Value)
			{
				return;
			}
			for (int j = 0; j < s_renderers.Count; j++)
			{
				MeshRenderer val3 = s_renderers[j];
				if (!((Object)(object)val3 == (Object)null) && (int)((Renderer)val3).shadowCastingMode != 0 && (int)((Renderer)val3).shadowCastingMode != 3)
				{
					_changed.Add(new Changed
					{
						R = (Renderer)(object)val3,
						Mode = ((Renderer)val3).shadowCastingMode
					});
					((Renderer)val3).shadowCastingMode = (ShadowCastingMode)0;
				}
			}
			_flatPieces.Add(((Object)piece).GetInstanceID());
		}
	}

	private static bool Ground(float x, float z, ref float gMin, ref float gMax)
	{
		//IL_0007: Unknown result type (might be due to invalid IL or missing references)
		float num = default(float);
		if (!Heightmap.GetHeight(new Vector3(x, 0f, z), ref num))
		{
			return false;
		}
		if (num < gMin)
		{
			gMin = num;
		}
		if (num > gMax)
		{
			gMax = num;
		}
		return true;
	}

	private void RestoreFlat()
	{
		//IL_0025: Unknown result type (might be due to invalid IL or missing references)
		//IL_0033: Unknown result type (might be due to invalid IL or missing references)
		for (int i = 0; i < _changed.Count; i++)
		{
			Changed changed = _changed[i];
			if ((Object)(object)changed.R != (Object)null && (int)changed.R.shadowCastingMode == 0)
			{
				changed.R.shadowCastingMode = changed.Mode;
			}
		}
		_changed.Clear();
		_flatPieces.Clear();
		_queue.Clear();
		_later.Clear();
		_queueCursor = 0;
	}

	private void FlipFlat(bool off)
	{
		//IL_0029: Unknown result type (might be due to invalid IL or missing references)
		for (int i = 0; i < _changed.Count; i++)
		{
			Changed changed = _changed[i];
			if ((Object)(object)changed.R != (Object)null)
			{
				changed.R.shadowCastingMode = (ShadowCastingMode)((!off) ? ((int)changed.Mode) : 0);
			}
		}
	}

	private void FlatCounts(out int pieces, out int renderers)
	{
		_changed.RemoveAll((Changed ch) => (Object)(object)ch.R == (Object)null);
		renderers = _changed.Count;
		HashSet<int> hashSet = new HashSet<int>();
		for (int num = 0; num < _changed.Count; num++)
		{
			Piece componentInParent = ((Component)_changed[num].R).GetComponentInParent<Piece>();
			if ((Object)(object)componentInParent != (Object)null)
			{
				hashSet.Add(((Object)componentInParent).GetInstanceID());
			}
		}
		pieces = hashSet.Count;
	}

	private void UpdateLayers()
	{
		if (!_layersDirty && Time.unscaledTime < _nextSunCheck)
		{
			return;
		}
		_nextSunCheck = Time.unscaledTime + 0.5f;
		Light sun = Sun;
		if (!_layersDirty && (Object)(object)sun == (Object)(object)_sun)
		{
			return;
		}
		_layersDirty = false;
		if ((Object)(object)_sun != (Object)null && (Object)(object)_sun != (Object)(object)sun)
		{
			Clear(_sun);
		}
		_sun = sun;
		if ((Object)(object)sun == (Object)null)
		{
			_applied = null;
			return;
		}
		float[] array = (Active ? Distances() : null);
		if (array == null)
		{
			Clear(sun);
			_applied = null;
		}
		else
		{
			sun.layerShadowCullDistances = array;
			_applied = array;
		}
	}

	private float[] Distances()
	{
		float[] array = new float[32];
		bool flag = false;
		foreach (KeyValuePair<string, float> item in LayerSettings())
		{
			int num = LayerMask.NameToLayer(item.Key);
			if (num < 0)
			{
				WarnOnce("layer:" + item.Key, "There is no layer '" + item.Key + "' in this game; its distance is ignored.");
				continue;
			}
			float num2 = item.Value;
			if (num2 < 0f || float.IsNaN(num2))
			{
				num2 = 0f;
			}
			array[num] = num2;
			if (num2 > 0f)
			{
				flag = true;
			}
		}
		if (!flag)
		{
			return null;
		}
		return array;
	}

	private static void Clear(Light l)
	{
		if ((Object)(object)l != (Object)null)
		{
			l.layerShadowCullDistances = new float[32];
		}
	}

	private void RestoreLayers()
	{
		if ((Object)(object)_sun != (Object)null)
		{
			Clear(_sun);
		}
		_sun = null;
		_applied = null;
	}

	private float EffectiveDistance(int layer)
	{
		float shadowDistance = QualitySettings.shadowDistance;
		if (_applied == null || layer < 0 || layer >= _applied.Length || _applied[layer] <= 0f)
		{
			return shadowDistance;
		}
		return Mathf.Min(shadowDistance, _applied[layer]);
	}

	private void BindPreset()
	{
		//IL_0046: Unknown result type (might be due to invalid IL or missing references)
		//IL_0050: Expected O, but got Unknown
		_cfgPreset = ((BaseUnityPlugin)this).Config.Bind<string>("01 General", "Preset", "Normal", new ConfigDescription("Normal: the defaults (buildings 60 m, decor 40, small objects 30, items 25). Low: for weak PCs (buildings 35 m, decor 25, small objects 20, items 15, rocks and other objects 60) - shadows appear closer, there are fewer of them. Custom: the distances below as you set them; changing any of them switches to Custom by itself.", (AcceptableValueBase)(object)new AcceptableValueList<string>(new string[3] { "Normal", "Low", "Custom" }), new object[0]));
		PresetValues presetValues = PresetFor(_cfgPreset.Value);
		if (presetValues != null && !Matches(presetValues))
		{
			_cfgPreset.Value = "Custom";
		}
		_cfgPreset.SettingChanged += delegate
		{
			ApplyPreset();
		};
		EventHandler eventHandler = delegate
		{
			if (!_applyingPreset)
			{
				PresetValues presetValues2 = PresetFor(_cfgPreset.Value);
				if (presetValues2 != null && !Matches(presetValues2))
				{
					_cfgPreset.Value = "Custom";
				}
			}
		};
		_cfgBuildings.SettingChanged += eventHandler;
		_cfgDecor.SettingChanged += eventHandler;
		_cfgSmall.SettingChanged += eventHandler;
		_cfgItems.SettingChanged += eventHandler;
		_cfgOther.SettingChanged += eventHandler;
	}

	private static PresetValues PresetFor(string name)
	{
		if (string.Equals(name, "Normal", StringComparison.OrdinalIgnoreCase))
		{
			return s_normal;
		}
		if (string.Equals(name, "Low", StringComparison.OrdinalIgnoreCase))
		{
			return s_low;
		}
		return null;
	}

	private bool Matches(PresetValues p)
	{
		if (_cfgBuildings.Value == p.Buildings && _cfgDecor.Value == p.Decor && _cfgSmall.Value == p.Small && _cfgItems.Value == p.Items)
		{
			return string.Equals((_cfgOther.Value ?? "").Replace(" ", ""), p.Other, StringComparison.OrdinalIgnoreCase);
		}
		return false;
	}

	private void ApplyPreset()
	{
		PresetValues presetValues = PresetFor(_cfgPreset.Value);
		if (presetValues != null && !Matches(presetValues))
		{
			_applyingPreset = true;
			try
			{
				_cfgBuildings.Value = presetValues.Buildings;
				_cfgDecor.Value = presetValues.Decor;
				_cfgSmall.Value = presetValues.Small;
				_cfgItems.Value = presetValues.Items;
				_cfgOther.Value = presetValues.Other;
			}
			finally
			{
				_applyingPreset = false;
			}
			_layersDirty = true;
		}
	}
}