Decompiled source of ValheimVisuals v1.2.3

plugins/ValheimVisuals.dll

Decompiled 2 days ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;
using UnityEngine.PostProcessing;
using UnityStandardAssets.ImageEffects;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("ValheimVisuals")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.2.3.0")]
[assembly: AssemblyInformationalVersion("1.2.3")]
[assembly: AssemblyProduct("ValheimVisuals")]
[assembly: AssemblyTitle("ValheimVisuals")]
[assembly: AssemblyVersion("1.2.3.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 ValheimVisuals
{
	internal class VisualsConfig
	{
		internal class ShadowsSection
		{
			public readonly ConfigEntry<bool> Enabled;

			public readonly ConfigEntry<bool> UseDistance;

			public readonly ConfigEntry<float> Distance;

			public readonly ConfigEntry<bool> UseCascades;

			public readonly ConfigEntry<int> Cascades;

			public readonly ConfigEntry<bool> UseCascadeSplit;

			public readonly ConfigEntry<float> CascadeSplit1;

			public readonly ConfigEntry<float> CascadeSplit2;

			public readonly ConfigEntry<float> CascadeSplit3;

			public readonly ConfigEntry<bool> UseResolution;

			public readonly ConfigEntry<ShadowResolution> Resolution;

			public readonly ConfigEntry<bool> UseProjection;

			public readonly ConfigEntry<ShadowProjection> Projection;

			public readonly ConfigEntry<bool> UseNearPlaneOffset;

			public readonly ConfigEntry<float> NearPlaneOffset;

			public readonly ConfigEntry<bool> UsePixelLightCount;

			public readonly ConfigEntry<int> PixelLightCount;

			public ShadowsSection(ConfigFile cfg, PresetValues p)
			{
				//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
				//IL_0110: Unknown result type (might be due to invalid IL or missing references)
				//IL_0140: Unknown result type (might be due to invalid IL or missing references)
				//IL_0191: Unknown result type (might be due to invalid IL or missing references)
				//IL_01d3: Unknown result type (might be due to invalid IL or missing references)
				Enabled = Bind(cfg, "Shadows", "Enabled", def: true, "Whether this mod touches shadows at all. Off hands every value below back to the game's own graphics settings.");
				UseDistance = Bind(cfg, "Shadows", "UseDistance", p.UseShadowDistance, "First thing to turn off if distant ground and trees look flat and unshaded. The game uses 80/120/150 m for its three shadow steps; stretching that to 300 spreads the same shadow map over four times the area, and shadows too coarse to see read as no shadows at all.");
				Distance = Bind(cfg, "Shadows", "Distance", p.ShadowDistance, "How far shadows are drawn, in metres. Vanilla tops out at 150.", 20f, 600f);
				UseCascades = Bind(cfg, "Shadows", "UseCascades", p.UseShadowCascades, "Off leaves the cascade count the game chose (2, 3 or 4 by shadow quality).");
				Cascades = Bind(cfg, "Shadows", "Cascades", p.ShadowCascades, "Shadow map cascades. More cascades spend the map more evenly over distance; only 1, 2 and 4 are meaningful to Unity.", 1, 4);
				UseCascadeSplit = Bind(cfg, "Shadows", "UseCascadeSplit", p.UseCascadeSplit, "Off restores Unity's own even-ish split. The game never writes this, so the mod owns it outright — and with Distance at 300 the front-loaded split below is what keeps near shadows sharp, so turning one off without the other changes the picture twice.");
				CascadeSplit1 = Bind(cfg, "Shadows", "CascadeSplit1", p.Cascade4Split.x, "End of the first cascade as a fraction of Distance. 0.04 of 300 m = 12 m.", 0.01f, 0.5f);
				CascadeSplit2 = Bind(cfg, "Shadows", "CascadeSplit2", p.Cascade4Split.y, "End of the second cascade. 0.12 of 300 m = 36 m.", 0.02f, 0.7f);
				CascadeSplit3 = Bind(cfg, "Shadows", "CascadeSplit3", p.Cascade4Split.z, "End of the third cascade; the fourth covers the rest. 0.32 of 300 m = 96 m, leaving 204 m for the last one — raise this if the middle distance is where things look unshaded.", 0.05f, 0.9f);
				UseResolution = Bind(cfg, "Shadows", "UseResolution", p.UseShadowResolution, "Off leaves the resolution the game picked (Low/Medium/High by shadow quality).");
				Resolution = Bind<ShadowResolution>(cfg, "Shadows", "Resolution", p.Resolution, "Shadow map resolution. Vanilla's top step is High; VeryHigh is the one setting that directly buys back what a longer Distance spends.");
				UseProjection = Bind(cfg, "Shadows", "UseProjection", p.UseShadowProjection, "Off leaves the projection from the game's quality asset, which nothing in the game ever writes.");
				Projection = Bind<ShadowProjection>(cfg, "Shadows", "Projection", p.Projection, "StableFit stops shadow edges crawling as the camera moves; CloseFit is sharper but shimmers, and spends no resolution on staying stable — worth trying against a long Distance. Note that Valheim already uses StableFit, so leaving this at its default changes nothing.");
				UseNearPlaneOffset = Bind(cfg, "Shadows", "UseNearPlaneOffset", p.UseNearPlaneOffset, "Off leaves the game's own value.");
				NearPlaneOffset = Bind(cfg, "Shadows", "NearPlaneOffset", p.NearPlaneOffset, "Pulls the shadow camera back. Too small and objects lose the near end of their own shadow; too large and shadows detach from what casts them.", 0f, 10f);
				UsePixelLightCount = Bind(cfg, "Shadows", "UsePixelLightCount", p.UsePixelLightCount, "Off by default and normally right: the game derives this from its Lights setting (2, 4 or 8) and is already at the top step here, so writing anything could only lower it.");
				PixelLightCount = Bind(cfg, "Shadows", "PixelLightCount", p.PixelLightCount, "How many lights are computed per pixel rather than per vertex. Lights past the limit are not removed, only shaded more crudely — this changes precision, not how much light there is.", 1, 16);
			}
		}

		internal class SurfacesSection
		{
			public readonly ConfigEntry<bool> Enabled;

			public readonly ConfigEntry<bool> ForceAnisotropic;

			public readonly ConfigEntry<int> AnisoLimit;

			public readonly ConfigEntry<bool> UseMipmapLimit;

			public readonly ConfigEntry<int> MipmapLimit;

			public readonly ConfigEntry<bool> UseStreamingMipmapsMemoryBudget;

			public readonly ConfigEntry<float> StreamingMipmapsMemoryBudget;

			public SurfacesSection(ConfigFile cfg, PresetValues p)
			{
				Enabled = Bind(cfg, "Surfaces", "Enabled", def: true, "Texture filtering and resolution. Off hands both back to the game.");
				ForceAnisotropic = Bind(cfg, "Surfaces", "ForceAnisotropic", p.ForceAnisotropic, "Forces anisotropic filtering on every texture. The game can only switch anisotropy on and off and never chooses a level, so ground seen at a grazing angle is blurred for no reason but a missing setting.");
				AnisoLimit = Bind(cfg, "Surfaces", "AnisoLimit", p.AnisoForcedLimit, "Forced anisotropic level. Forcing the mode alone is not enough — without raising the limits the level stays whatever each texture was imported with, usually 1.", 1, 16);
				UseMipmapLimit = Bind(cfg, "Surfaces", "UseMipmapLimit", p.UseMipmapLimit, "Off leaves the game's own texture resolution setting alone.");
				MipmapLimit = Bind(cfg, "Surfaces", "MipmapLimit", p.MipmapLimit, "0 means full resolution. Each step up throws away the top mip of every texture.", 0, 3);
				UseStreamingMipmapsMemoryBudget = Bind(cfg, "Surfaces", "UseStreamingMipmapsMemoryBudget", p.UseStreamingMipmapsMemoryBudget, "For custom texture packs only. Vanilla Valheim currently has no loaded textures using Unity mipmap streaming, so this has no effect without a texture pack that explicitly enables streaming mipmaps. Off leaves the game's own budget alone.");
				StreamingMipmapsMemoryBudget = Bind(cfg, "Surfaces", "StreamingMipmapsMemoryBudget", p.StreamingMipmapsMemoryBudget, "Texture-mipmap streaming cache, in MiB, for a compatible custom texture pack. 4096 is approximately 4 GiB: a cache ceiling, not a VRAM reservation and not a texture-quality multiplier. Use only with spare VRAM.", 128f, 16384f);
			}
		}

		internal class PostFxSection
		{
			public readonly ConfigEntry<bool> Enabled;

			public readonly ConfigEntry<AaMethod> Antialiasing;

			public readonly ConfigEntry<bool> UseTaaSharpen;

			public readonly ConfigEntry<float> TaaSharpen;

			public readonly ConfigEntry<bool> UseTaaMotionBlending;

			public readonly ConfigEntry<float> TaaMotionBlending;

			public readonly ConfigEntry<bool> AmbientOcclusion;

			public readonly ConfigEntry<bool> UseAoIntensity;

			public readonly ConfigEntry<float> AoIntensity;

			public readonly ConfigEntry<bool> UseAoRadius;

			public readonly ConfigEntry<float> AoRadius;

			public readonly ConfigEntry<bool> UseAoForwardCompatibility;

			public readonly ConfigEntry<bool> AoForwardCompatibility;

			public readonly ConfigEntry<bool> UseAoIntensityFar;

			public readonly ConfigEntry<float> AoIntensityFar;

			public readonly ConfigEntry<bool> UseAoFarDistance;

			public readonly ConfigEntry<float> AoFarDistance;

			public readonly ConfigEntry<bool> UseAoSampleCount;

			public readonly ConfigEntry<SampleCount> AoSampleCount;

			public readonly ConfigEntry<bool> UseAoDownsampling;

			public readonly ConfigEntry<bool> AoDownsampling;

			public readonly ConfigEntry<bool> UseAoHighPrecision;

			public readonly ConfigEntry<bool> AoHighPrecision;

			public readonly ConfigEntry<bool> UseAoAmbientOnly;

			public readonly ConfigEntry<bool> AoAmbientOnly;

			public readonly ConfigEntry<bool> ScreenSpaceReflections;

			public readonly ConfigEntry<float> SsrIntensity;

			public readonly ConfigEntry<SSRResolution> SsrResolution;

			public readonly ConfigEntry<int> SsrIterationCount;

			public readonly ConfigEntry<float> SsrMaxDistance;

			public PostFxSection(ConfigFile cfg, PresetValues p)
			{
				//IL_029c: Unknown result type (might be due to invalid IL or missing references)
				//IL_03cf: Unknown result type (might be due to invalid IL or missing references)
				Enabled = Bind(cfg, "PostFx", "Enabled", def: true, "Whether the mod edits the post-processing profile at all. Off gives the game back its untouched profile, which is the cleanest way to tell a post effect apart from a lighting change.");
				Antialiasing = Bind(cfg, "PostFx", "Antialiasing", p.AaMode, "Vanilla, Taa or Fxaa. Vanilla means the mod does not touch antialiasing. TAA resolves edges by blending several jittered frames, so it is soft in motion and hits alpha-tested grass and leaves hardest — try Vanilla here first if foliage looks washed out or oddly lit while moving.");
				UseTaaSharpen = Bind(cfg, "PostFx", "UseTaaSharpen", p.UseTaaSharpen, "Off leaves TAA's own sharpen value. Only matters when Antialiasing is Taa.");
				TaaSharpen = Bind(cfg, "PostFx", "TaaSharpen", p.TaaSharpen, "Counter-sharpening applied by TAA, which is blurry by construction.", 0f, 3f);
				UseTaaMotionBlending = Bind(cfg, "PostFx", "UseTaaMotionBlending", p.UseTaaMotionBlending, "Off leaves TAA's own motion blending. Only matters when Antialiasing is Taa.");
				TaaMotionBlending = Bind(cfg, "PostFx", "TaaMotionBlending", p.TaaMotionBlending, "How much of the previous frame TAA keeps while the camera moves. Lower is crisper in motion and shimmers more; higher smears, which is what reads as a haze that clears when you stand still.", 0f, 0.99f);
				AmbientOcclusion = Bind(cfg, "PostFx", "AmbientOcclusion", p.Ao, "Whether the mod configures ambient occlusion (high sample count, full precision, no downsampling) and forces it on. Off leaves AO exactly as the game's own SSAO setting left it. AO darkens creases and never touches the sky, so a hazy landscape under a clean sky points here.");
				UseAoIntensity = Bind(cfg, "PostFx", "UseAoIntensity", p.UseAoIntensity, "Off leaves the profile's own intensity.");
				AoIntensity = Bind(cfg, "PostFx", "AoIntensity", p.AoIntensity, "Strength of ambient occlusion. Try 0.3 if the picture is hazy but AO is worth keeping.", 0f, 4f);
				UseAoRadius = Bind(cfg, "PostFx", "UseAoRadius", p.UseAoRadius, "Off — the default — leaves the radius the game's own profile was authored with. This is the switch behind \"AO makes the grass look overlit\": occlusion samples in world units, so a wide radius throws them a metre out into open air around a blade of grass, finds nothing to occlude with, and loses the contact shading at the base that gives a grass patch its depth. The ground keeps being shaded, the plants stop, and the difference reads as the plants being too bright.");
				AoRadius = Bind(cfg, "PostFx", "AoRadius", p.AoRadius, "How far ambient occlusion reaches, in world units, when the switch above is on. Valheim's own profile uses 0.15, and 0.3 here is the stack's default: both shade tight creases and contact points, which is what foliage needs. Around 1.0 it shades whole corners, which flatters buildings and flattens plants. Past that it stops looking like shadow and starts looking like dirt on the lens.", 0.05f, 4f);
				UseAoForwardCompatibility = Bind(cfg, "PostFx", "UseAoForwardCompatibility", p.UseAoForwardCompatibility, "Off leaves the profile's own setting, which in this stack defaults to reading the G-buffer.");
				AoForwardCompatibility = Bind(cfg, "PostFx", "AoForwardCompatibility", p.AoForwardCompatibility, "Compute occlusion from the depth buffer instead of the G-buffer. This is the setting behind \"AO makes the grass look overlit\": the G-buffer holds only what the deferred path drew, so anything rendered in the forward pass gets no usable normals there — grass and foliage come out unoccluded while the ground around them darkens, and that contrast reads as the plants being lit too brightly. On, occlusion covers the whole image. Costs a little more, and disables AmbientOnly.");
				UseAoIntensityFar = Bind(cfg, "PostFx", "UseAoIntensityFar", p.UseAoIntensityFar, "Off leaves the profile's own far intensity — 1.5 in Valheim's, half again the near value, which is a quiet reason distance looks darker than the number above suggests.");
				AoIntensityFar = Bind(cfg, "PostFx", "AoIntensityFar", p.AoIntensityFar, "Ambient occlusion strength past FarDistance. Set it equal to AoIntensity for occlusion that does not change with distance.", 0f, 4f);
				UseAoFarDistance = Bind(cfg, "PostFx", "UseAoFarDistance", p.UseAoFarDistance, "Off leaves the profile's own far distance. The game itself writes 100 or 150 m here depending on its SSAO setting.");
				AoFarDistance = Bind(cfg, "PostFx", "AoFarDistance", p.AoFarDistance, "Where ambient occlusion switches from AoIntensity to AoIntensityFar, in metres.", 10f, 500f);
				UseAoSampleCount = Bind(cfg, "PostFx", "UseAoSampleCount", p.UseAoSampleCount, "Off leaves the sample count the game chose (Low or Medium by its SSAO setting).");
				AoSampleCount = Bind<SampleCount>(cfg, "PostFx", "AoSampleCount", p.AoSampleCount, "Occlusion samples per pixel: Lowest 3, Low 6, Medium 10, High 16. Too few is what makes ambient occlusion look like grain rather than shading.");
				UseAoDownsampling = Bind(cfg, "PostFx", "UseAoDownsampling", p.UseAoDownsampling, "Off leaves the game's own choice, which halves the resolution on its lower SSAO step.");
				AoDownsampling = Bind(cfg, "PostFx", "AoDownsampling", p.AoDownsampling, "Halve the resolution of the occlusion pass. Cheaper, and blurs occlusion across edges — which on grass means a dark halo where there should be a shaded blade.");
				UseAoHighPrecision = Bind(cfg, "PostFx", "UseAoHighPrecision", p.UseAoHighPrecision, "Off leaves the profile's own setting.");
				AoHighPrecision = Bind(cfg, "PostFx", "AoHighPrecision", p.AoHighPrecision, "Use the full-precision depth texture rather than the packed depth+normals one. Only has an effect when AoForwardCompatibility is on, where it decides whether occlusion reads depth alone (cheaper, Valheim already renders it) or a separate depth+normals pass.");
				UseAoAmbientOnly = Bind(cfg, "PostFx", "UseAoAmbientOnly", p.UseAoAmbientOnly, "Off leaves the profile's own setting.");
				AoAmbientOnly = Bind(cfg, "PostFx", "AoAmbientOnly", p.AoAmbientOnly, "Apply occlusion only to ambient light rather than to the finished image. Physically the more correct of the two, and the one that misses forward-rendered objects hardest — it needs the G-buffer, so it does nothing while AoForwardCompatibility is on.");
				ScreenSpaceReflections = Bind(cfg, "PostFx", "ScreenSpaceReflections", p.Ssr, "Screen space reflections on water and wet surfaces. The game never enables these at all, so this is added light rather than improved light — a fair suspect if surfaces look brighter than they should.");
				SsrIntensity = Bind(cfg, "PostFx", "SsrIntensity", p.SsrIntensity, "Multiplier on how much the screen space reflection contributes. 1 is the stack's own default.", 0f, 2f);
				SsrResolution = Bind<SSRResolution>(cfg, "PostFx", "SsrResolution", p.SsrResolution, "High traces at full screen resolution; Low traces at half resolution. Low is faster but may lose thin reflections and add edge artifacts.");
				SsrIterationCount = Bind(cfg, "PostFx", "SsrIterationCount", p.SsrIterationCount, "Maximum ray steps per screen-space reflection. Lower values reduce tracing work but can miss distant or off-angle reflections. The previous fixed value was 256.", 16, 1024);
				SsrMaxDistance = Bind(cfg, "PostFx", "SsrMaxDistance", p.SsrMaxDistance, "Maximum SSR ray distance in world units. The previous fixed value was 100 m; shorter rays can miss distant reflections.", 0.1f, 300f);
			}
		}

		internal class DofSection
		{
			public readonly ConfigEntry<bool> Enabled;

			public readonly ConfigEntry<float> MaxBlurSize;

			public readonly ConfigEntry<float> FocalSize;

			public readonly ConfigEntry<float> Aperture;

			public readonly ConfigEntry<bool> HighResolution;

			public readonly ConfigEntry<BlurSampleCount> BlurSampleCount;

			public DofSection(ConfigFile cfg, PresetValues p)
			{
				//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
				Enabled = Bind(cfg, "DoF", "Enabled", p.DofEnabled, "Apply the values below to Valheim's world camera. The game's own Depth of Field graphics switch still controls whether the effect runs. Off restores the camera's original values.");
				MaxBlurSize = Bind(cfg, "DoF", "MaxBlurSize", p.DofMaxBlurSize, "Maximum blur radius. The measured world camera used 1.1; lower values make out-of-focus objects less blurry.", 0.1f, 3f);
				FocalSize = Bind(cfg, "DoF", "FocalSize", p.DofFocalSize, "Width of the in-focus region. The measured world camera used 0.97.", 0f, 2f);
				Aperture = Bind(cfg, "DoF", "Aperture", p.DofAperture, "Transition from focus to blur. The measured world camera used 0.68. Keep this below 1 to avoid the image effect's singularity.", 0f, 0.95f);
				HighResolution = Bind(cfg, "DoF", "HighResolution", p.DofHighResolution, "Render the blur at the image effect's higher resolution. Disable to trade blur quality for GPU time.");
				BlurSampleCount = Bind<BlurSampleCount>(cfg, "DoF", "BlurSampleCount", p.DofBlurSampleCount, "Low, Medium or High disc-blur sample count. Valheim's measured world camera used Medium; High matches the author's current configuration and costs more GPU time.");
			}
		}

		internal class VegetationSection
		{
			public readonly ConfigEntry<bool> Enabled;

			public readonly ConfigEntry<bool> UseDistance;

			public readonly ConfigEntry<float> Distance;

			public readonly ConfigEntry<bool> UseAmount;

			public readonly ConfigEntry<float> Amount;

			public VegetationSection(ConfigFile cfg, PresetValues p)
			{
				Enabled = Bind(cfg, "Vegetation", "Enabled", def: true, "Grass and small props. This is near-field clutter only, not draw distance — that belongs to DistantValheim.");
				UseDistance = Bind(cfg, "Vegetation", "UseDistance", p.UseClutterDistance, "Off leaves the game's own clutter radius (140 m in a loaded world).");
				Distance = Bind(cfg, "Vegetation", "Distance", p.ClutterDistance, "How far grass and small props are drawn, in metres. Area grows with the square of this, and building grass patches is main-thread work — the bottleneck on this machine. Raise it while watching frame time.", 20f, 400f);
				UseAmount = Bind(cfg, "Vegetation", "UseAmount", p.UseClutterAmount, "Off leaves the game's own density. On, the multiplier below applies — and unlike most of this mod it costs CPU, not GPU.");
				Amount = Bind(cfg, "Vegetation", "Amount", p.ClutterAmount, "Grass and undergrowth density multiplier. 1 is vanilla.", 0.1f, 5f);
			}
		}

		internal class ReflectionsSection
		{
			public readonly ConfigEntry<bool> Enabled;

			public readonly ConfigEntry<bool> UseResolution;

			public readonly ConfigEntry<int> Resolution;

			public readonly ConfigEntry<bool> ForceRealtimeProbes;

			public ReflectionsSection(ConfigFile cfg, PresetValues p)
			{
				Enabled = Bind(cfg, "Reflections", "Enabled", def: true, "Environment reflection probes. This is the one large system the game never configures, so unlike anisotropy or the light limit there is real headroom here.");
				UseResolution = Bind(cfg, "Reflections", "UseResolution", p.UseReflectionResolution, "Off leaves every probe at the resolution it was authored with.");
				Resolution = Bind(cfg, "Reflections", "Resolution", p.ReflectionResolution, "Cubemap resolution of reflection probes; powers of two only. A cubemap is six scene renders, so cost grows with the square of this — it lands on the GPU, not the main thread.", 16, 2048);
				ForceRealtimeProbes = Bind(cfg, "Reflections", "ForceRealtimeProbes", p.ForceRealtimeProbes, "Forces QualitySettings.realtimeReflectionProbes on. Valheim already has it on, so this changes nothing there and exists as insurance against another mod or a quality preset turning it off — in which case the probe resolution below would be spent on cubemaps that never refresh.");
			}
		}

		internal class MaterialsSection
		{
			public readonly ConfigEntry<bool> Enabled;

			public readonly ConfigEntry<float> NormalIntensity;

			public MaterialsSection(ConfigFile cfg, PresetValues p)
			{
				Enabled = Bind(cfg, "Materials", "NormalBoost", p.NormalBoostEnabled, "Strengthen normal maps across every material that has one — buildings, rocks, creatures, props, foliage. The detail is already authored and already in memory: the log wall carries a 4096x4096 normal map applied at intensity 1.0. This only reads it harder, so it costs no geometry and no draw calls. It does change how every surface catches light, which makes it worth toggling when the lighting itself looks wrong.");
				NormalIntensity = Bind(cfg, "Materials", "NormalIntensity", p.NormalIntensity, "Multiplier on each material's own normal intensity, not a flat replacement — a surface an artist deliberately kept subtle stays subtle. 1 is vanilla. Past about 2 the lighting starts to look embossed rather than carved.", 0.1f, 4f);
			}
		}

		internal class WaterSection
		{
			public readonly ConfigEntry<bool> Enabled;

			public readonly ConfigEntry<bool> UseTessellation;

			public readonly ConfigEntry<float> Tessellation;

			public readonly ConfigEntry<bool> UseRefraction;

			public readonly ConfigEntry<float> RefractionScale;

			public readonly ConfigEntry<float> RefractionMax;

			public readonly ConfigEntry<bool> UseNormalPower;

			public readonly ConfigEntry<float> NormalPower;

			public readonly ConfigEntry<bool> UseLodDistance;

			public readonly ConfigEntry<float> LodDistance;

			public readonly ConfigEntry<bool> UseSmoothness;

			public readonly ConfigEntry<float> Smoothness;

			public readonly ConfigEntry<bool> UseMetallic;

			public readonly ConfigEntry<float> Metallic;

			public WaterSection(ConfigFile cfg, PresetValues p)
			{
				Enabled = Bind(cfg, "Water", "Enabled", def: true, "Turn up the parts of the game's own water shader that ship almost switched off. Nothing here is invented: every value is a property Custom/Water declares, moved inside the range its author set.");
				UseTessellation = Bind(cfg, "Water", "UseTessellation", p.UseWaterTessellation, "Off leaves the shader's own tessellation (4 of a possible 32).");
				Tessellation = Bind(cfg, "Water", "Tessellation", p.WaterTessellation, "Real geometry for waves instead of a flat plane shaded to look bumpy. This is the single biggest change to how water reads, and it is paid in GPU time. Drop it if the shoreline starts costing frames.", 1f, 32f);
				UseRefraction = Bind(cfg, "Water", "UseRefraction", p.UseWaterRefraction, "Off leaves the shader's own refraction, which is capped at 0.01 — close to none at all.");
				RefractionScale = Bind(cfg, "Water", "RefractionScale", p.WaterRefractionScale, "How strongly the surface normal bends the view through it, before the cap below.", 0f, 2f);
				RefractionMax = Bind(cfg, "Water", "RefractionMax", p.WaterRefractionMax, "Cap on that bend. Raise gently: past about 0.1 the bottom starts to wobble like jelly.", 0f, 0.5f);
				UseNormalPower = Bind(cfg, "Water", "UseNormalPower", p.UseWaterNormalPower, "Off leaves the shader's own ripple strength (0.5 of 5).");
				NormalPower = Bind(cfg, "Water", "NormalPower", p.WaterNormalPower, "Strength of the fine ripple on the surface. With tessellation giving the waves real geometry, strong normals on top read as noise rather than ripple.", 0f, 5f);
				UseLodDistance = Bind(cfg, "Water", "UseLodDistance", p.UseWaterLodDistance, "Off leaves the shader's own LOD distance (120 m).");
				LodDistance = Bind(cfg, "Water", "LodDistance", p.WaterLodDistance, "Metres before water switches to its simplified LOD surface. Unlike the rest of this section it also costs main-thread time, because more detailed water surfaces stay active.", 50f, 1000f);
				UseSmoothness = Bind(cfg, "Water", "UseSmoothness", p.UseWaterSmoothness, "Off leaves the shader's own smoothness (0.94).");
				Smoothness = Bind(cfg, "Water", "Smoothness", p.WaterSmoothness, "How mirror-like the surface is. The last stretch towards 1 is where a reflection stops being a soft sheen and starts showing the shoreline back. Costs nothing, but works with reflection probe resolution: a sharp mirror of a blurry cubemap is still blurry.", 0f, 1f);
				UseMetallic = Bind(cfg, "Water", "UseMetallic", p.UseWaterMetallic, "Off leaves the shader's own value (0).");
				Metallic = Bind(cfg, "Water", "Metallic", p.WaterMetallic, "Metallic tints reflections with the surface colour, and at 0.8 that reads as a real body of water rather than a sheet of glass — chosen by eye, against the initial assumption that it should be left alone.", 0f, 1f);
			}
		}

		internal class ParticlesSection
		{
			public readonly ConfigEntry<bool> Enabled;

			public readonly ConfigEntry<bool> UseTranslucency;

			public readonly ConfigEntry<float> Translucency;

			public readonly ConfigEntry<bool> UseSoftness;

			public readonly ConfigEntry<float> Softness;

			public readonly ConfigEntry<bool> UseTessellation;

			public readonly ConfigEntry<float> Tessellation;

			public readonly ConfigEntry<bool> UseLightNormal;

			public readonly ConfigEntry<float> LightNormal;

			public ParticlesSection(ConfigFile cfg, PresetValues p)
			{
				Enabled = Bind(cfg, "Particles", "Enabled", def: true, "Improve how smoke, mist and other particles are lit. Found by scanning shaders for values parked at the bottom of their own range: smoke ships with translucency at 0.19 out of 1.0.");
				UseTranslucency = Bind(cfg, "Particles", "UseTranslucency", p.UseParticleTranslucency, "Off leaves each effect's own translucency and wrapped diffuse.");
				Translucency = Bind(cfg, "Particles", "Translucency", p.ParticleTranslucency, "Multiplier on how much light passes through a particle. This is what makes smoke above a hearth glow from the fire inside it instead of hanging there as a grey blob. A multiplier, not a fixed value, so effects authored subtle stay subtle. 1 is vanilla.", 0f, 5f);
				UseSoftness = Bind(cfg, "Particles", "UseSoftness", p.UseParticleSoftness, "Off leaves each effect's own soft-particle factor.");
				Softness = Bind(cfg, "Particles", "Softness", p.ParticleSoftness, "Multiplier on the soft-particle factor, which fades a particle where it intersects solid geometry. Vanilla smoke cuts a hard line into the ground; the shader allows up to 3.", 0f, 3f);
				UseTessellation = Bind(cfg, "Particles", "UseTessellation", p.UseParticleTessellation, "Off leaves the tessellated mist shader alone.");
				Tessellation = Bind(cfg, "Particles", "Tessellation", p.ParticleTessellation, "Subdivisions for the tessellated mist shader used by Mistlands fog. The shader declares 1..13 and ships at 3.", 1f, 13f);
				UseLightNormal = Bind(cfg, "Particles", "UseLightNormal", p.UseParticleLightNormal, "Off leaves the shader's own value, which for snow and rain is exactly 0.");
				LightNormal = Bind(cfg, "Particles", "LightNormalFactor", p.ParticleLightNormal, "How much a particle's facing affects how it is lit. Snow and rain ship at 0 — a flake is lit identically whichever way it is turned, which is why falling snow reads as flat white specks. At 0.5 flakes facing a fire or the moon catch the light and those turned away stay dark. Set rather than multiplied, because vanilla is zero and scaling zero changes nothing.", 0f, 1f);
			}
		}

		public readonly ConfigEntry<bool> Enabled;

		public readonly ConfigEntry<string> Preset;

		public readonly ConfigEntry<KeyboardShortcut> PresetKey;

		public readonly ShadowsSection Shadows;

		public readonly SurfacesSection Surfaces;

		public readonly PostFxSection PostFx;

		public readonly DofSection DoF;

		public readonly VegetationSection Vegetation;

		public readonly ReflectionsSection Reflections;

		public readonly MaterialsSection Materials;

		public readonly WaterSection Water;

		public readonly ParticlesSection Particles;

		public VisualsConfig(ConfigFile cfg)
		{
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			PresetValues physical = Presets.Physical;
			Enabled = Bind(cfg, "General", "Enabled", def: true, "Master switch. Off restores every value the mod changed and stands down.");
			Preset = cfg.Bind<string>("General", "Preset", "Physical", "Off or Physical. Off restores everything to vanilla and is the reference to compare against — useful for judging whether a change actually helped.");
			PresetKey = cfg.Bind<KeyboardShortcut>("Keys", "PresetKey", new KeyboardShortcut((KeyCode)275, Array.Empty<KeyCode>()), "Cycles presets. Taken elsewhere and deliberately avoided: F5 console, F6/F9/F11 perf probe, F8/F10 ValheimAcoustics, F7 DistantValheim diagnostics, PageDown Badgers, arrows and Insert/End/Delete DistantValheim.");
			Shadows = new ShadowsSection(cfg, physical);
			Surfaces = new SurfacesSection(cfg, physical);
			PostFx = new PostFxSection(cfg, physical);
			DoF = new DofSection(cfg, physical);
			Vegetation = new VegetationSection(cfg, physical);
			Reflections = new ReflectionsSection(cfg, physical);
			Materials = new MaterialsSection(cfg, physical);
			Water = new WaterSection(cfg, physical);
			Particles = new ParticlesSection(cfg, physical);
			Preset.SettingChanged += delegate
			{
				VisualsPlugin.Preset = Preset.Value;
			};
		}

		public PresetValues Compose(PresetValues v)
		{
			//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_00ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_011c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0121: Unknown result type (might be due to invalid IL or missing references)
			//IL_03de: Unknown result type (might be due to invalid IL or missing references)
			//IL_03e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_04b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_04b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_056d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0572: Unknown result type (might be due to invalid IL or missing references)
			v.ShadowsEnabled &= Shadows.Enabled.Value;
			v.UseShadowDistance = Shadows.UseDistance.Value;
			v.ShadowDistance = Shadows.Distance.Value;
			v.UseShadowCascades = Shadows.UseCascades.Value;
			v.ShadowCascades = Shadows.Cascades.Value;
			v.UseCascadeSplit = Shadows.UseCascadeSplit.Value;
			v.Cascade4Split = new Vector3(Shadows.CascadeSplit1.Value, Shadows.CascadeSplit2.Value, Shadows.CascadeSplit3.Value);
			v.UseShadowResolution = Shadows.UseResolution.Value;
			v.Resolution = Shadows.Resolution.Value;
			v.UseShadowProjection = Shadows.UseProjection.Value;
			v.Projection = Shadows.Projection.Value;
			v.UseNearPlaneOffset = Shadows.UseNearPlaneOffset.Value;
			v.NearPlaneOffset = Shadows.NearPlaneOffset.Value;
			v.UsePixelLightCount = Shadows.UsePixelLightCount.Value;
			v.PixelLightCount = Shadows.PixelLightCount.Value;
			v.SurfacesEnabled &= Surfaces.Enabled.Value;
			v.ForceAnisotropic &= Surfaces.ForceAnisotropic.Value;
			v.AnisoForcedLimit = Surfaces.AnisoLimit.Value;
			v.UseMipmapLimit = Surfaces.UseMipmapLimit.Value;
			v.MipmapLimit = Surfaces.MipmapLimit.Value;
			v.UseStreamingMipmapsMemoryBudget = Surfaces.UseStreamingMipmapsMemoryBudget.Value;
			v.StreamingMipmapsMemoryBudget = Surfaces.StreamingMipmapsMemoryBudget.Value;
			v.PostFxEnabled &= PostFx.Enabled.Value;
			v.AaMode = PostFx.Antialiasing.Value;
			v.UseTaaSharpen = PostFx.UseTaaSharpen.Value;
			v.TaaSharpen = PostFx.TaaSharpen.Value;
			v.UseTaaMotionBlending = PostFx.UseTaaMotionBlending.Value;
			v.TaaMotionBlending = PostFx.TaaMotionBlending.Value;
			v.Ao &= PostFx.AmbientOcclusion.Value;
			v.UseAoIntensity = PostFx.UseAoIntensity.Value;
			v.AoIntensity = PostFx.AoIntensity.Value;
			v.UseAoRadius = PostFx.UseAoRadius.Value;
			v.AoRadius = PostFx.AoRadius.Value;
			v.UseAoForwardCompatibility = PostFx.UseAoForwardCompatibility.Value;
			v.AoForwardCompatibility = PostFx.AoForwardCompatibility.Value;
			v.UseAoIntensityFar = PostFx.UseAoIntensityFar.Value;
			v.AoIntensityFar = PostFx.AoIntensityFar.Value;
			v.UseAoFarDistance = PostFx.UseAoFarDistance.Value;
			v.AoFarDistance = PostFx.AoFarDistance.Value;
			v.UseAoSampleCount = PostFx.UseAoSampleCount.Value;
			v.AoSampleCount = PostFx.AoSampleCount.Value;
			v.UseAoDownsampling = PostFx.UseAoDownsampling.Value;
			v.AoDownsampling = PostFx.AoDownsampling.Value;
			v.UseAoHighPrecision = PostFx.UseAoHighPrecision.Value;
			v.AoHighPrecision = PostFx.AoHighPrecision.Value;
			v.UseAoAmbientOnly = PostFx.UseAoAmbientOnly.Value;
			v.AoAmbientOnly = PostFx.AoAmbientOnly.Value;
			v.Ssr &= PostFx.ScreenSpaceReflections.Value;
			v.SsrIntensity = PostFx.SsrIntensity.Value;
			v.SsrResolution = PostFx.SsrResolution.Value;
			v.SsrIterationCount = PostFx.SsrIterationCount.Value;
			v.SsrMaxDistance = PostFx.SsrMaxDistance.Value;
			v.DofEnabled &= DoF.Enabled.Value;
			v.DofMaxBlurSize = DoF.MaxBlurSize.Value;
			v.DofFocalSize = DoF.FocalSize.Value;
			v.DofAperture = DoF.Aperture.Value;
			v.DofHighResolution = DoF.HighResolution.Value;
			v.DofBlurSampleCount = DoF.BlurSampleCount.Value;
			v.VegetationEnabled &= Vegetation.Enabled.Value;
			v.UseClutterDistance = Vegetation.UseDistance.Value;
			v.ClutterDistance = Vegetation.Distance.Value;
			v.UseClutterAmount = Vegetation.UseAmount.Value;
			v.ClutterAmount = Vegetation.Amount.Value;
			v.ReflectionsEnabled &= Reflections.Enabled.Value;
			v.UseReflectionResolution = Reflections.UseResolution.Value;
			v.ReflectionResolution = Reflections.Resolution.Value;
			v.ForceRealtimeProbes &= Reflections.ForceRealtimeProbes.Value;
			v.NormalBoostEnabled &= Materials.Enabled.Value;
			v.NormalIntensity = Materials.NormalIntensity.Value;
			v.WaterEnabled &= Water.Enabled.Value;
			v.UseWaterTessellation = Water.UseTessellation.Value;
			v.WaterTessellation = Water.Tessellation.Value;
			v.UseWaterRefraction = Water.UseRefraction.Value;
			v.WaterRefractionScale = Water.RefractionScale.Value;
			v.WaterRefractionMax = Water.RefractionMax.Value;
			v.UseWaterNormalPower = Water.UseNormalPower.Value;
			v.WaterNormalPower = Water.NormalPower.Value;
			v.UseWaterLodDistance = Water.UseLodDistance.Value;
			v.WaterLodDistance = Water.LodDistance.Value;
			v.UseWaterSmoothness = Water.UseSmoothness.Value;
			v.WaterSmoothness = Water.Smoothness.Value;
			v.UseWaterMetallic = Water.UseMetallic.Value;
			v.WaterMetallic = Water.Metallic.Value;
			v.ParticlesEnabled &= Particles.Enabled.Value;
			v.UseParticleTranslucency = Particles.UseTranslucency.Value;
			v.ParticleTranslucency = Particles.Translucency.Value;
			v.UseParticleSoftness = Particles.UseSoftness.Value;
			v.ParticleSoftness = Particles.Softness.Value;
			v.UseParticleTessellation = Particles.UseTessellation.Value;
			v.ParticleTessellation = Particles.Tessellation.Value;
			v.UseParticleLightNormal = Particles.UseLightNormal.Value;
			v.ParticleLightNormal = Particles.LightNormal.Value;
			return v;
		}

		private static ConfigEntry<T> Bind<T>(ConfigFile cfg, string section, string key, T def, string desc)
		{
			ConfigEntry<T> obj = cfg.Bind<T>(section, key, def, desc);
			obj.SettingChanged += Reapply;
			return obj;
		}

		private static ConfigEntry<T> Bind<T>(ConfigFile cfg, string section, string key, T def, string desc, T min, T max) where T : IComparable
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Expected O, but got Unknown
			ConfigEntry<T> obj = cfg.Bind<T>(section, key, def, new ConfigDescription(desc, (AcceptableValueBase)(object)new AcceptableValueRange<T>(min, max), Array.Empty<object>()));
			obj.SettingChanged += Reapply;
			return obj;
		}

		private static void Reapply(object sender, EventArgs e)
		{
			VisualsPlugin.MarkDirty();
		}
	}
	internal static class DepthOfFieldTuning
	{
		private static DepthOfField _dof;

		private static float _originalMaxBlurSize;

		private static float _originalFocalSize;

		private static float _originalAperture;

		private static bool _originalHighResolution;

		private static BlurSampleCount _originalBlurSampleCount;

		public static void Apply(PresetValues v)
		{
			//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			CameraEffects instance = CameraEffects.instance;
			if (!v.DofEnabled || (Object)(object)instance == (Object)null || instance.m_forceDof)
			{
				Restore();
				return;
			}
			DepthOfField component = ((Component)instance).GetComponent<DepthOfField>();
			if ((Object)(object)component == (Object)null)
			{
				Restore();
				return;
			}
			if ((Object)(object)_dof != (Object)(object)component)
			{
				Restore();
				_dof = component;
				_originalMaxBlurSize = component.maxBlurSize;
				_originalFocalSize = component.focalSize;
				_originalAperture = component.aperture;
				_originalHighResolution = component.highResolution;
				_originalBlurSampleCount = component.blurSampleCount;
			}
			component.maxBlurSize = v.DofMaxBlurSize;
			component.focalSize = v.DofFocalSize;
			component.aperture = v.DofAperture;
			component.highResolution = v.DofHighResolution;
			component.blurSampleCount = v.DofBlurSampleCount;
		}

		public static void Restore()
		{
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_dof != (Object)null)
			{
				_dof.maxBlurSize = _originalMaxBlurSize;
				_dof.focalSize = _originalFocalSize;
				_dof.aperture = _originalAperture;
				_dof.highResolution = _originalHighResolution;
				_dof.blurSampleCount = _originalBlurSampleCount;
			}
			_dof = null;
		}
	}
	internal static class Materials
	{
		private static readonly Dictionary<Material, float> Original = new Dictionary<Material, float>();

		private static readonly int BumpScaleId = Shader.PropertyToID("_BumpScale");

		private static readonly int BumpMapId = Shader.PropertyToID("_BumpMap");

		private static readonly List<Material> Fresh = new List<Material>();

		private static float _applied;

		private static float _nextSweepAt;

		public static void Apply(PresetValues v)
		{
			if (!v.NormalBoostEnabled || v.NormalIntensity <= 0f)
			{
				Restore();
				return;
			}
			if (Time.realtimeSinceStartup >= _nextSweepAt)
			{
				_nextSweepAt = Time.realtimeSinceStartup + 20f;
				Sweep();
			}
			bool flag = !Mathf.Approximately(_applied, v.NormalIntensity);
			if (!flag && Fresh.Count == 0)
			{
				return;
			}
			if (flag)
			{
				foreach (KeyValuePair<Material, float> item in Original)
				{
					if (!((Object)(object)item.Key == (Object)null))
					{
						item.Key.SetFloat(BumpScaleId, item.Value * v.NormalIntensity);
					}
				}
			}
			else
			{
				foreach (Material item2 in Fresh)
				{
					if (!((Object)(object)item2 == (Object)null))
					{
						item2.SetFloat(BumpScaleId, Original[item2] * v.NormalIntensity);
					}
				}
			}
			_applied = v.NormalIntensity;
			Fresh.Clear();
		}

		private static void Sweep()
		{
			Material[] array = Resources.FindObjectsOfTypeAll<Material>();
			foreach (Material val in array)
			{
				if (!((Object)(object)val == (Object)null) && !((Object)(object)val.shader == (Object)null) && !Original.ContainsKey(val) && val.HasFloat(BumpScaleId) && val.HasTexture(BumpMapId) && !((Object)(object)val.GetTexture(BumpMapId) == (Object)null))
				{
					Original[val] = val.GetFloat(BumpScaleId);
					Fresh.Add(val);
				}
			}
		}

		public static void Restore()
		{
			foreach (KeyValuePair<Material, float> item in Original)
			{
				if ((Object)(object)item.Key != (Object)null)
				{
					item.Key.SetFloat(BumpScaleId, item.Value);
				}
			}
			_applied = 0f;
		}
	}
	internal sealed class OwnedSetting<T>
	{
		private readonly Func<T> _read;

		private readonly Action<T> _write;

		private T _vanilla;

		private bool _owned;

		public OwnedSetting(Func<T> read, Action<T> write)
		{
			_read = read;
			_write = write;
		}

		public void Apply(bool use, T value)
		{
			if (!use)
			{
				Restore();
				return;
			}
			if (!_owned)
			{
				_vanilla = _read();
				_owned = true;
			}
			_write(value);
		}

		public void Restore()
		{
			if (_owned)
			{
				_write(_vanilla);
				_owned = false;
			}
		}

		public void Disown()
		{
			_owned = false;
		}
	}
	internal static class Particles
	{
		private struct Saved
		{
			public float Translucency;

			public float WrappedDiffuse;

			public float InvFade;

			public float Tess;

			public float LightNormalFactor;
		}

		private static readonly Dictionary<Material, Saved> Original = new Dictionary<Material, Saved>();

		private static readonly List<Material> Fresh = new List<Material>();

		private static readonly int TranslucencyId = Shader.PropertyToID("_Translucency");

		private static readonly int WrappedDiffuseId = Shader.PropertyToID("_WrappedDiffuse");

		private static readonly int InvFadeId = Shader.PropertyToID("_InvFade");

		private static readonly int TessId = Shader.PropertyToID("_Tess");

		private static readonly int LightNormalFactorId = Shader.PropertyToID("_LightNormalFactor");

		private static string _applied;

		private static float _nextSweepAt;

		public static void Apply(PresetValues v)
		{
			if (!v.ParticlesEnabled)
			{
				Restore();
				return;
			}
			if (Time.realtimeSinceStartup >= _nextSweepAt)
			{
				_nextSweepAt = Time.realtimeSinceStartup + 20f;
				Sweep();
			}
			string text = $"{v.UseParticleTranslucency}:{v.ParticleTranslucency}|{v.UseParticleSoftness}:{v.ParticleSoftness}|" + $"{v.UseParticleTessellation}:{v.ParticleTessellation}|{v.UseParticleLightNormal}:{v.ParticleLightNormal}";
			bool flag = _applied != text;
			if (!flag && Fresh.Count == 0)
			{
				return;
			}
			IEnumerable<Material> enumerable;
			if (!flag)
			{
				IEnumerable<Material> fresh = Fresh;
				enumerable = fresh;
			}
			else
			{
				IEnumerable<Material> fresh = Original.Keys;
				enumerable = fresh;
			}
			foreach (Material item in enumerable)
			{
				if (!((Object)(object)item == (Object)null) && Original.TryGetValue(item, out var value))
				{
					if (item.HasFloat(TranslucencyId))
					{
						item.SetFloat(TranslucencyId, v.UseParticleTranslucency ? Mathf.Clamp01(value.Translucency * v.ParticleTranslucency) : value.Translucency);
					}
					if (item.HasFloat(WrappedDiffuseId))
					{
						item.SetFloat(WrappedDiffuseId, v.UseParticleTranslucency ? Mathf.Clamp01(value.WrappedDiffuse * v.ParticleTranslucency) : value.WrappedDiffuse);
					}
					if (item.HasFloat(InvFadeId))
					{
						item.SetFloat(InvFadeId, v.UseParticleSoftness ? Mathf.Clamp(value.InvFade * v.ParticleSoftness, 0f, 3f) : value.InvFade);
					}
					if (item.HasFloat(TessId) && value.Tess > 0f)
					{
						item.SetFloat(TessId, (v.UseParticleTessellation && v.ParticleTessellation > 0f) ? Mathf.Clamp(v.ParticleTessellation, 1f, 13f) : value.Tess);
					}
					if (item.HasFloat(LightNormalFactorId))
					{
						item.SetFloat(LightNormalFactorId, (v.UseParticleLightNormal && v.ParticleLightNormal >= 0f) ? Mathf.Clamp01(v.ParticleLightNormal) : value.LightNormalFactor);
					}
				}
			}
			_applied = text;
			Fresh.Clear();
		}

		private static void Sweep()
		{
			Material[] array = Resources.FindObjectsOfTypeAll<Material>();
			foreach (Material val in array)
			{
				if (!((Object)(object)val == (Object)null) && !((Object)(object)val.shader == (Object)null) && !Original.ContainsKey(val))
				{
					bool flag = val.HasFloat(TranslucencyId);
					bool flag2 = val.HasFloat(InvFadeId);
					bool flag3 = val.HasFloat(LightNormalFactorId);
					if (flag || flag2 || flag3)
					{
						Original[val] = new Saved
						{
							Translucency = (flag ? val.GetFloat(TranslucencyId) : 0f),
							WrappedDiffuse = (val.HasFloat(WrappedDiffuseId) ? val.GetFloat(WrappedDiffuseId) : 0f),
							InvFade = (flag2 ? val.GetFloat(InvFadeId) : 0f),
							Tess = (val.HasFloat(TessId) ? val.GetFloat(TessId) : 0f),
							LightNormalFactor = (flag3 ? val.GetFloat(LightNormalFactorId) : 0f)
						};
						Fresh.Add(val);
					}
				}
			}
		}

		public static void Restore()
		{
			foreach (KeyValuePair<Material, Saved> item in Original)
			{
				Material key = item.Key;
				if (!((Object)(object)key == (Object)null))
				{
					if (key.HasFloat(TranslucencyId))
					{
						key.SetFloat(TranslucencyId, item.Value.Translucency);
					}
					if (key.HasFloat(WrappedDiffuseId))
					{
						key.SetFloat(WrappedDiffuseId, item.Value.WrappedDiffuse);
					}
					if (key.HasFloat(InvFadeId))
					{
						key.SetFloat(InvFadeId, item.Value.InvFade);
					}
					if (key.HasFloat(TessId) && item.Value.Tess > 0f)
					{
						key.SetFloat(TessId, item.Value.Tess);
					}
					if (key.HasFloat(LightNormalFactorId))
					{
						key.SetFloat(LightNormalFactorId, item.Value.LightNormalFactor);
					}
				}
			}
			_applied = null;
		}
	}
	[BepInPlugin("ru.ivest.valheimvisuals", "Valheim Visuals", "1.2.3")]
	public class VisualsPlugin : BaseUnityPlugin
	{
		public const string Guid = "ru.ivest.valheimvisuals";

		public const string Name = "Valheim Visuals";

		public const string Version = "1.2.3";

		internal static ManualLogSource Log;

		internal static VisualsConfig Cfg;

		private static string _preset = "Off";

		private static bool _dirty;

		private float _nextProbeSweepAt;

		private Harmony _harmony;

		public static string Preset
		{
			get
			{
				return _preset;
			}
			set
			{
				if (!(_preset == value))
				{
					_preset = value;
					_dirty = true;
				}
			}
		}

		internal static void MarkDirty()
		{
			_dirty = true;
		}

		private void Awake()
		{
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			Cfg = new VisualsConfig(((BaseUnityPlugin)this).Config);
			_preset = Cfg.Preset.Value;
			if (Array.IndexOf(Presets.Names, _preset) < 0)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("[visuals] unknown preset '" + _preset + "' in config — using Physical instead. Known presets: " + string.Join(", ", Presets.Names)));
				_preset = "Physical";
			}
			_harmony = new Harmony("ru.ivest.valheimvisuals");
			try
			{
				_harmony.PatchAll();
			}
			catch (Exception ex)
			{
				Log.LogError((object)("PatchAll failed — settings may be overwritten by the game: " + ex.Message));
			}
			_dirty = true;
		}

		private void LateUpdate()
		{
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			KeyboardShortcut value = Cfg.PresetKey.Value;
			if (((KeyboardShortcut)(ref value)).IsDown())
			{
				CyclePreset();
			}
			if (Cfg.Enabled.Value && Time.realtimeSinceStartup >= _nextProbeSweepAt)
			{
				_nextProbeSweepAt = Time.realtimeSinceStartup + 5f;
				PresetValues v = Cfg.Compose(Presets.Get(_preset));
				Reflections.Apply(v);
				Water.Apply(v);
				DepthOfFieldTuning.Apply(v);
			}
			if (_dirty)
			{
				_dirty = false;
				ApplyNow();
			}
		}

		private void ApplyNow()
		{
			if (!Cfg.Enabled.Value)
			{
				RestoreAll();
				return;
			}
			PresetValues v = Cfg.Compose(Presets.Get(_preset));
			Shadows.Apply(v);
			Surfaces.Apply(v);
			PostFx.Apply(v);
			DepthOfFieldTuning.Apply(v);
			Vegetation.Apply(v);
			Reflections.Apply(v);
			Water.Apply(v);
			Materials.Apply(v);
			Particles.Apply(v);
		}

		private static void RestoreAll()
		{
			Shadows.Restore();
			Surfaces.Restore();
			PostFx.Restore();
			DepthOfFieldTuning.Restore();
			Vegetation.Restore();
			Reflections.Restore();
			Water.Restore();
			Materials.Restore();
			Particles.Restore();
		}

		private void CyclePreset()
		{
			int num = Array.IndexOf(Presets.Names, _preset);
			Preset = Presets.Names[(num + 1) % Presets.Names.Length];
		}

		private void OnDestroy()
		{
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
			RestoreAll();
		}
	}
	[HarmonyPatch(typeof(GraphicsSettingsManager), "ApplyQualitySettings")]
	internal static class Patch_GraphicsSettingsManager_ApplyQualitySettings
	{
		private static void Postfix()
		{
			Shadows.OnGameWroteSettings();
			VisualsPlugin.MarkDirty();
		}
	}
	internal static class PostFx
	{
		private static PostProcessingProfile _original;

		private static PostProcessingProfile _clone;

		private static PostProcessingBehaviour _behaviour;

		private static string _appliedKey;

		public static void Apply(PresetValues v)
		{
			//IL_0135: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fc: Unknown result type (might be due to invalid IL or missing references)
			if (!v.PostFxEnabled)
			{
				Restore();
				_appliedKey = null;
				return;
			}
			string text = $"{v.AaMode}|{v.UseTaaSharpen}:{v.TaaSharpen}|{v.UseTaaMotionBlending}:{v.TaaMotionBlending}|" + $"{v.Ao}|{v.UseAoIntensity}:{v.AoIntensity}|{v.UseAoRadius}:{v.AoRadius}|" + $"{v.UseAoIntensityFar}:{v.AoIntensityFar}|{v.UseAoFarDistance}:{v.AoFarDistance}|" + $"{v.UseAoSampleCount}:{v.AoSampleCount}|{v.UseAoDownsampling}:{v.AoDownsampling}|" + $"{v.UseAoHighPrecision}:{v.AoHighPrecision}|{v.UseAoAmbientOnly}:{v.AoAmbientOnly}|" + $"{v.UseAoForwardCompatibility}:{v.AoForwardCompatibility}|" + $"{v.Ssr}:{v.SsrIntensity}:{v.SsrResolution}:{v.SsrIterationCount}:{v.SsrMaxDistance}";
			if (_appliedKey != null && _appliedKey != text)
			{
				Restore();
			}
			_appliedKey = text;
			if (EnsureClone())
			{
				ApplyAntialiasing(v);
				ApplyAmbientOcclusion(v);
				ApplyReflections(v);
			}
		}

		private static bool EnsureClone()
		{
			if ((Object)(object)_clone != (Object)null && (Object)(object)_behaviour != (Object)null)
			{
				return true;
			}
			CameraEffects instance = CameraEffects.instance;
			if ((Object)(object)instance == (Object)null)
			{
				return false;
			}
			object? obj = AccessTools.Field(typeof(CameraEffects), "m_postProcessing")?.GetValue(instance);
			_behaviour = (PostProcessingBehaviour)((obj is PostProcessingBehaviour) ? obj : null);
			if ((Object)(object)_behaviour == (Object)null)
			{
				VisualsPlugin.Log.LogWarning((object)"[postfx] CameraEffects has no PostProcessingBehaviour — module disabled");
				return false;
			}
			if ((Object)(object)_behaviour.profile == (Object)null)
			{
				VisualsPlugin.Log.LogWarning((object)"[postfx] behaviour has no profile yet — will retry");
				return false;
			}
			_original = _behaviour.profile;
			_clone = Object.Instantiate<PostProcessingProfile>(_original);
			((Object)_clone).name = ((Object)_original).name + " (ValheimVisuals)";
			_behaviour.profile = _clone;
			return true;
		}

		private static void ApplyAntialiasing(PresetValues v)
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			if (v.AaMode == AaMethod.Vanilla)
			{
				return;
			}
			Settings settings = _clone.antialiasing.settings;
			if (v.AaMode == AaMethod.Fxaa)
			{
				settings.method = (Method)0;
				_clone.antialiasing.settings = settings;
				((PostProcessingModel)_clone.antialiasing).enabled = true;
				return;
			}
			settings.method = (Method)1;
			if (v.UseTaaSharpen)
			{
				settings.taaSettings.sharpen = v.TaaSharpen;
			}
			if (v.UseTaaMotionBlending)
			{
				settings.taaSettings.motionBlending = v.TaaMotionBlending;
			}
			_clone.antialiasing.settings = settings;
			((PostProcessingModel)_clone.antialiasing).enabled = true;
		}

		private static void ApplyAmbientOcclusion(PresetValues v)
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
			if (v.Ao)
			{
				Settings settings = _clone.ambientOcclusion.settings;
				if (v.UseAoIntensity)
				{
					settings.intensity = v.AoIntensity;
				}
				if (v.UseAoRadius)
				{
					settings.radius = v.AoRadius;
				}
				if (v.UseAoIntensityFar)
				{
					settings.intensityFar = v.AoIntensityFar;
				}
				if (v.UseAoFarDistance)
				{
					settings.farDistance = v.AoFarDistance;
				}
				if (v.UseAoSampleCount)
				{
					settings.sampleCount = v.AoSampleCount;
				}
				if (v.UseAoDownsampling)
				{
					settings.downsampling = v.AoDownsampling;
				}
				if (v.UseAoHighPrecision)
				{
					settings.highPrecision = v.AoHighPrecision;
				}
				if (v.UseAoAmbientOnly)
				{
					settings.ambientOnly = v.AoAmbientOnly;
				}
				if (v.UseAoForwardCompatibility)
				{
					settings.forceForwardCompatibility = v.AoForwardCompatibility;
				}
				_clone.ambientOcclusion.settings = settings;
				((PostProcessingModel)_clone.ambientOcclusion).enabled = true;
			}
		}

		private static void ApplyReflections(PresetValues v)
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: 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)
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			if (v.Ssr)
			{
				Settings settings = _clone.screenSpaceReflection.settings;
				settings.reflection.reflectionQuality = v.SsrResolution;
				settings.reflection.blendType = (SSRReflectionBlendType)0;
				settings.reflection.maxDistance = v.SsrMaxDistance;
				settings.reflection.iterationCount = v.SsrIterationCount;
				settings.reflection.stepSize = 3;
				settings.intensity.reflectionMultiplier = v.SsrIntensity;
				settings.intensity.fadeDistance = 100f;
				_clone.screenSpaceReflection.settings = settings;
				((PostProcessingModel)_clone.screenSpaceReflection).enabled = true;
			}
		}

		public static void Restore()
		{
			if (!((Object)(object)_behaviour == (Object)null) && !((Object)(object)_original == (Object)null))
			{
				_behaviour.profile = _original;
				if ((Object)(object)_clone != (Object)null)
				{
					Object.Destroy((Object)(object)_clone);
				}
				_clone = null;
			}
		}
	}
	[HarmonyPatch(typeof(CameraEffects), "ApplySettings")]
	internal static class Patch_CameraEffects_ApplySettings
	{
		private static void Postfix()
		{
			VisualsPlugin.MarkDirty();
		}
	}
	internal enum AaMethod
	{
		Vanilla,
		Taa,
		Fxaa
	}
	internal struct PresetValues
	{
		public string Name;

		public bool ShadowsEnabled;

		public bool UseShadowDistance;

		public float ShadowDistance;

		public bool UseShadowCascades;

		public int ShadowCascades;

		public bool UseCascadeSplit;

		public Vector3 Cascade4Split;

		public bool UseShadowResolution;

		public ShadowResolution Resolution;

		public bool UseShadowProjection;

		public ShadowProjection Projection;

		public bool UseNearPlaneOffset;

		public float NearPlaneOffset;

		public bool UsePixelLightCount;

		public int PixelLightCount;

		public bool SurfacesEnabled;

		public bool ForceAnisotropic;

		public int AnisoForcedLimit;

		public bool UseMipmapLimit;

		public int MipmapLimit;

		public bool UseStreamingMipmapsMemoryBudget;

		public float StreamingMipmapsMemoryBudget;

		public bool PostFxEnabled;

		public AaMethod AaMode;

		public bool UseTaaSharpen;

		public float TaaSharpen;

		public bool UseTaaMotionBlending;

		public float TaaMotionBlending;

		public bool Ao;

		public bool UseAoIntensity;

		public float AoIntensity;

		public bool UseAoRadius;

		public float AoRadius;

		public bool UseAoIntensityFar;

		public float AoIntensityFar;

		public bool UseAoFarDistance;

		public float AoFarDistance;

		public bool UseAoSampleCount;

		public SampleCount AoSampleCount;

		public bool UseAoDownsampling;

		public bool AoDownsampling;

		public bool UseAoHighPrecision;

		public bool AoHighPrecision;

		public bool UseAoAmbientOnly;

		public bool AoAmbientOnly;

		public bool UseAoForwardCompatibility;

		public bool AoForwardCompatibility;

		public bool Ssr;

		public float SsrIntensity;

		public SSRResolution SsrResolution;

		public int SsrIterationCount;

		public float SsrMaxDistance;

		public bool DofEnabled;

		public float DofMaxBlurSize;

		public float DofFocalSize;

		public float DofAperture;

		public bool DofHighResolution;

		public BlurSampleCount DofBlurSampleCount;

		public bool ReflectionsEnabled;

		public bool UseReflectionResolution;

		public int ReflectionResolution;

		public bool ForceRealtimeProbes;

		public bool ParticlesEnabled;

		public bool UseParticleTranslucency;

		public float ParticleTranslucency;

		public bool UseParticleSoftness;

		public float ParticleSoftness;

		public bool UseParticleTessellation;

		public float ParticleTessellation;

		public bool UseParticleLightNormal;

		public float ParticleLightNormal;

		public bool NormalBoostEnabled;

		public float NormalIntensity;

		public bool WaterEnabled;

		public bool UseWaterTessellation;

		public float WaterTessellation;

		public bool UseWaterRefraction;

		public float WaterRefractionScale;

		public float WaterRefractionMax;

		public bool UseWaterNormalPower;

		public float WaterNormalPower;

		public bool UseWaterLodDistance;

		public float WaterLodDistance;

		public bool UseWaterSmoothness;

		public float WaterSmoothness;

		public bool UseWaterMetallic;

		public float WaterMetallic;

		public bool VegetationEnabled;

		public bool UseClutterDistance;

		public float ClutterDistance;

		public bool UseClutterAmount;

		public float ClutterAmount;
	}
	internal static class Presets
	{
		public static readonly string[] Names = new string[2] { "Off", "Physical" };

		private static readonly PresetValues Off = new PresetValues
		{
			Name = "Off"
		};

		public static readonly PresetValues Physical = new PresetValues
		{
			Name = "Physical",
			ShadowsEnabled = true,
			UseShadowDistance = true,
			ShadowDistance = 300f,
			UseShadowCascades = true,
			ShadowCascades = 4,
			UseCascadeSplit = true,
			Cascade4Split = new Vector3(0.04f, 0.12f, 0.32f),
			UseShadowResolution = true,
			Resolution = (ShadowResolution)3,
			UseShadowProjection = true,
			Projection = (ShadowProjection)1,
			UseNearPlaneOffset = true,
			NearPlaneOffset = 2f,
			UsePixelLightCount = false,
			PixelLightCount = 8,
			SurfacesEnabled = true,
			ForceAnisotropic = true,
			AnisoForcedLimit = 16,
			UseMipmapLimit = true,
			MipmapLimit = 0,
			UseStreamingMipmapsMemoryBudget = true,
			StreamingMipmapsMemoryBudget = 4096f,
			PostFxEnabled = true,
			AaMode = AaMethod.Taa,
			UseTaaSharpen = true,
			TaaSharpen = 0.6f,
			UseTaaMotionBlending = true,
			TaaMotionBlending = 0.75f,
			Ao = true,
			UseAoIntensity = true,
			AoIntensity = 0.1f,
			UseAoRadius = false,
			AoRadius = 0.3f,
			UseAoSampleCount = true,
			AoSampleCount = (SampleCount)10,
			UseAoDownsampling = true,
			AoDownsampling = true,
			UseAoHighPrecision = true,
			AoHighPrecision = true,
			UseAoForwardCompatibility = true,
			AoForwardCompatibility = true,
			UseAoAmbientOnly = false,
			AoAmbientOnly = false,
			UseAoIntensityFar = false,
			AoIntensityFar = 2f,
			UseAoFarDistance = false,
			AoFarDistance = 100f,
			Ssr = true,
			SsrIntensity = 2f,
			SsrResolution = (SSRResolution)0,
			SsrIterationCount = 128,
			SsrMaxDistance = 100f,
			DofEnabled = true,
			DofMaxBlurSize = 1.1f,
			DofFocalSize = 0.97f,
			DofAperture = 0.68f,
			DofHighResolution = true,
			DofBlurSampleCount = (BlurSampleCount)2,
			ReflectionsEnabled = true,
			UseReflectionResolution = true,
			ReflectionResolution = 512,
			ForceRealtimeProbes = true,
			NormalBoostEnabled = true,
			NormalIntensity = 1.4f,
			ParticlesEnabled = true,
			UseParticleTranslucency = true,
			ParticleTranslucency = 1f,
			UseParticleSoftness = true,
			ParticleSoftness = 1f,
			UseParticleTessellation = true,
			ParticleTessellation = 3f,
			UseParticleLightNormal = true,
			ParticleLightNormal = 0.5f,
			WaterEnabled = true,
			UseWaterTessellation = true,
			WaterTessellation = 16f,
			UseWaterRefraction = true,
			WaterRefractionScale = 0.3f,
			WaterRefractionMax = 0.05f,
			UseWaterNormalPower = true,
			WaterNormalPower = 0.2f,
			UseWaterLodDistance = true,
			WaterLodDistance = 400f,
			UseWaterSmoothness = false,
			WaterSmoothness = 0.98f,
			UseWaterMetallic = true,
			WaterMetallic = 0.8f,
			VegetationEnabled = true,
			UseClutterDistance = true,
			ClutterDistance = 200f,
			UseClutterAmount = false,
			ClutterAmount = 1f
		};

		public static PresetValues Get(string name)
		{
			if (name == "Physical")
			{
				return Physical;
			}
			return Off;
		}
	}
	internal static class Reflections
	{
		private static readonly Dictionary<ReflectionProbe, int> Original = new Dictionary<ReflectionProbe, int>();

		private static readonly OwnedSetting<bool> Realtime = new OwnedSetting<bool>(() => QualitySettings.realtimeReflectionProbes, delegate(bool x)
		{
			QualitySettings.realtimeReflectionProbes = x;
		});

		public static void Apply(PresetValues v)
		{
			if (!v.ReflectionsEnabled)
			{
				Restore();
				return;
			}
			Realtime.Apply(v.ForceRealtimeProbes, value: true);
			if (!v.UseReflectionResolution || v.ReflectionResolution <= 0)
			{
				RestoreProbes();
				return;
			}
			ReflectionProbe[] array = Object.FindObjectsByType<ReflectionProbe>((FindObjectsInactive)1, (FindObjectsSortMode)0);
			foreach (ReflectionProbe val in array)
			{
				if (!((Object)(object)val == (Object)null))
				{
					if (!Original.ContainsKey(val))
					{
						Original[val] = val.resolution;
					}
					if (val.resolution != v.ReflectionResolution)
					{
						val.resolution = v.ReflectionResolution;
						val.RenderProbe();
					}
				}
			}
		}

		public static void Restore()
		{
			Realtime.Restore();
			RestoreProbes();
		}

		private static void RestoreProbes()
		{
			if (Original.Count == 0)
			{
				return;
			}
			foreach (KeyValuePair<ReflectionProbe, int> item in Original)
			{
				if (!((Object)(object)item.Key == (Object)null))
				{
					item.Key.resolution = item.Value;
					item.Key.RenderProbe();
				}
			}
			Original.Clear();
		}
	}
	internal static class Shadows
	{
		private static readonly OwnedSetting<float> Distance = new OwnedSetting<float>(() => QualitySettings.shadowDistance, delegate(float x)
		{
			QualitySettings.shadowDistance = x;
		});

		private static readonly OwnedSetting<int> Cascades = new OwnedSetting<int>(() => QualitySettings.shadowCascades, delegate(int x)
		{
			QualitySettings.shadowCascades = x;
		});

		private static readonly OwnedSetting<Vector3> Split = new OwnedSetting<Vector3>(() => QualitySettings.shadowCascade4Split, delegate(Vector3 x)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			QualitySettings.shadowCascade4Split = x;
		});

		private static readonly OwnedSetting<ShadowResolution> Resolution = new OwnedSetting<ShadowResolution>(() => QualitySettings.shadowResolution, delegate(ShadowResolution x)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			QualitySettings.shadowResolution = x;
		});

		private static readonly OwnedSetting<ShadowProjection> Projection = new OwnedSetting<ShadowProjection>(() => QualitySettings.shadowProjection, delegate(ShadowProjection x)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			QualitySettings.shadowProjection = x;
		});

		private static readonly OwnedSetting<float> NearOffset = new OwnedSetting<float>(() => QualitySettings.shadowNearPlaneOffset, delegate(float x)
		{
			QualitySettings.shadowNearPlaneOffset = x;
		});

		private static readonly OwnedSetting<int> PixelLights = new OwnedSetting<int>(() => QualitySettings.pixelLightCount, delegate(int x)
		{
			QualitySettings.pixelLightCount = x;
		});

		public static void Apply(PresetValues v)
		{
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			if (!v.ShadowsEnabled)
			{
				Restore();
				return;
			}
			Distance.Apply(v.UseShadowDistance, v.ShadowDistance);
			Cascades.Apply(v.UseShadowCascades, v.ShadowCascades);
			Split.Apply(v.UseCascadeSplit, v.Cascade4Split);
			Resolution.Apply(v.UseShadowResolution, v.Resolution);
			Projection.Apply(v.UseShadowProjection, v.Projection);
			NearOffset.Apply(v.UseNearPlaneOffset, v.NearPlaneOffset);
			PixelLights.Apply(v.UsePixelLightCount && v.PixelLightCount > 0, v.PixelLightCount);
		}

		public static void OnGameWroteSettings()
		{
			Distance.Disown();
			Cascades.Disown();
			Resolution.Disown();
			PixelLights.Disown();
		}

		public static void Restore()
		{
			Distance.Restore();
			Cascades.Restore();
			Split.Restore();
			Resolution.Restore();
			Projection.Restore();
			NearOffset.Restore();
			PixelLights.Restore();
		}
	}
	internal static class Surfaces
	{
		private static readonly OwnedSetting<AnisotropicFiltering> Filtering = new OwnedSetting<AnisotropicFiltering>(() => QualitySettings.anisotropicFiltering, delegate(AnisotropicFiltering x)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			QualitySettings.anisotropicFiltering = x;
		});

		private static readonly OwnedSetting<int> MipmapLimit = new OwnedSetting<int>(() => QualitySettings.globalTextureMipmapLimit, delegate(int x)
		{
			QualitySettings.globalTextureMipmapLimit = x;
		});

		private static readonly OwnedSetting<float> StreamingMipmapsMemoryBudget = new OwnedSetting<float>(() => QualitySettings.streamingMipmapsMemoryBudget, delegate(float x)
		{
			QualitySettings.streamingMipmapsMemoryBudget = x;
		});

		private static bool _forcedLimits;

		public static void Apply(PresetValues v)
		{
			if (!v.SurfacesEnabled)
			{
				Restore();
				return;
			}
			Filtering.Apply(v.ForceAnisotropic, (AnisotropicFiltering)2);
			if (v.ForceAnisotropic)
			{
				Texture.SetGlobalAnisotropicFilteringLimits(v.AnisoForcedLimit, v.AnisoForcedLimit);
				_forcedLimits = true;
			}
			else if (_forcedLimits)
			{
				Texture.SetGlobalAnisotropicFilteringLimits(-1, -1);
				_forcedLimits = false;
			}
			MipmapLimit.Apply(v.UseMipmapLimit, v.MipmapLimit);
			StreamingMipmapsMemoryBudget.Apply(v.UseStreamingMipmapsMemoryBudget, v.StreamingMipmapsMemoryBudget);
		}

		public static void Restore()
		{
			Filtering.Restore();
			MipmapLimit.Restore();
			StreamingMipmapsMemoryBudget.Restore();
			if (_forcedLimits)
			{
				Texture.SetGlobalAnisotropicFilteringLimits(-1, -1);
				_forcedLimits = false;
			}
		}
	}
	internal static class Vegetation
	{
		private static bool _ownsDistance;

		private static float _distance;

		private static bool _ownsAmount;

		private static float _amount;

		public static void Apply(PresetValues v)
		{
			ClutterSystem instance = ClutterSystem.instance;
			if ((Object)(object)instance == (Object)null)
			{
				return;
			}
			if (!v.VegetationEnabled)
			{
				Restore();
				return;
			}
			bool flag = false;
			if (v.UseClutterDistance)
			{
				if (!_ownsDistance)
				{
					_distance = instance.m_distance;
					_ownsDistance = true;
				}
				flag |= Write(ref instance.m_distance, v.ClutterDistance);
			}
			else if (_ownsDistance)
			{
				flag |= Write(ref instance.m_distance, _distance);
				_ownsDistance = false;
			}
			if (v.UseClutterAmount)
			{
				if (!_ownsAmount)
				{
					_amount = instance.m_amountScale;
					_ownsAmount = true;
				}
				flag |= Write(ref instance.m_amountScale, v.ClutterAmount);
			}
			else if (_ownsAmount)
			{
				flag |= Write(ref instance.m_amountScale, _amount);
				_ownsAmount = false;
			}
			if (flag)
			{
				ForceRebuild(instance);
			}
		}

		private static bool Write(ref float field, float value)
		{
			if (Mathf.Approximately(field, value))
			{
				return false;
			}
			field = value;
			return true;
		}

		public static void Restore()
		{
			ClutterSystem instance = ClutterSystem.instance;
			if ((Object)(object)instance == (Object)null)
			{
				_ownsDistance = false;
				_ownsAmount = false;
				return;
			}
			bool flag = false;
			if (_ownsDistance)
			{
				flag |= Write(ref instance.m_distance, _distance);
				_ownsDistance = false;
			}
			if (_ownsAmount)
			{
				flag |= Write(ref instance.m_amountScale, _amount);
				_ownsAmount = false;
			}
			if (flag)
			{
				ForceRebuild(instance);
			}
		}

		private static void ForceRebuild(ClutterSystem cs)
		{
			FieldInfo fieldInfo = AccessTools.Field(typeof(ClutterSystem), "m_forceRebuild");
			if (fieldInfo == null)
			{
				VisualsPlugin.Log.LogWarning((object)"[vegetation] m_forceRebuild not found — grass will only change after moving away and back");
			}
			else
			{
				fieldInfo.SetValue(cs, true);
			}
		}
	}
	internal static class Water
	{
		private struct Saved
		{
			public float Tess;

			public float RefractionScale;

			public float RefractionMax;

			public float NormalPower;

			public float VisibleMaxDistance;

			public float Glossiness;

			public float Metallic;
		}

		private static readonly Dictionary<Material, Saved> Original = new Dictionary<Material, Saved>();

		private static readonly int TessId = Shader.PropertyToID("_Tess");

		private static readonly int RefractionScaleId = Shader.PropertyToID("_RefractionScale");

		private static readonly int RefractionMaxId = Shader.PropertyToID("_RefractionMax");

		private static readonly int NormalPowerId = Shader.PropertyToID("_NormalPower");

		private static readonly int VisibleMaxDistanceId = Shader.PropertyToID("_VisibleMaxDistance");

		private static readonly int GlossinessId = Shader.PropertyToID("_Glossiness");

		private static readonly int MetallicId = Shader.PropertyToID("_Metallic");

		public static void Apply(PresetValues v)
		{
			if (!v.WaterEnabled)
			{
				Restore();
				return;
			}
			foreach (Material item in Collect())
			{
				if (!Original.TryGetValue(item, out var value))
				{
					value = new Saved
					{
						Tess = item.GetFloat(TessId),
						RefractionScale = item.GetFloat(RefractionScaleId),
						RefractionMax = item.GetFloat(RefractionMaxId),
						NormalPower = item.GetFloat(NormalPowerId),
						VisibleMaxDistance = item.GetFloat(VisibleMaxDistanceId),
						Glossiness = item.GetFloat(GlossinessId),
						Metallic = item.GetFloat(MetallicId)
					};
					Original[item] = value;
				}
				item.SetFloat(TessId, v.UseWaterTessellation ? Mathf.Clamp(v.WaterTessellation, 1f, 32f) : value.Tess);
				item.SetFloat(RefractionScaleId, v.UseWaterRefraction ? v.WaterRefractionScale : value.RefractionScale);
				item.SetFloat(RefractionMaxId, v.UseWaterRefraction ? v.WaterRefractionMax : value.RefractionMax);
				item.SetFloat(NormalPowerId, v.UseWaterNormalPower ? Mathf.Clamp(v.WaterNormalPower, 0f, 5f) : value.NormalPower);
				item.SetFloat(VisibleMaxDistanceId, (v.UseWaterLodDistance && v.WaterLodDistance > 0f) ? v.WaterLodDistance : value.VisibleMaxDistance);
				item.SetFloat(GlossinessId, v.UseWaterSmoothness ? Mathf.Clamp01(v.WaterSmoothness) : value.Glossiness);
				item.SetFloat(MetallicId, (v.UseWaterMetallic && v.WaterMetallic >= 0f) ? Mathf.Clamp01(v.WaterMetallic) : value.Metallic);
			}
		}

		private static IEnumerable<Material> Collect()
		{
			HashSet<Material> seen = new HashSet<Material>();
			List<WaterVolume> volumes = WaterVolume.Instances;
			for (int i = 0; i < volumes.Count; i++)
			{
				WaterVolume val = volumes[i];
				if (!((Object)(object)val == (Object)null) && !((Object)(object)val.m_waterSurface == (Object)null))
				{
					Material sharedMaterial = ((Renderer)val.m_waterSurface).sharedMaterial;
					if (!((Object)(object)sharedMaterial == (Object)null) && !((Object)(object)sharedMaterial.shader == (Object)null) && sharedMaterial.HasFloat(TessId) && seen.Add(sharedMaterial))
					{
						yield return sharedMaterial;
					}
				}
			}
		}

		public static void Restore()
		{
			foreach (KeyValuePair<Material, Saved> item in Original)
			{
				Material key = item.Key;
				if (!((Object)(object)key == (Object)null))
				{
					key.SetFloat(TessId, item.Value.Tess);
					key.SetFloat(RefractionScaleId, item.Value.RefractionScale);
					key.SetFloat(RefractionMaxId, item.Value.RefractionMax);
					key.SetFloat(NormalPowerId, item.Value.NormalPower);
					key.SetFloat(VisibleMaxDistanceId, item.Value.VisibleMaxDistance);
					key.SetFloat(GlossinessId, item.Value.Glossiness);
					key.SetFloat(MetallicId, item.Value.Metallic);
				}
			}
			Original.Clear();
		}
	}
}