Decompiled source of ShieldSledding v1.0.9

ShieldSledding.dll

Decompiled a month ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: IgnoresAccessChecksTo("assembly_guiutils")]
[assembly: IgnoresAccessChecksTo("assembly_valheim")]
[assembly: AssemblyCompany("ShieldSledding")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.9.0")]
[assembly: AssemblyInformationalVersion("1.0.9")]
[assembly: AssemblyProduct("ShieldSledding")]
[assembly: AssemblyTitle("ShieldSledding")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.9.0")]
[module: UnverifiableCode]
[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 ShieldSledding
{
	internal sealed class AudioController
	{
		private AudioSource _slideSource;

		private AudioSource _windSource;

		private float _lastImpactTime;

		private static AudioClip _slideClip;

		private static AudioClip _windClip;

		private static AudioClip _impactClip;

		internal static AudioController Instance { get; private set; }

		internal AudioController()
		{
			Instance = this;
		}

		internal static void Prewarm()
		{
			if (!Utility.IsDedicatedServer())
			{
				EnsureClips();
			}
		}

		internal void Begin(Player player)
		{
			EnsureSources(player);
		}

		internal void End(Player player)
		{
			if ((Object)(object)_slideSource != (Object)null)
			{
				_slideSource.Stop();
			}
			if ((Object)(object)_windSource != (Object)null)
			{
				_windSource.Stop();
			}
		}

		internal void Update(Player player, float speed)
		{
			if (!ConfigManager.EnableSoundEffects.Value || (Object)(object)player == (Object)null || Utility.IsDedicatedServer())
			{
				if ((Object)(object)_slideSource != (Object)null && _slideSource.isPlaying)
				{
					_slideSource.Stop();
				}
				if ((Object)(object)_windSource != (Object)null && _windSource.isPlaying)
				{
					_windSource.Stop();
				}
				return;
			}
			EnsureSources(player);
			float num = 12f;
			if (Utility.TrySampleGround(player, out var sample))
			{
				num = Mathf.Max(6f, ShieldSpeedRegistry.EstimateNaturalSpeed(in sample));
			}
			float num2 = Mathf.Clamp01(speed / num);
			if ((Object)(object)_slideSource != (Object)null)
			{
				if (speed > 1.5f)
				{
					if (!_slideSource.isPlaying)
					{
						_slideSource.Play();
					}
					_slideSource.volume = 0.15f + num2 * 0.45f;
					_slideSource.pitch = 0.85f + num2 * 0.45f;
				}
				else
				{
					_slideSource.Stop();
				}
			}
			if (!((Object)(object)_windSource != (Object)null))
			{
				return;
			}
			if (speed > 6f)
			{
				if (!_windSource.isPlaying)
				{
					_windSource.Play();
				}
				_windSource.volume = num2 * 0.4f;
				_windSource.pitch = 0.95f + num2 * 0.7f;
			}
			else
			{
				_windSource.Stop();
			}
		}

		internal void OnLanding(Player player, float impact)
		{
			PlayImpact(player, impact);
		}

		internal void PlayImpact(Player player, float impact)
		{
			if (ConfigManager.EnableSoundEffects.Value && !((Object)(object)player == (Object)null) && !(impact < 4f) && !(Time.time - _lastImpactTime < 0.2f))
			{
				_lastImpactTime = Time.time;
				EnsureSources(player);
				if ((Object)(object)_slideSource != (Object)null && (Object)(object)_impactClip != (Object)null)
				{
					_slideSource.PlayOneShot(_impactClip, Mathf.Clamp01(impact / 20f) * 0.85f);
				}
			}
		}

		private void EnsureSources(Player player)
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Expected O, but got Unknown
			EnsureClips();
			if (!((Object)(object)_slideSource != (Object)null))
			{
				GameObject val = new GameObject("ShieldSledding_Audio");
				val.transform.SetParent(((Component)player).transform, false);
				_slideSource = val.AddComponent<AudioSource>();
				_slideSource.loop = true;
				_slideSource.spatialBlend = 0f;
				_slideSource.playOnAwake = false;
				_slideSource.clip = _slideClip;
				_slideSource.volume = 0.4f;
				_windSource = val.AddComponent<AudioSource>();
				_windSource.loop = true;
				_windSource.spatialBlend = 0f;
				_windSource.playOnAwake = false;
				_windSource.clip = _windClip;
				_windSource.volume = 0.25f;
				_windSource.pitch = 1.1f;
			}
		}

		private static void EnsureClips()
		{
			if ((Object)(object)_slideClip == (Object)null)
			{
				_slideClip = CreateNoiseClip("ShieldSled_Slide", 0.55f, 0.35f, soft: true);
			}
			if ((Object)(object)_windClip == (Object)null)
			{
				_windClip = CreateNoiseClip("ShieldSled_Wind", 0.8f, 0.22f, soft: true);
			}
			if ((Object)(object)_impactClip == (Object)null)
			{
				_impactClip = CreateNoiseClip("ShieldSled_Impact", 0.12f, 0.85f, soft: false);
			}
		}

		private static AudioClip CreateNoiseClip(string name, float seconds, float amplitude, bool soft)
		{
			int num = 22050;
			int num2 = Mathf.Max(256, Mathf.RoundToInt((float)num * seconds));
			float[] array = new float[num2];
			float num3 = 0f;
			for (int i = 0; i < num2; i++)
			{
				float num4 = (float)i / (float)(num2 - 1);
				float num5 = (soft ? 1f : (Mathf.Exp((0f - num4) * 14f) * (1f - num4)));
				float num6 = Random.value * 2f - 1f;
				float num7 = (soft ? ((num3 + 0.02f * num6) / 1.02f) : num6);
				num3 = num7;
				array[i] = num7 * amplitude * num5;
			}
			AudioClip obj = AudioClip.Create(name, num2, 1, num, false);
			if (!TrySetClipData(obj, array))
			{
				Plugin.Log.LogWarning((object)"Shield Sledding: AudioClip.SetData unavailable — using empty baked clip.");
			}
			return obj;
		}

		private static bool TrySetClipData(AudioClip clip, float[] buffer)
		{
			if ((Object)(object)clip == (Object)null || buffer == null)
			{
				return false;
			}
			try
			{
				MethodInfo method = typeof(AudioClip).GetMethod("SetData", new Type[2]
				{
					typeof(float[]),
					typeof(int)
				});
				if (method == null)
				{
					return false;
				}
				return !(method.Invoke(clip, new object[2] { buffer, 0 }) is bool flag) || flag;
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Shield Sledding: AudioClip.SetData failed — " + ex.Message));
				return false;
			}
		}
	}
	internal sealed class CollisionController
	{
		private Vector3 _lastVelocity;

		private float _cooldownUntil;

		internal void Begin(Player player)
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			_lastVelocity = (((Object)(object)player != (Object)null) ? ((Character)player).GetVelocity() : Vector3.zero);
			_cooldownUntil = 0f;
		}

		internal void Tick(Player player, ItemData shield, PhysicsResult result)
		{
			//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_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)player == (Object)null)
			{
				return;
			}
			WildlifeImpact.TryRam(player, shield, result);
			if (Time.time < _cooldownUntil)
			{
				_lastVelocity = result.Velocity;
				return;
			}
			float horizontalSpeed = result.HorizontalSpeed;
			if (horizontalSpeed < ConfigManager.CollisionSpeedThreshold.Value)
			{
				_lastVelocity = result.Velocity;
				return;
			}
			Vector3 val = Utility.Flatten(result.Velocity - _lastVelocity);
			float magnitude = ((Vector3)(ref val)).magnitude;
			if (magnitude < 4f)
			{
				_lastVelocity = result.Velocity;
				return;
			}
			if (!TryFindObstacle(player, out var hit))
			{
				_lastVelocity = result.Velocity;
				return;
			}
			float impact = magnitude * ConfigManager.CollisionSpeedDamageScale.Value;
			ApplyImpact(player, shield, ((RaycastHit)(ref hit)).point, ((RaycastHit)(ref hit)).normal, impact, horizontalSpeed);
			_cooldownUntil = Time.time + 0.35f;
			_lastVelocity = result.Velocity;
		}

		internal void OnLanding(Player player, float impact)
		{
			if (!(impact < 8f) && Utility.IsLocalPlayer(player))
			{
				EffectController.Instance?.ShakeCamera(impact * ConfigManager.CameraShakeStrength.Value * 0.05f);
				AudioController.Instance?.PlayImpact(player, impact);
			}
		}

		private bool TryFindObstacle(Player player, out RaycastHit hit)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: 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_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			hit = default(RaycastHit);
			Vector3 val = ((Component)player).transform.position + Vector3.up * 0.6f;
			Vector3 val2 = Utility.Flatten((ShieldSledController.Instance != null) ? ShieldSledController.Instance.SimulatedVelocity : ((Character)player).GetVelocity());
			if (((Vector3)(ref val2)).sqrMagnitude < 0.01f)
			{
				val2 = ((Component)player).transform.forward;
			}
			((Vector3)(ref val2)).Normalize();
			return Physics.SphereCast(val, 0.4f, val2, ref hit, 1.2f, -1);
		}

		private void ApplyImpact(Player player, ItemData shield, Vector3 point, Vector3 normal, float impact, float speed)
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: 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)
			EffectController.Instance?.OnCollision(player, point, normal, impact);
			AudioController.Instance?.PlayImpact(player, impact);
			MultiplayerSync.Instance?.SendImpactEvent(player, point, impact);
		}
	}
	internal static class ConfigManager
	{
		internal static ConfigEntry<bool> EnableShieldSledding;

		internal static ConfigEntry<KeyboardShortcut> ActivationKey;

		internal static ConfigEntry<float> ActivationWindow;

		internal static ConfigEntry<float> ArmedWindow;

		internal static ConfigEntry<float> MinimumSlope;

		internal static ConfigEntry<bool> ShowActivationHints;

		internal static ConfigEntry<float> SledGroundClearance;

		internal static ConfigEntry<bool> EnableWaterSledding;

		internal static ConfigEntry<float> WaterSledMinSpeed;

		internal static ConfigEntry<float> WaterSledSustainSpeed;

		internal static ConfigEntry<float> WaterSledFriction;

		internal static ConfigEntry<float> MinimumSledSpeed;

		internal static ConfigEntry<float> BaseAcceleration;

		internal static ConfigEntry<float> MaximumSpeed;

		internal static ConfigEntry<float> MinBlockSkillSpeedScale;

		internal static ConfigEntry<float> MaxBlockSkillSpeedScale;

		internal static ConfigEntry<float> GravityMultiplier;

		internal static ConfigEntry<float> FrictionMultiplier;

		internal const float CoastFrictionScale = 0.28f;

		internal const float CoastFlatDecelScale = 0.35f;

		internal static ConfigEntry<float> AirControl;

		internal static ConfigEntry<float> SteeringStrength;

		internal static ConfigEntry<bool> InvertSteering;

		internal static ConfigEntry<bool> CameraRelativeSteering;

		internal static ConfigEntry<float> BrakeStrength;

		internal static ConfigEntry<float> ForwardLeanBonus;

		internal static ConfigEntry<float> SteeringSpeedFalloff;

		internal static ConfigEntry<float> UphillPenalty;

		internal static ConfigEntry<float> SlopeTransitionRetention;

		internal const float ClimbFrictionScale = 0.38f;

		internal static ConfigEntry<float> FlatDeceleration;

		internal static ConfigEntry<bool> AbsorbDamageToShield;

		internal static ConfigEntry<bool> EndSledOnCombatHit;

		internal static ConfigEntry<bool> StaggerOnCombatHit;

		internal static ConfigEntry<float> CombatHitMinDamage;

		internal static ConfigEntry<bool> EnableDurabilityLoss;

		internal static ConfigEntry<float> DurabilityPerMeter;

		internal static ConfigEntry<float> DurabilityPerSecond;

		internal static ConfigEntry<float> LandingDamageMultiplier;

		internal static ConfigEntry<float> DamageToDurabilityMultiplier;

		internal static ConfigEntry<bool> StumbleOnShieldBreak;

		internal static ConfigEntry<float> SnowFriction;

		internal static ConfigEntry<float> GrassFriction;

		internal static ConfigEntry<float> BlackForestFriction;

		internal static ConfigEntry<float> SwampFriction;

		internal static ConfigEntry<float> AshlandsFriction;

		internal static ConfigEntry<float> MountainFriction;

		internal static ConfigEntry<float> MeadowsFriction;

		internal static ConfigEntry<float> MistlandsFriction;

		internal static ConfigEntry<float> OceanFriction;

		internal static ConfigEntry<float> DeepNorthFriction;

		internal static ConfigEntry<float> AshlandsDurabilityWearMultiplier;

		internal static ConfigEntry<bool> EnableShieldSurf;

		internal static ConfigEntry<float> ShieldSurfFriction;

		internal static ConfigEntry<float> CollisionSpeedThreshold;

		internal static ConfigEntry<float> CollisionSpeedDamageScale;

		internal static ConfigEntry<bool> EnableWildlifeImpactKill;

		internal static ConfigEntry<SledStaminaMode> StaminaMode;

		internal static ConfigEntry<float> StaminaActivationCost;

		internal static ConfigEntry<float> StaminaPerSecond;

		internal static ConfigEntry<float> StaminaJumpCost;

		internal static ConfigEntry<bool> EnableParticles;

		internal static ConfigEntry<bool> EnableCameraEffects;

		internal static ConfigEntry<bool> EnableSoundEffects;

		internal static ConfigEntry<bool> EnableSpeedometer;

		internal static ConfigEntry<float> CameraShakeStrength;

		internal static ConfigEntry<float> SpeedFovIncrease;

		internal static ConfigEntry<float> CameraTiltStrength;

		internal static ConfigEntry<bool> EnableShieldSparks;

		internal static ConfigEntry<int> ConfigSchemaVersion;

		private static ConfigDescription FloatRange(float min, float max, string description = "")
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Expected O, but got Unknown
			return new ConfigDescription(description, (AcceptableValueBase)(object)new AcceptableValueRange<float>(min, max), Array.Empty<object>());
		}

		private static ConfigDescription IntRange(int min, int max, string description = "")
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Expected O, but got Unknown
			return new ConfigDescription(description, (AcceptableValueBase)(object)new AcceptableValueRange<int>(min, max), Array.Empty<object>());
		}

		internal static void Init(ConfigFile config)
		{
			//IL_0048: 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)
			//IL_008f: Expected O, but got Unknown
			//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c7: Expected O, but got Unknown
			//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ff: Expected O, but got Unknown
			//IL_0148: Unknown result type (might be due to invalid IL or missing references)
			//IL_0152: Expected O, but got Unknown
			//IL_019b: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a5: Expected O, but got Unknown
			//IL_01d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01dd: Expected O, but got Unknown
			//IL_020b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0215: Expected O, but got Unknown
			//IL_0243: Unknown result type (might be due to invalid IL or missing references)
			//IL_024d: Expected O, but got Unknown
			//IL_027b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0285: Expected O, but got Unknown
			//IL_02b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_02bd: Expected O, but got Unknown
			//IL_02eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f5: Expected O, but got Unknown
			//IL_0323: Unknown result type (might be due to invalid IL or missing references)
			//IL_032d: Expected O, but got Unknown
			//IL_035b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0365: Expected O, but got Unknown
			//IL_0393: Unknown result type (might be due to invalid IL or missing references)
			//IL_039d: Expected O, but got Unknown
			//IL_03cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_03d5: Expected O, but got Unknown
			//IL_0403: Unknown result type (might be due to invalid IL or missing references)
			//IL_040d: Expected O, but got Unknown
			//IL_0471: Unknown result type (might be due to invalid IL or missing references)
			//IL_047b: Expected O, but got Unknown
			//IL_04a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_04b3: Expected O, but got Unknown
			//IL_04e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_04eb: Expected O, but got Unknown
			//IL_0519: Unknown result type (might be due to invalid IL or missing references)
			//IL_0523: Expected O, but got Unknown
			//IL_0551: Unknown result type (might be due to invalid IL or missing references)
			//IL_055b: Expected O, but got Unknown
			//IL_0589: Unknown result type (might be due to invalid IL or missing references)
			//IL_0593: Expected O, but got Unknown
			//IL_0612: Unknown result type (might be due to invalid IL or missing references)
			//IL_061c: Expected O, but got Unknown
			//IL_064a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0654: Expected O, but got Unknown
			//IL_0985: Unknown result type (might be due to invalid IL or missing references)
			//IL_098f: Expected O, but got Unknown
			//IL_09bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_09c7: Expected O, but got Unknown
			ConfigSchemaVersion = config.Bind<int>("General", "ConfigSchemaVersion", 1, "Internal config migration version. Do not edit unless you know what you are doing.");
			EnableShieldSledding = config.Bind<bool>("General", "EnableShieldSledding", true, "Master toggle for shield sledding.");
			ActivationKey = config.Bind<KeyboardShortcut>("General", "ActivationKey", new KeyboardShortcut((KeyCode)103, Array.Empty<KeyCode>()), "Press while airborne after a jump to arm shield sled mode.");
			ActivationWindow = config.Bind<float>("General", "ActivationWindow", 2.5f, new ConfigDescription("Seconds after jumping during which G can arm sled mode.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 5f), Array.Empty<object>()));
			ArmedWindow = config.Bind<float>("General", "ArmedWindow", 6f, new ConfigDescription("Seconds after arming to land and start sledding before the arm expires.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 15f), Array.Empty<object>()));
			MinimumSlope = config.Bind<float>("General", "MinimumSlope", 8f, new ConfigDescription("Minimum ground slope in degrees required to start sledding on landing.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 45f), Array.Empty<object>()));
			ShowActivationHints = config.Bind<bool>("General", "ShowActivationHints", true, "Show on-screen center messages for arming, flat landing, and other sled hints.");
			SledGroundClearance = config.Bind<float>("Physics", "SledGroundClearance", 0f, new ConfigDescription("Target height of the player's feet above the slope while sledding (meters). 0 = full contact.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 0.6f), Array.Empty<object>()));
			EnableWaterSledding = config.Bind<bool>("Physics", "EnableWaterSledding", true, "Allow sledding across water when moving fast enough.");
			WaterSledMinSpeed = config.Bind<float>("Physics", "WaterSledMinSpeed", 6f, new ConfigDescription("Minimum speed (m/s) to START water skim. Clamped up to WaterSledSustainSpeed so skim cannot engage below the cancel floor.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 20f), Array.Empty<object>()));
			WaterSledSustainSpeed = config.Bind<float>("Physics", "WaterSledSustainSpeed", 6f, new ConfigDescription("ONLY water speed restriction while skimming: end skim when horizontal speed falls below this (m/s). Default 6. Short flicker grace only — cancel should read ~6, not ~3.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(2f, 25f), Array.Empty<object>()));
			WaterSledFriction = config.Bind<float>("Physics", "WaterSledFriction", 0.4f, new ConfigDescription("Friction while skimming water only (not multiplied by OceanFriction). Lower = longer skim. No W-propulsion on water — skim must coast in.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.05f, 2f), Array.Empty<object>()));
			MinimumSledSpeed = config.Bind<float>("Physics", "MinimumSledSpeed", 4f, new ConfigDescription("Minimum horizontal speed (m/s) required to stay in sledding on ground. Drop below this (with a short grace) and sledding ends. 0 = disabled. Skipped while water-skimming.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 20f), Array.Empty<object>()));
			BaseAcceleration = config.Bind<float>("Physics", "BaseAcceleration", 10f, new ConfigDescription("Slope pull strength (~10 ≈ gravity). Higher = faster coast on the same hill.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 100f), Array.Empty<object>()));
			MaximumSpeed = config.Bind<float>("Physics", "MaximumSpeed", 0f, new ConfigDescription("Optional safety speed cap in m/s. 0 = disabled — speed follows slope steepness and terrain friction.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 80f), Array.Empty<object>()));
			MinBlockSkillSpeedScale = config.Bind<float>("Physics", "BlockSkillSpeedScaleMin", 0.8f, new ConfigDescription("Blocking skill speed scale at skill 0. Lerps to BlockSkillSpeedScaleMax at Blocking 100. Affects slope pull, coast speed, and the optional MaximumSpeed cap.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.35f, 3f), Array.Empty<object>()));
			MaxBlockSkillSpeedScale = config.Bind<float>("Physics", "BlockSkillSpeedScaleMax", 1.7f, new ConfigDescription("Blocking skill speed scale at skill 100. Lerps from BlockSkillSpeedScaleMin at Blocking 0.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 3f), Array.Empty<object>()));
			GravityMultiplier = config.Bind<float>("Physics", "GravityMultiplier", 1f, new ConfigDescription("Multiplier applied to downhill gravity acceleration.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 5f), Array.Empty<object>()));
			FrictionMultiplier = config.Bind<float>("Physics", "FrictionMultiplier", 1f, new ConfigDescription("Global friction multiplier. Always applied (including downhill). Free-coast uses a lighter fraction so glide stays long; braking uses full strength. Biome/shield frictions still scale with this.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 5f), Array.Empty<object>()));
			AirControl = config.Bind<float>("Physics", "AirControl", 0.15f, new ConfigDescription("Fraction of ground steering applied while airborne.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
			SteeringStrength = config.Bind<float>("Physics", "SteeringStrength", 4.75f, new ConfigDescription("How quickly velocity direction can change while sledding.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 10f), Array.Empty<object>()));
			InvertSteering = config.Bind<bool>("Physics", "InvertSteering", false, "Swap A/D while sledding. Leave false for natural left/right; enable if steering feels backwards.");
			CameraRelativeSteering = config.Bind<bool>("Physics", "CameraRelativeSteering", true, "When true, A/D lean uses screen left/right when the camera has a clear lateral view of the slide; sideways freelook and slope-align keep sled/body left/right. Looking against travel stays sled-natural (no lean swap). When false, A/D always turn relative to slide direction.");
			BrakeStrength = config.Bind<float>("Physics", "BrakeStrength", 1.5f, new ConfigDescription("Extra friction multiplier when holding S.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 10f), Array.Empty<object>()));
			ForwardLeanBonus = config.Bind<float>("Physics", "ForwardLeanBonus", 1.5f, new ConfigDescription("Extra acceleration when holding W on real downhill slopes. No effect on flat / near-flat ground (cannot sustain sledding forever with Shift+W).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 30f), Array.Empty<object>()));
			SteeringSpeedFalloff = config.Bind<float>("Physics", "SteeringSpeedFalloff", 0.035f, new ConfigDescription("Higher values reduce steering responsiveness at speed.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
			UphillPenalty = config.Bind<float>("Physics", "UphillPenalty", 0.75f, new ConfigDescription("Uphill deceleration relative to downhill slope pull (1 = mirror gravity × grade). Scaled by slope steepness. Lower = more climb carry from downhill runs.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.25f, 10f), Array.Empty<object>()));
			SlopeTransitionRetention = config.Bind<float>("Physics", "SlopeTransitionRetention", 0.9f, new ConfigDescription("How much speed to keep when the ground normal changes (valleys / downhill→uphill). 1 = full retain, 0 = raw ProjectOnPlane bleed.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
			FlatDeceleration = config.Bind<float>("Physics", "FlatDeceleration", 8f, new ConfigDescription("Deceleration on flat ground (m/s² scale). Free-coast applies a lighter fraction; braking uses full strength.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 30f), Array.Empty<object>()));
			AbsorbDamageToShield = config.Bind<bool>("Damage", "AbsorbDamageToShield", true, "When true, fall and impact damage go to shield durability instead of player HP while sledding.");
			EndSledOnCombatHit = config.Bind<bool>("Damage", "EndSledOnCombatHit", true, "When true, creature or player attacks knock you off the sled while sledding.");
			StaggerOnCombatHit = config.Bind<bool>("Damage", "StaggerOnCombatHit", true, "Apply a stagger when knocked off the sled by a creature or player attack.");
			CombatHitMinDamage = config.Bind<float>("Damage", "CombatHitMinDamage", 1f, new ConfigDescription("Minimum damage before a combat hit can knock you off the sled.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 50f), Array.Empty<object>()));
			DamageToDurabilityMultiplier = config.Bind<float>("Damage", "DamageToDurabilityMultiplier", 0.15f, new ConfigDescription("Shield durability lost per point of HP damage absorbed while sledding.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 5f), Array.Empty<object>()));
			EnableDurabilityLoss = config.Bind<bool>("Durability", "EnableDurabilityLoss", true, "Whether sledding wears down the equipped shield.");
			DurabilityPerMeter = config.Bind<float>("Durability", "DurabilityPerMeter", 0.02f, FloatRange(0f, 5f));
			DurabilityPerSecond = config.Bind<float>("Durability", "DurabilityPerSecond", 0.05f, FloatRange(0f, 5f));
			LandingDamageMultiplier = config.Bind<float>("Durability", "LandingDamageMultiplier", 1.5f, FloatRange(0f, 10f));
			StumbleOnShieldBreak = config.Bind<bool>("Durability", "StumbleOnShieldBreak", true, "Apply a stagger when the shield breaks from sledding.");
			SnowFriction = config.Bind<float>("Terrain", "SnowFriction", 0.55f, FloatRange(0.1f, 3f, "Snow-surface friction blended with MountainFriction / DeepNorthFriction."));
			GrassFriction = config.Bind<float>("Terrain", "GrassFriction", 0.85f, FloatRange(0.1f, 3f));
			BlackForestFriction = config.Bind<float>("Terrain", "BlackForestFriction", 0.95f, FloatRange(0.1f, 3f));
			SwampFriction = config.Bind<float>("Terrain", "SwampFriction", 1.45f, FloatRange(0.1f, 3f));
			AshlandsFriction = config.Bind<float>("Terrain", "AshlandsFriction", 0.75f, FloatRange(0.1f, 3f));
			MountainFriction = config.Bind<float>("Terrain", "MountainFriction", 0.5f, FloatRange(0.1f, 3f));
			MeadowsFriction = config.Bind<float>("Terrain", "MeadowsFriction", 1f, FloatRange(0.1f, 3f));
			MistlandsFriction = config.Bind<float>("Terrain", "MistlandsFriction", 0.9f, FloatRange(0.1f, 3f));
			OceanFriction = config.Bind<float>("Terrain", "OceanFriction", 1.2f, FloatRange(0.1f, 3f));
			DeepNorthFriction = config.Bind<float>("Terrain", "DeepNorthFriction", 0.5f, FloatRange(0.1f, 3f));
			AshlandsDurabilityWearMultiplier = config.Bind<float>("Terrain", "AshlandsDurabilityWearMultiplier", 1.35f, FloatRange(1f, 5f));
			EnableShieldSurf = config.Bind<bool>("Terrain", "EnableShieldSurf", true, "Reduced friction when sledding on player-built pieces / wood ramps (piece layer, wood WearNTear, ramp-like names).");
			ShieldSurfFriction = config.Bind<float>("Terrain", "ShieldSurfFriction", 0.4f, FloatRange(0.1f, 3f, "Friction multiplier on built wood/piece surfaces. Lower = faster (0.4 default)."));
			CollisionSpeedThreshold = config.Bind<float>("Impacts", "CollisionSpeedThreshold", 6f, new ConfigDescription("Minimum speed (m/s) before impact camera/audio effects trigger.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 30f), Array.Empty<object>()));
			CollisionSpeedDamageScale = config.Bind<float>("Impacts", "CollisionSpeedDamageScale", 1.25f, new ConfigDescription("Impact intensity scale for camera shake and sound.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 5f), Array.Empty<object>()));
			EnableWildlifeImpactKill = config.Bind<bool>("Impacts", "EnableWildlifeImpactKill", true, "Instantly kill small wildlife when sledding into them at or above CollisionSpeedThreshold (Boar, Deer, Greyling, Neck, Crow, Gull, Hare, Ash crow).");
			StaminaMode = config.Bind<SledStaminaMode>("Stamina", "StaminaMode", SledStaminaMode.None, "Stamina drain behavior while sledding.");
			StaminaActivationCost = config.Bind<float>("Stamina", "StaminaActivationCost", 10f, FloatRange(0f, 100f));
			StaminaPerSecond = config.Bind<float>("Stamina", "StaminaPerSecond", 2f, FloatRange(0f, 50f));
			StaminaJumpCost = config.Bind<float>("Stamina", "StaminaJumpCost", 8f, FloatRange(0f, 100f));
			EnableParticles = config.Bind<bool>("Effects", "EnableParticles", true, (ConfigDescription)null);
			EnableCameraEffects = config.Bind<bool>("Effects", "EnableCameraEffects", true, "Master switch for sled camera FOV boost, tilt, and shake.");
			EnableSoundEffects = config.Bind<bool>("Effects", "EnableSoundEffects", true, "Slide / wind / impact audio while sledding.");
			EnableSpeedometer = config.Bind<bool>("Effects", "EnableSpeedometer", false, (ConfigDescription)null);
			CameraShakeStrength = config.Bind<float>("Effects", "CameraShakeStrength", 0.35f, FloatRange(0f, 2f, "Impact camera shake scale. Requires EnableCameraEffects=true."));
			SpeedFovIncrease = config.Bind<float>("Effects", "SpeedFovIncrease", 8f, FloatRange(0f, 30f, "Extra FOV (degrees) at full sled speed. Requires EnableCameraEffects=true. Restores when you exit the sled."));
			CameraTiltStrength = config.Bind<float>("Effects", "CameraTiltStrength", 4f, FloatRange(0f, 20f, "Steer roll (degrees) while sledding. Requires EnableCameraEffects=true."));
			EnableShieldSparks = config.Bind<bool>("Effects", "EnableShieldSparks", true, "Spawn brief spark bursts on collisions / wildlife impacts. Also requires EnableParticles.");
			ShieldWearConfig.Init(config);
			MigrateLegacyConfig(config);
		}

		private static void MigrateLegacyConfig(ConfigFile config)
		{
			if (ConfigSchemaVersion.Value < 27)
			{
				MigrateRenamedFloat(config, "Physics", "MinBlockSkillSpeedScale", MinBlockSkillSpeedScale);
				MigrateRenamedFloat(config, "Physics", "MaxBlockSkillSpeedScale", MaxBlockSkillSpeedScale);
			}
			if (ConfigSchemaVersion.Value < 2)
			{
				if (ActivationWindow.Value <= 0.6f)
				{
					ActivationWindow.Value = 2.5f;
					Plugin.Log.LogInfo((object)"Shield Sledding: raised ActivationWindow to 2.5s (was too short).");
				}
				if (MinimumSlope.Value >= 10f)
				{
					MinimumSlope.Value = 2f;
					Plugin.Log.LogInfo((object)"Shield Sledding: lowered MinimumSlope to 2° (was too steep).");
				}
			}
			if (ConfigSchemaVersion.Value < 3)
			{
				if (MinimumSlope.Value > 0f)
				{
					MinimumSlope.Value = 0f;
					Plugin.Log.LogInfo((object)"Shield Sledding: lowered MinimumSlope to 0° so flat ground works.");
				}
				ConfigSchemaVersion.Value = 3;
			}
			if (ConfigSchemaVersion.Value < 4)
			{
				ConfigSchemaVersion.Value = 4;
			}
			if (ConfigSchemaVersion.Value < 5)
			{
				if (SledGroundClearance.Value > 0.1f)
				{
					SledGroundClearance.Value = 0.06f;
					Plugin.Log.LogInfo((object)"Shield Sledding: lowered SledGroundClearance to 0.06m for slope contact.");
				}
				ConfigSchemaVersion.Value = 5;
			}
			if (ConfigSchemaVersion.Value < 6)
			{
				SledGroundClearance.Value = 0f;
				Plugin.Log.LogInfo((object)"Shield Sledding: set SledGroundClearance to 0m for full slope contact.");
				ConfigSchemaVersion.Value = 6;
			}
			if (ConfigSchemaVersion.Value < 7)
			{
				if (DamageToDurabilityMultiplier.Value >= 0.99f)
				{
					DamageToDurabilityMultiplier.Value = 0.15f;
					Plugin.Log.LogInfo((object)"Shield Sledding: lowered DamageToDurabilityMultiplier to 0.15 (was melting shields).");
				}
				ConfigSchemaVersion.Value = 7;
			}
			if (ConfigSchemaVersion.Value < 8)
			{
				ConfigSchemaVersion.Value = 8;
			}
			if (ConfigSchemaVersion.Value < 9)
			{
				Plugin.Log.LogInfo((object)"Shield Sledding: config v9 — removed legacy collision/ghost options.");
				ConfigSchemaVersion.Value = 9;
			}
			if (ConfigSchemaVersion.Value < 10)
			{
				Plugin.Log.LogInfo((object)"Shield Sledding: config v10 — simplified damage toggle and per-shield speed entries in [ShieldSpeeds].");
				ConfigSchemaVersion.Value = 10;
			}
			if (ConfigSchemaVersion.Value < 11)
			{
				if (MaximumSpeed.Value >= 20f)
				{
					MaximumSpeed.Value = 13f;
				}
				if (BaseAcceleration.Value >= 16f)
				{
					BaseAcceleration.Value = 10f;
				}
				if (ForwardLeanBonus.Value >= 3f)
				{
					ForwardLeanBonus.Value = 1.5f;
				}
				Plugin.Log.LogInfo((object)"Shield Sledding: config v11 — lowered default speeds and removed stats section.");
				ConfigSchemaVersion.Value = 11;
			}
			if (ConfigSchemaVersion.Value < 12)
			{
				if (MaximumSpeed.Value <= 13.5f)
				{
					MaximumSpeed.Value = 10f;
				}
				Plugin.Log.LogInfo((object)"Shield Sledding: config v12 — ShieldBanded baseline 10 m/s, tier scaling, Blocking skill affects speed.");
				ConfigSchemaVersion.Value = 12;
			}
			if (ConfigSchemaVersion.Value < 13)
			{
				Plugin.Log.LogInfo((object)"Shield Sledding: config v13 — Blocking skill scales 80% to 100% of max speed (10 m/s on banded at skill 100).");
				ConfigSchemaVersion.Value = 13;
			}
			if (ConfigSchemaVersion.Value < 14)
			{
				Plugin.Log.LogInfo((object)"Shield Sledding: config v14 — uniform sled speed; shield tier affects durability wear ([ShieldWear]).");
				ConfigSchemaVersion.Value = 14;
			}
			if (ConfigSchemaVersion.Value < 15)
			{
				ResetObsoleteSpeedConfig(config);
				Plugin.Log.LogInfo((object)"Shield Sledding: config v15 — cleared obsolete [ShieldSpeeds] values. Speed is uniform; edit [ShieldWear] only.");
				ConfigSchemaVersion.Value = 15;
			}
			if (ConfigSchemaVersion.Value < 16)
			{
				MaximumSpeed.Value = 0f;
				Plugin.Log.LogInfo((object)"Shield Sledding: config v16 — slope-natural speed (MaximumSpeed 0 = no cap; speed follows hill steepness).");
				ConfigSchemaVersion.Value = 16;
			}
			if (ConfigSchemaVersion.Value < 17)
			{
				ResetObsoleteSpeedConfig(config);
				ShieldWearConfig.MigrateLegacyWearDefaults(config);
				Plugin.Log.LogInfo((object)"Shield Sledding: config v17 — updated [ShieldWear] defaults; use ModdedWolf.ShieldSledding.cfg only.");
				ConfigSchemaVersion.Value = 17;
			}
			if (ConfigSchemaVersion.Value < 18)
			{
				if (MinimumSlope.Value < 12f)
				{
					MinimumSlope.Value = 12f;
					Plugin.Log.LogInfo((object)"Shield Sledding: restored MinimumSlope to 12° — flat ground cannot start sledding.");
				}
				ConfigSchemaVersion.Value = 18;
			}
			if (ConfigSchemaVersion.Value < 19)
			{
				if (MinimumSlope.Value < 12f)
				{
					MinimumSlope.Value = 12f;
					Plugin.Log.LogInfo((object)"Shield Sledding: set MinimumSlope to 12° (flat ground cannot start sledding).");
				}
				ConfigSchemaVersion.Value = 19;
			}
			if (ConfigSchemaVersion.Value < 20)
			{
				if (UphillPenalty.Value >= 2.9f)
				{
					UphillPenalty.Value = 1f;
					Plugin.Log.LogInfo((object)"Shield Sledding: lowered UphillPenalty to 1 (was stopping hard on climbs; now grade-scaled).");
				}
				ConfigSchemaVersion.Value = 20;
			}
			if (ConfigSchemaVersion.Value < 21)
			{
				ShieldWearConfig.MigrateWoodWearDefault(config);
				Plugin.Log.LogInfo((object)"Shield Sledding: config v21 — ShieldWood wear default 6.00 (2× prior); [ShieldWear] range max 10.");
				ConfigSchemaVersion.Value = 21;
			}
			if (ConfigSchemaVersion.Value < 22)
			{
				ShieldWearConfig.MigrateEarlyShieldWearDoubling(config);
				Plugin.Log.LogInfo((object)"Shield Sledding: config v22 — early shield wear (Wood→Banded) defaults doubled.");
				ConfigSchemaVersion.Value = 22;
			}
			if (ConfigSchemaVersion.Value < 23)
			{
				ShieldWearConfig.MigrateTowerWearAdvantage(config);
				Plugin.Log.LogInfo((object)"Shield Sledding: config v23 — tower shields wear slower than matching non-tower boards.");
				ConfigSchemaVersion.Value = 23;
			}
			if (ConfigSchemaVersion.Value < 24)
			{
				if (Mathf.Approximately(MinimumSlope.Value, 12f))
				{
					MinimumSlope.Value = 8f;
					Plugin.Log.LogInfo((object)"Shield Sledding: config v24 — MinimumSlope default 8° (was 12°).");
				}
				else
				{
					Plugin.Log.LogInfo((object)"Shield Sledding: config v24 — MinimumSledSpeed added (default 2 m/s to stay sledding).");
				}
				ConfigSchemaVersion.Value = 24;
			}
			if (ConfigSchemaVersion.Value < 25)
			{
				if (Mathf.Approximately(MinBlockSkillSpeedScale.Value, 0.8f))
				{
					MinBlockSkillSpeedScale.Value = 1f;
				}
				Plugin.Log.LogInfo((object)"Shield Sledding: config v25 — Blocking skill speed scale 1.0 at skill 0 → 1.7 at skill 100.");
				ConfigSchemaVersion.Value = 25;
			}
			if (ConfigSchemaVersion.Value < 26)
			{
				if (Mathf.Approximately(MinBlockSkillSpeedScale.Value, 1f))
				{
					MinBlockSkillSpeedScale.Value = 0.8f;
				}
				Plugin.Log.LogInfo((object)"Shield Sledding: config v26 — Blocking skill speed scale 0.80 at skill 0 → 1.7 at skill 100.");
				ConfigSchemaVersion.Value = 26;
			}
			if (ConfigSchemaVersion.Value < 27)
			{
				Plugin.Log.LogInfo((object)"Shield Sledding: config v27 — renamed Blocking skill scales to BlockSkillSpeedScaleMin / BlockSkillSpeedScaleMax (kept adjacent in config UI).");
				ConfigSchemaVersion.Value = 27;
			}
			if (ConfigSchemaVersion.Value < 28)
			{
				if (Mathf.Approximately(SteeringStrength.Value, 2.5f))
				{
					SteeringStrength.Value = 4.75f;
				}
				if (Mathf.Approximately(SteeringSpeedFalloff.Value, 0.08f))
				{
					SteeringSpeedFalloff.Value = 0.035f;
				}
				Plugin.Log.LogInfo((object)"Shield Sledding: config v28 — stronger default steering; softer rock/obstacle sled collisions.");
				ConfigSchemaVersion.Value = 28;
			}
			if (ConfigSchemaVersion.Value < 29)
			{
				Plugin.Log.LogInfo((object)"Shield Sledding: config v29 — aggressive rock skim (pass-through low obstacles; heightmap ride plane).");
				ConfigSchemaVersion.Value = 29;
			}
			if (ConfigSchemaVersion.Value < 30)
			{
				Plugin.Log.LogInfo((object)"Shield Sledding: config v30 — fixed canopy/roof upward snaps; restored water skim ground.");
				ConfigSchemaVersion.Value = 30;
			}
			if (ConfigSchemaVersion.Value < 31)
			{
				if (Mathf.Approximately(WaterSledMinSpeed.Value, 6f))
				{
					WaterSledMinSpeed.Value = 3.5f;
				}
				if (Mathf.Approximately(WaterSledFriction.Value, 0.35f))
				{
					WaterSledFriction.Value = 0.22f;
				}
				Plugin.Log.LogInfo((object)"Shield Sledding: config v31 — water skim latch/hysteresis; lower engage speed; fixed water friction.");
				ConfigSchemaVersion.Value = 31;
			}
			if (ConfigSchemaVersion.Value < 32)
			{
				if (Mathf.Approximately(ShieldSurfFriction.Value, 0.65f))
				{
					ShieldSurfFriction.Value = 0.4f;
				}
				Plugin.Log.LogInfo((object)"Shield Sledding: config v32 — water skim blocks shore terrain; faster wood/piece ramp friction.");
				ConfigSchemaVersion.Value = 32;
			}
			if (ConfigSchemaVersion.Value < 33)
			{
				Plugin.Log.LogInfo((object)"Shield Sledding: config v33 — wood ramp lock (no micro-bounce); solid collision inside dungeons.");
				ConfigSchemaVersion.Value = 33;
			}
			if (ConfigSchemaVersion.Value < 34)
			{
				if (Mathf.Approximately(WaterSledFriction.Value, 0.22f))
				{
					WaterSledFriction.Value = 0.48f;
				}
				Plugin.Log.LogInfo((object)"Shield Sledding: config v34 — water skim rides live wave height; higher water friction + sustain speed.");
				ConfigSchemaVersion.Value = 34;
			}
			if (ConfigSchemaVersion.Value < 35)
			{
				if (Mathf.Approximately(WaterSledMinSpeed.Value, 3.5f))
				{
					WaterSledMinSpeed.Value = 5.5f;
				}
				if (Mathf.Approximately(WaterSledSustainSpeed.Value, 5f))
				{
					WaterSledSustainSpeed.Value = 6.5f;
				}
				if (Mathf.Approximately(WaterSledFriction.Value, 0.48f) || Mathf.Approximately(WaterSledFriction.Value, 0.22f))
				{
					WaterSledFriction.Value = 0.55f;
				}
				Plugin.Log.LogInfo((object)"Shield Sledding: config v35 — no water W-propulsion; harder skim engage/decay; soft particle spray.");
				ConfigSchemaVersion.Value = 35;
			}
			if (ConfigSchemaVersion.Value < 36)
			{
				if (Mathf.Approximately(WaterSledMinSpeed.Value, 5.5f))
				{
					WaterSledMinSpeed.Value = 4.5f;
				}
				if (Mathf.Approximately(WaterSledSustainSpeed.Value, 6.5f))
				{
					WaterSledSustainSpeed.Value = 5f;
				}
				if (Mathf.Approximately(WaterSledFriction.Value, 0.55f))
				{
					WaterSledFriction.Value = 0.4f;
				}
				if (Mathf.Approximately(MinimumSledSpeed.Value, 2f))
				{
					MinimumSledSpeed.Value = 0.75f;
				}
				Plugin.Log.LogInfo((object)"Shield Sledding: config v36 — softer water coast/exit; end sled when submerged; gentler ground min speed.");
				ConfigSchemaVersion.Value = 36;
			}
			if (ConfigSchemaVersion.Value < 37)
			{
				if (Mathf.Approximately(WaterSledSustainSpeed.Value, 5f))
				{
					WaterSledSustainSpeed.Value = 6f;
				}
				Plugin.Log.LogInfo((object)"Shield Sledding: config v37 — water skim sustain/exit near ~6 m/s (no longer cancels around ~4).");
				ConfigSchemaVersion.Value = 37;
			}
			if (ConfigSchemaVersion.Value < 38)
			{
				WaterSledSustainSpeed.Value = 6f;
				if (WaterSledMinSpeed.Value < 6f)
				{
					WaterSledMinSpeed.Value = 6f;
				}
				Plugin.Log.LogInfo((object)"Shield Sledding: config v38 — water skim cancel hard-forced to 6 m/s (sole water speed restriction).");
				ConfigSchemaVersion.Value = 38;
			}
			if (ConfigSchemaVersion.Value < 39)
			{
				if (Mathf.Approximately(MinimumSledSpeed.Value, 0.75f))
				{
					MinimumSledSpeed.Value = 2f;
				}
				Plugin.Log.LogInfo((object)"Shield Sledding: config v39 — ground min speed default 2 m/s (water cancel stays at 6).");
				ConfigSchemaVersion.Value = 39;
			}
			if (ConfigSchemaVersion.Value < 40)
			{
				if (Mathf.Approximately(UphillPenalty.Value, 1f))
				{
					UphillPenalty.Value = 0.75f;
				}
				Plugin.Log.LogInfo((object)"Shield Sledding: config v40 — better downhill→uphill momentum (UphillPenalty 0.75 + SlopeTransitionRetention).");
				ConfigSchemaVersion.Value = 40;
			}
			if (ConfigSchemaVersion.Value < 41)
			{
				if (Mathf.Approximately(MinimumSledSpeed.Value, 2f))
				{
					MinimumSledSpeed.Value = 4f;
				}
				Plugin.Log.LogInfo((object)"Shield Sledding: config v41 — ground min speed default 4 m/s (water cancel stays at 6).");
				ConfigSchemaVersion.Value = 41;
			}
			if (ConfigSchemaVersion.Value < 42)
			{
				Plugin.Log.LogInfo((object)"Shield Sledding: config v42 — InvertSteering + CameraRelativeSteering (natural A/D default).");
				ConfigSchemaVersion.Value = 42;
			}
			if (ConfigSchemaVersion.Value < 43)
			{
				Plugin.Log.LogInfo((object)"Shield Sledding: config v43 — travel-based steer frame (stable A/D while freelook / slope-align).");
				ConfigSchemaVersion.Value = 43;
			}
		}

		private static void MigrateRenamedFloat(ConfigFile config, string section, string oldKey, ConfigEntry<float> newEntry)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Expected O, but got Unknown
			ConfigDefinition val = new ConfigDefinition(section, oldKey);
			bool saveOnConfigSet = config.SaveOnConfigSet;
			config.SaveOnConfigSet = false;
			try
			{
				ConfigEntry<float> val2 = config.Bind<float>(section, oldKey, float.NegativeInfinity, (ConfigDescription)null);
				if (!float.IsNegativeInfinity(val2.Value))
				{
					newEntry.Value = val2.Value;
				}
				config.Remove(val);
			}
			finally
			{
				config.SaveOnConfigSet = saveOnConfigSet;
			}
		}

		private static void ResetObsoleteSpeedConfig(ConfigFile config)
		{
			for (int i = 0; i < ShieldSpeedRegistry.VanillaShieldPrefabs.Length; i++)
			{
				string text = ShieldSpeedRegistry.VanillaShieldPrefabs[i];
				ConfigEntry<float> val = config.Bind<float>("ShieldSpeeds", text, 1f, (ConfigDescription)null);
				if (!Mathf.Approximately(val.Value, 1f))
				{
					val.Value = 1f;
					Plugin.Log.LogInfo((object)("Shield Sledding: reset obsolete ShieldSpeeds." + text + " to 1.0 (unused)."));
				}
			}
			ConfigEntry<float> val2 = config.Bind<float>("ShieldSpeeds", "DefaultModdedSpeed", 1f, (ConfigDescription)null);
			if (!Mathf.Approximately(val2.Value, 1f))
			{
				val2.Value = 1f;
			}
			ConfigEntry<string> val3 = config.Bind<string>("ShieldSpeeds", "CustomModdedSpeeds", string.Empty, (ConfigDescription)null);
			if (!string.IsNullOrEmpty(val3.Value))
			{
				val3.Value = string.Empty;
			}
		}
	}
	internal static class DamageAbsorption
	{
		internal static bool ShouldAbsorb(Player player)
		{
			if (!Utility.IsLocalPlayer(player))
			{
				return false;
			}
			ShieldSledController instance = ShieldSledController.Instance;
			if (instance != null)
			{
				if (!instance.IsSledding)
				{
					return instance.IsArmed;
				}
				return true;
			}
			return false;
		}

		internal static bool TryAbsorb(Character character, HitData hit)
		{
			if (!ConfigManager.AbsorbDamageToShield.Value)
			{
				return false;
			}
			Player player = (Player)(object)((character is Player) ? character : null);
			if (!ShouldAbsorb(player) || hit == null)
			{
				return false;
			}
			float totalDamage = ((DamageTypes)(ref hit.m_damage)).GetTotalDamage();
			if (totalDamage <= 0.001f)
			{
				return false;
			}
			ShieldSledController instance = ShieldSledController.Instance;
			bool isSledding = instance.IsSledding;
			Character attacker = null;
			if (isSledding && ConfigManager.EndSledOnCombatHit.Value && totalDamage >= ConfigManager.CombatHitMinDamage.Value && TryGetCombatAttacker(hit, character, out attacker))
			{
				if (totalDamage > 1f)
				{
					EffectController.Instance?.ShakeCamera(totalDamage * 0.02f * ConfigManager.CameraShakeStrength.Value);
				}
				instance.EndSledding(player, SledEndReason.CombatHit);
				if (ConfigManager.StaggerOnCombatHit.Value)
				{
					ApplyCombatStagger(player, hit, attacker);
				}
				ClearHitDamage(hit);
				return true;
			}
			if (isSledding)
			{
				ItemData handShield = Utility.GetHandShield(player);
				if (handShield != null)
				{
					instance.ReportDamageAsShieldWear(player, handShield, totalDamage);
				}
			}
			if (totalDamage > 1f)
			{
				EffectController.Instance?.ShakeCamera(totalDamage * 0.01f * ConfigManager.CameraShakeStrength.Value);
			}
			ClearHitDamage(hit);
			return true;
		}

		internal static bool TryGetCombatAttacker(HitData hit, Character victim, out Character attacker)
		{
			attacker = hit.GetAttacker();
			if ((Object)(object)attacker == (Object)null || (Object)(object)attacker == (Object)(object)victim || attacker.IsDead())
			{
				return false;
			}
			return true;
		}

		private static void ApplyCombatStagger(Player player, HitData hit, Character attacker)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: 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_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: 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_0039: Unknown result type (might be due to invalid IL or missing references)
			Vector3 val = hit.m_dir;
			if (((Vector3)(ref val)).sqrMagnitude < 0.01f && (Object)(object)attacker != (Object)null)
			{
				val = ((Component)player).transform.position - ((Component)attacker).transform.position;
			}
			if (((Vector3)(ref val)).sqrMagnitude < 0.01f)
			{
				val = -((Component)player).transform.forward;
			}
			((Character)player).Stagger(((Vector3)(ref val)).normalized);
		}

		internal static void ApplyLandingDamage(Player player, float impact)
		{
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Expected O, but got Unknown
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)player == (Object)null || impact <= 0.5f)
			{
				return;
			}
			float num = impact * ConfigManager.LandingDamageMultiplier.Value;
			if (!(num <= 0.001f))
			{
				HitData val = new HitData();
				val.m_damage.m_blunt = num;
				val.m_point = ((Component)player).transform.position;
				val.m_dir = Vector3.down;
				((Character)player).Damage(val);
				if (num > 1f)
				{
					EffectController.Instance?.ShakeCamera(num * 0.01f * ConfigManager.CameraShakeStrength.Value);
				}
			}
		}

		internal static void ClearHitDamage(HitData hit)
		{
			if (hit != null)
			{
				hit.m_damage.m_blunt = 0f;
				hit.m_damage.m_slash = 0f;
				hit.m_damage.m_pierce = 0f;
				hit.m_damage.m_fire = 0f;
				hit.m_damage.m_frost = 0f;
				hit.m_damage.m_lightning = 0f;
				hit.m_damage.m_poison = 0f;
				hit.m_damage.m_spirit = 0f;
				hit.m_damage.m_chop = 0f;
				hit.m_damage.m_pickaxe = 0f;
				hit.m_staggerMultiplier = 0f;
				hit.m_pushForce = 0f;
			}
		}
	}
	internal sealed class DurabilityController
	{
		private Vector3 _lastPosition;

		private float _sessionTime;

		internal void Begin(Player player)
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			_lastPosition = (((Object)(object)player != (Object)null) ? ((Component)player).transform.position : Vector3.zero);
			_sessionTime = 0f;
		}

		internal void Tick(Player player, ItemData shield, PhysicsResult result)
		{
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: 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_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: 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 (ConfigManager.EnableDurabilityLoss.Value && !((Object)(object)player == (Object)null) && shield != null)
			{
				Vector3 position = ((Component)player).transform.position;
				float num = Vector3.Distance(position, _lastPosition);
				if (num > 3f)
				{
					num = 3f;
				}
				_lastPosition = position;
				_sessionTime += Time.fixedDeltaTime;
				float num2 = 1f;
				if ((int)result.Ground.Biome != 0)
				{
					num2 = Utility.GetDurabilityWearBiomeMultiplier(result.Ground.Biome);
				}
				float num3 = 0f;
				num3 += num * ConfigManager.DurabilityPerMeter.Value;
				num3 += Time.fixedDeltaTime * ConfigManager.DurabilityPerSecond.Value;
				num3 += result.HorizontalSpeed * 0.001f * ConfigManager.DurabilityPerMeter.Value;
				num3 *= num2;
				num3 *= ShieldSpeedRegistry.GetWearMultiplier(shield);
				ApplyWear(player, shield, num3);
			}
		}

		internal void OnLanding(Player player, ItemData shield, float impact)
		{
			if (ConfigManager.EnableDurabilityLoss.Value && shield != null && !(impact <= 0.5f))
			{
				float num = impact * ConfigManager.LandingDamageMultiplier.Value * 0.15f;
				num *= ShieldSpeedRegistry.GetWearMultiplier(shield);
				ApplyWear(player, shield, num);
			}
		}

		internal void OnCollision(Player player, ItemData shield, float impactForce)
		{
		}

		internal void OnDamage(Player player, ItemData shield, float damage)
		{
			if (ConfigManager.EnableDurabilityLoss.Value && shield != null && !(damage <= 0f))
			{
				float num = damage * ConfigManager.DamageToDurabilityMultiplier.Value;
				num *= ShieldSpeedRegistry.GetWearMultiplier(shield);
				num = Mathf.Min(num, shield.GetMaxDurability() * 0.08f);
				ApplyWear(player, shield, num);
			}
		}

		private void ApplyWear(Player player, ItemData shield, float wear)
		{
			if (!(wear <= 0f))
			{
				float num = shield.GetMaxDurability() * 0.05f;
				if (num > 0f)
				{
					wear = Mathf.Min(wear, num);
				}
				shield.m_durability -= wear;
				if (shield.m_durability <= 0f)
				{
					shield.m_durability = 0f;
					((Humanoid)player).UnequipItem(shield, true);
					ShieldSledController.Instance?.EndSledding(player, SledEndReason.ShieldBroken);
				}
			}
		}
	}
	internal sealed class EffectController
	{
		private readonly Dictionary<int, ParticleSystem> _remoteParticles = new Dictionary<int, ParticleSystem>();

		private ParticleSystem _localParticles;

		private float _shakeTimer;

		private float _shakeStrength;

		private Vector3 _shakeOffset;

		private float _fovOffset;

		private float _fovSpeedRatio;

		private float _tilt;

		private static Material _sharedParticleMaterial;

		private static Texture2D _sharedParticleTexture;

		internal static EffectController Instance { get; private set; }

		internal float CurrentFovOffset => _fovOffset;

		internal EffectController()
		{
			Instance = this;
		}

		internal static void Prewarm()
		{
			if (!Utility.IsDedicatedServer())
			{
				EnsureSharedParticleMaterial();
			}
		}

		internal void Begin(Player player)
		{
			EnsureLocalParticles(player);
			if ((Object)(object)_localParticles != (Object)null && !_localParticles.isPlaying)
			{
				_localParticles.Stop(true, (ParticleSystemStopBehavior)0);
			}
		}

		internal void End(Player player)
		{
			CleanupForPlayer(player);
			ClearCameraEffects();
		}

		internal void CleanupOrphans()
		{
			if ((Object)(object)_localParticles != (Object)null)
			{
				Object.Destroy((Object)(object)((Component)_localParticles).gameObject);
				_localParticles = null;
			}
		}

		internal void CleanupForPlayer(Player player)
		{
			if (!((Object)(object)player == (Object)null))
			{
				if ((Object)(object)_localParticles != (Object)null)
				{
					_localParticles.Stop(true, (ParticleSystemStopBehavior)0);
				}
				ClearCameraEffects();
			}
		}

		internal void Update(Player player, float speed, float steerX)
		{
			if (!((Object)(object)player == (Object)null) && !Utility.IsDedicatedServer())
			{
				UpdateParticles(player, speed);
				if (!ConfigManager.EnableCameraEffects.Value)
				{
					ClearCameraEffects();
					UpdateRemoteEffects();
				}
				else
				{
					UpdateCamera(player, speed, steerX);
					UpdateRemoteEffects();
				}
			}
		}

		private void UpdateParticles(Player player, float speed)
		{
			if (!ConfigManager.EnableParticles.Value)
			{
				if ((Object)(object)_localParticles != (Object)null)
				{
					_localParticles.Stop(true, (ParticleSystemStopBehavior)1);
				}
			}
			else if (speed > 1f)
			{
				EnsureLocalParticles(player);
				if ((Object)(object)_localParticles != (Object)null && !_localParticles.isPlaying)
				{
					_localParticles.Play();
				}
			}
			else if ((Object)(object)_localParticles != (Object)null)
			{
				_localParticles.Stop(true, (ParticleSystemStopBehavior)1);
			}
		}

		internal void OnLanding(Player player, float impact)
		{
			if (Utility.IsLocalPlayer(player))
			{
				ShakeCamera(impact * ConfigManager.CameraShakeStrength.Value * 0.02f);
			}
		}

		internal void OnCollision(Player player, Vector3 point, Vector3 normal, float impact)
		{
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			if (Utility.IsLocalPlayer(player))
			{
				ShakeCamera(impact * ConfigManager.CameraShakeStrength.Value * 0.03f);
			}
			if (ConfigManager.EnableShieldSparks.Value && ConfigManager.EnableParticles.Value)
			{
				SpawnBurst(point, normal, new Color(1f, 0.7f, 0.2f), 12);
			}
		}

		internal void OnRemoteCollision(Vector3 point, Vector3 normal, float impact)
		{
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			if (!(impact < 1f) && !Utility.IsDedicatedServer() && ConfigManager.EnableShieldSparks.Value && ConfigManager.EnableParticles.Value)
			{
				SpawnBurst(point, normal, new Color(1f, 0.7f, 0.2f), 12);
			}
		}

		internal void OnWildlifeImpact(Player player, Vector3 point, Vector3 direction, float impact)
		{
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			if (Utility.IsLocalPlayer(player))
			{
				ShakeCamera(impact * ConfigManager.CameraShakeStrength.Value * 0.04f);
			}
			if (ConfigManager.EnableShieldSparks.Value && ConfigManager.EnableParticles.Value)
			{
				SpawnBurst(point, direction, new Color(1f, 0.7f, 0.2f), 18);
			}
		}

		internal void ShakeCamera(float strength)
		{
			if (ConfigManager.EnableCameraEffects.Value && !(strength <= 0f) && !Utility.IsDedicatedServer() && !((Object)(object)Player.m_localPlayer == (Object)null))
			{
				_shakeStrength = Mathf.Max(_shakeStrength, strength);
				_shakeTimer = 0.25f;
			}
		}

		private void EnsureLocalParticles(Player player)
		{
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Expected O, but got Unknown
			//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00df: Unknown result type (might be due to invalid IL or missing references)
			//IL_0102: Unknown result type (might be due to invalid IL or missing references)
			//IL_0107: Unknown result type (might be due to invalid IL or missing references)
			//IL_010f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0120: Unknown result type (might be due to invalid IL or missing references)
			//IL_0131: Unknown result type (might be due to invalid IL or missing references)
			//IL_0162: Unknown result type (might be due to invalid IL or missing references)
			//IL_0167: Unknown result type (might be due to invalid IL or missing references)
			//IL_0177: Unknown result type (might be due to invalid IL or missing references)
			//IL_017c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0184: Unknown result type (might be due to invalid IL or missing references)
			//IL_0194: Unknown result type (might be due to invalid IL or missing references)
			//IL_0199: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d7: Expected O, but got Unknown
			//IL_01f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_0215: Unknown result type (might be due to invalid IL or missing references)
			//IL_021f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0224: Unknown result type (might be due to invalid IL or missing references)
			//IL_023b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0240: Unknown result type (might be due to invalid IL or missing references)
			//IL_0251: Unknown result type (might be due to invalid IL or missing references)
			//IL_0256: Unknown result type (might be due to invalid IL or missing references)
			//IL_0264: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_localParticles != (Object)null)
			{
				Transform feetAttach = ShieldSledVisual.GetFeetAttach(player);
				Transform val = (((Object)(object)feetAttach != (Object)null) ? feetAttach : ((Component)player).transform);
				if ((Object)(object)((Component)_localParticles).transform.parent != (Object)(object)val)
				{
					((Component)_localParticles).transform.SetParent(val, false);
					((Component)_localParticles).transform.localPosition = (Vector3)(((Object)(object)feetAttach != (Object)null) ? Vector3.zero : new Vector3(0f, 0.1f, 0.4f));
				}
				return;
			}
			Transform feetAttach2 = ShieldSledVisual.GetFeetAttach(player);
			GameObject val2 = new GameObject("ShieldSledding_LocalParticles");
			val2.transform.SetParent(((Object)(object)feetAttach2 != (Object)null) ? feetAttach2 : ((Component)player).transform, false);
			val2.transform.localPosition = (Vector3)(((Object)(object)feetAttach2 != (Object)null) ? Vector3.zero : new Vector3(0f, 0.1f, 0.4f));
			_localParticles = val2.AddComponent<ParticleSystem>();
			MainModule main = _localParticles.main;
			((MainModule)(ref main)).startLifetime = MinMaxCurve.op_Implicit(0.45f);
			((MainModule)(ref main)).startSpeed = MinMaxCurve.op_Implicit(1.5f);
			((MainModule)(ref main)).startSize = MinMaxCurve.op_Implicit(0.08f);
			((MainModule)(ref main)).maxParticles = 48;
			((MainModule)(ref main)).simulationSpace = (ParticleSystemSimulationSpace)1;
			((MainModule)(ref main)).startColor = MinMaxGradient.op_Implicit(new Color(0.92f, 0.96f, 1f, 0.65f));
			EmissionModule emission = _localParticles.emission;
			((EmissionModule)(ref emission)).rateOverTime = MinMaxCurve.op_Implicit(18f);
			ShapeModule shape = _localParticles.shape;
			((ShapeModule)(ref shape)).shapeType = (ParticleSystemShapeType)4;
			((ShapeModule)(ref shape)).angle = 14f;
			((ShapeModule)(ref shape)).radius = 0.15f;
			ColorOverLifetimeModule colorOverLifetime = _localParticles.colorOverLifetime;
			((ColorOverLifetimeModule)(ref colorOverLifetime)).enabled = true;
			Gradient val3 = new Gradient();
			val3.SetKeys((GradientColorKey[])(object)new GradientColorKey[2]
			{
				new GradientColorKey(new Color(0.95f, 0.98f, 1f), 0f),
				new GradientColorKey(new Color(0.75f, 0.88f, 1f), 1f)
			}, (GradientAlphaKey[])(object)new GradientAlphaKey[2]
			{
				new GradientAlphaKey(0.65f, 0f),
				new GradientAlphaKey(0f, 1f)
			});
			((ColorOverLifetimeModule)(ref colorOverLifetime)).color = MinMaxGradient.op_Implicit(val3);
			ApplyParticleMaterial(_localParticles);
			_localParticles.Stop(true, (ParticleSystemStopBehavior)0);
		}

		private static Shader FindParticleShader()
		{
			string[] array = new string[7] { "Legacy Shaders/Particles/Alpha Blended", "Particles/Standard Unlit", "Mobile/Particles/Alpha Blended", "Particles/Additive", "Mobile/Particles/Additive", "Sprites/Default", "Unlit/Transparent" };
			for (int i = 0; i < array.Length; i++)
			{
				Shader val = Shader.Find(array[i]);
				if ((Object)(object)val != (Object)null)
				{
					return val;
				}
			}
			return null;
		}

		private static Texture2D CreateSoftParticleTexture()
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Expected O, but got Unknown
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			Texture2D val = new Texture2D(64, 64, (TextureFormat)4, false)
			{
				name = "ShieldSledding_SoftParticle",
				wrapMode = (TextureWrapMode)1,
				filterMode = (FilterMode)1
			};
			float num = 31.5f;
			float num2 = num;
			Color[] array = (Color[])(object)new Color[4096];
			for (int i = 0; i < 64; i++)
			{
				for (int j = 0; j < 64; j++)
				{
					float num3 = ((float)j - num) / num2;
					float num4 = ((float)i - num) / num2;
					float num5 = Mathf.Sqrt(num3 * num3 + num4 * num4);
					float num6 = ((num5 >= 1f) ? 0f : Mathf.Pow(1f - num5, 1.65f));
					array[i * 64 + j] = new Color(1f, 1f, 1f, num6);
				}
			}
			val.SetPixels(array);
			val.Apply(false, true);
			return val;
		}

		private static Material EnsureSharedParticleMaterial()
		{
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Expected O, but got Unknown
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_sharedParticleMaterial != (Object)null)
			{
				return _sharedParticleMaterial;
			}
			Shader val = FindParticleShader();
			if ((Object)(object)val == (Object)null)
			{
				return null;
			}
			if ((Object)(object)_sharedParticleTexture == (Object)null)
			{
				_sharedParticleTexture = CreateSoftParticleTexture();
			}
			Color val2 = default(Color);
			((Color)(ref val2))..ctor(0.92f, 0.96f, 1f, 0.55f);
			Material val3 = new Material(val);
			((Object)val3).name = "ShieldSledding_ParticleMat";
			val3.mainTexture = (Texture)(object)_sharedParticleTexture;
			if (val3.HasProperty("_MainTex"))
			{
				val3.SetTexture("_MainTex", (Texture)(object)_sharedParticleTexture);
			}
			if (val3.HasProperty("_Color"))
			{
				val3.SetColor("_Color", val2);
			}
			if (val3.HasProperty("_TintColor"))
			{
				val3.SetColor("_TintColor", val2);
			}
			val3.color = val2;
			if (val3.HasProperty("_Mode"))
			{
				val3.SetFloat("_Mode", 2f);
			}
			val3.SetInt("_SrcBlend", 5);
			val3.SetInt("_DstBlend", 10);
			val3.SetInt("_ZWrite", 0);
			val3.DisableKeyword("_ALPHATEST_ON");
			val3.EnableKeyword("_ALPHABLEND_ON");
			val3.DisableKeyword("_ALPHAPREMULTIPLY_ON");
			val3.renderQueue = 3000;
			_sharedParticleMaterial = val3;
			return _sharedParticleMaterial;
		}

		private static void ApplyParticleMaterial(ParticleSystem particles)
		{
			if ((Object)(object)particles == (Object)null)
			{
				return;
			}
			ParticleSystemRenderer component = ((Component)particles).GetComponent<ParticleSystemRenderer>();
			if (!((Object)(object)component == (Object)null))
			{
				Material val = EnsureSharedParticleMaterial();
				if (!((Object)(object)val == (Object)null))
				{
					component.renderMode = (ParticleSystemRenderMode)0;
					((Renderer)component).sharedMaterial = val;
				}
			}
		}

		private void UpdateCamera(Player player, float speed, float steerX)
		{
			//IL_0134: Unknown result type (might be due to invalid IL or missing references)
			//IL_0139: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_0105: Unknown result type (might be due to invalid IL or missing references)
			//IL_010a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0128: 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)
			if ((Object)(object)GameCamera.instance?.m_camera == (Object)null)
			{
				ClearCameraEffects();
				return;
			}
			float num = 12f;
			if (Utility.TrySampleGround(player, out var sample))
			{
				num = Mathf.Max(6f, ShieldSpeedRegistry.EstimateNaturalSpeed(in sample));
			}
			float num2 = Mathf.Clamp01(speed / Mathf.Max(0.01f, num));
			float deltaTime = Time.deltaTime;
			float num3 = ((num2 >= _fovSpeedRatio) ? 12f : 2.2f);
			_fovSpeedRatio = Mathf.Lerp(_fovSpeedRatio, num2, 1f - Mathf.Exp((0f - num3) * deltaTime));
			_fovOffset = _fovSpeedRatio * ConfigManager.SpeedFovIncrease.Value;
			float num4 = (0f - steerX) * ConfigManager.CameraTiltStrength.Value;
			_tilt = Mathf.Lerp(_tilt, num4, Time.deltaTime * 6f);
			if (_shakeTimer > 0f)
			{
				_shakeTimer -= Time.deltaTime;
				_shakeOffset = Random.insideUnitSphere * _shakeStrength;
				if (_shakeTimer <= 0f)
				{
					_shakeStrength = 0f;
					_shakeOffset = Vector3.zero;
				}
			}
			else
			{
				_shakeOffset = Vector3.zero;
			}
		}

		internal static void ApplyCameraAfterGameCamera(GameCamera gameCamera)
		{
			//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_00ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_010d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
			EffectController instance = Instance;
			if (instance == null || (Object)(object)gameCamera == (Object)null || !ConfigManager.EnableCameraEffects.Value)
			{
				return;
			}
			float fovOffset = instance._fovOffset;
			if (fovOffset > 0.001f)
			{
				if ((Object)(object)gameCamera.m_camera != (Object)null)
				{
					Camera camera = gameCamera.m_camera;
					camera.fieldOfView += fovOffset;
				}
				if ((Object)(object)gameCamera.m_skyCamera != (Object)null)
				{
					Camera skyCamera = gameCamera.m_skyCamera;
					skyCamera.fieldOfView += fovOffset;
				}
			}
			float tilt = instance._tilt;
			Vector3 shakeOffset = instance._shakeOffset;
			bool flag = Mathf.Abs(tilt) > 0.001f;
			bool flag2 = ((Vector3)(ref shakeOffset)).sqrMagnitude > 1E-07f;
			if (flag || flag2)
			{
				ApplyTiltAndShake(((Component)gameCamera).transform, tilt, shakeOffset, flag, flag2);
				if ((Object)(object)gameCamera.m_camera != (Object)null && (Object)(object)((Component)gameCamera.m_camera).transform != (Object)(object)((Component)gameCamera).transform)
				{
					ApplyTiltAndShake(((Component)gameCamera.m_camera).transform, tilt, shakeOffset, flag, flag2);
				}
				if ((Object)(object)gameCamera.m_skyCamera != (Object)null)
				{
					ApplyTiltAndShake(((Component)gameCamera.m_skyCamera).transform, tilt, shakeOffset, flag, flag2);
				}
			}
		}

		private static void ApplyTiltAndShake(Transform transform, float tilt, Vector3 shake, bool hasTilt, bool hasShake)
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: 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_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)transform == (Object)null))
			{
				if (hasTilt)
				{
					transform.rotation *= Quaternion.Euler(0f, 0f, tilt);
				}
				if (hasShake)
				{
					transform.position += shake;
				}
			}
		}

		private void ClearCameraEffects()
		{
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			_fovOffset = 0f;
			_fovSpeedRatio = 0f;
			_tilt = 0f;
			_shakeTimer = 0f;
			_shakeStrength = 0f;
			_shakeOffset = Vector3.zero;
		}

		private void UpdateRemoteEffects()
		{
			List<Player> allPlayers = Player.GetAllPlayers();
			if (allPlayers == null)
			{
				return;
			}
			for (int i = 0; i < allPlayers.Count; i++)
			{
				Player val = allPlayers[i];
				if (!((Object)(object)val == (Object)null) && !((Object)(object)val == (Object)(object)Player.m_localPlayer))
				{
					if (!MultiplayerSync.Instance.TryReadRemoteState(val, out var active, out var speed, out var _))
					{
						StopRemote(val);
					}
					else if (!active || speed < 1f || !ConfigManager.EnableParticles.Value)
					{
						StopRemote(val);
					}
					else
					{
						EnsureRemoteParticles(val);
					}
				}
			}
		}

		private void EnsureRemoteParticles(Player player)
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			int instanceID = ((Object)player).GetInstanceID();
			if (!_remoteParticles.ContainsKey(instanceID))
			{
				GameObject val = new GameObject("ShieldSledding_RemoteParticles");
				val.transform.SetParent(((Component)player).transform, false);
				val.transform.localPosition = new Vector3(0f, 0.1f, 0.4f);
				ParticleSystem val2 = val.AddComponent<ParticleSystem>();
				MainModule main = val2.main;
				((MainModule)(ref main)).startLifetime = MinMaxCurve.op_Implicit(0.4f);
				((MainModule)(ref main)).startSpeed = MinMaxCurve.op_Implicit(1.2f);
				((MainModule)(ref main)).startSize = MinMaxCurve.op_Implicit(0.08f);
				((MainModule)(ref main)).maxParticles = 24;
				((MainModule)(ref main)).startColor = MinMaxGradient.op_Implicit(new Color(0.95f, 0.98f, 1f, 0.5f));
				ApplyParticleMaterial(val2);
				val2.Play();
				_remoteParticles[instanceID] = val2;
			}
		}

		private void StopRemote(Player player)
		{
			int instanceID = ((Object)player).GetInstanceID();
			if (_remoteParticles.TryGetValue(instanceID, out var value) && (Object)(object)value != (Object)null)
			{
				value.Stop(true, (ParticleSystemStopBehavior)1);
			}
		}

		private static void SpawnBurst(Vector3 point, Vector3 normal, Color color, int count)
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Expected O, but got Unknown
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00df: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			if (!Utility.IsDedicatedServer())
			{
				GameObject val = new GameObject("ShieldSledding_SparkBurst");
				val.transform.position = point;
				if (((Vector3)(ref normal)).sqrMagnitude > 0.01f)
				{
					val.transform.rotation = Quaternion.LookRotation(((Vector3)(ref normal)).normalized);
				}
				ParticleSystem val2 = val.AddComponent<ParticleSystem>();
				MainModule main = val2.main;
				((MainModule)(ref main)).duration = 0.2f;
				((MainModule)(ref main)).loop = false;
				((MainModule)(ref main)).startLifetime = MinMaxCurve.op_Implicit(0.35f);
				((MainModule)(ref main)).startSpeed = MinMaxCurve.op_Implicit(2.5f);
				((MainModule)(ref main)).startSize = MinMaxCurve.op_Implicit(0.06f);
				((MainModule)(ref main)).startColor = MinMaxGradient.op_Implicit(color);
				((MainModule)(ref main)).maxParticles = count;
				((MainModule)(ref main)).simulationSpace = (ParticleSystemSimulationSpace)1;
				EmissionModule emission = val2.emission;
				((EmissionModule)(ref emission)).rateOverTime = MinMaxCurve.op_Implicit(0f);
				((EmissionModule)(ref emission)).SetBursts((Burst[])(object)new Burst[1]
				{
					new Burst(0f, (short)count)
				});
				ShapeModule shape = val2.shape;
				((ShapeModule)(ref shape)).shapeType = (ParticleSystemShapeType)4;
				((ShapeModule)(ref shape)).angle = 35f;
				((ShapeModule)(ref shape)).radius = 0.05f;
				ApplyParticleMaterial(val2);
				val2.Play();
				Object.Destroy((Object)(object)val, 1.25f);
			}
		}
	}
	internal sealed class MultiplayerSync
	{
		private struct RemoteSledState
		{
			internal bool Active;

			internal float Speed;

			internal string ShieldPrefab;
		}

		internal const string ImpactRpc = "ShieldSledding_Impact";

		internal const string StateRpc = "ShieldSledding_State";

		private static readonly int ZdoActive = StringExtensionMethods.GetStableHashCode("SS_Active");

		private static readonly int ZdoShieldPrefab = StringExtensionMethods.GetStableHashCode("SS_ShieldPrefab");

		private static readonly int ZdoSpeed = StringExtensionMethods.GetStableHashCode("SS_Speed");

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

		private readonly Dictionary<int, RemoteSledState> _remoteStates = new Dictionary<int, RemoteSledState>();

		private float _lastWriteTime;

		private bool _lastActive;

		private float _lastSpeed;

		private string _lastShield = string.Empty;

		internal static MultiplayerSync Instance { get; private set; }

		internal MultiplayerSync()
		{
			Instance = this;
		}

		internal static void EnsureRegistered(ZNetView nview)
		{
			if ((Object)(object)nview == (Object)null || !nview.IsValid())
			{
				return;
			}
			MultiplayerSync sync = Instance;
			if (sync == null)
			{
				return;
			}
			int id = ((Object)nview).GetInstanceID();
			if (!sync._registeredViews.Add(id))
			{
				return;
			}
			nview.Register<Vector3, float>("ShieldSledding_Impact", (Action<long, Vector3, float>)delegate(long sender, Vector3 point, float impact)
			{
				//IL_001d: Unknown result type (might be due to invalid IL or missing references)
				//IL_001e: Unknown result type (might be due to invalid IL or missing references)
				if (!(impact < 1f) && sender != ZNet.GetUID())
				{
					EffectController.Instance?.OnRemoteCollision(point, Vector3.up, impact);
					AudioController.Instance?.PlayImpact(Player.m_localPlayer, impact);
				}
			});
			nview.Register<bool, float, string>("ShieldSledding_State", (Action<long, bool, float, string>)delegate(long sender, bool active, float speed, string shieldPrefab)
			{
				if (sender != ZNet.GetUID())
				{
					sync._remoteStates[id] = new RemoteSledState
					{
						Active = active,
						Speed = speed,
						ShieldPrefab = (shieldPrefab ?? string.Empty)
					};
				}
			});
		}

		internal void WriteLocalState(Player player, bool active, float speed)
		{
			if ((Object)(object)player == (Object)null || !Utility.IsLocalPlayer(player))
			{
				return;
			}
			ZDO playerZdo = Utility.GetPlayerZdo(player);
			if (playerZdo == null)
			{
				return;
			}
			string text = string.Empty;
			ItemData val = Utility.GetEquippedShield(player);
			if (val == null)
			{
				val = ShieldSledController.Instance?.LockedShield;
			}
			if (val != null)
			{
				text = Utility.GetShieldPrefabName(val);
			}
			if ((active && Time.time - _lastWriteTime >= 1f) || active != _lastActive || !Mathf.Approximately(speed, _lastSpeed) || !(text == _lastShield) || !(Time.time - _lastWriteTime < 0.1f))
			{
				playerZdo.Set(ZdoActive, active ? 1 : 0, false);
				playerZdo.Set(ZdoSpeed, speed);
				if (!string.IsNullOrEmpty(text))
				{
					playerZdo.Set(ZdoShieldPrefab, text);
				}
				else if (!active)
				{
					playerZdo.Set(ZdoShieldPrefab, string.Empty);
				}
				_lastActive = active;
				_lastSpeed = speed;
				_lastShield = text;
				_lastWriteTime = Time.time;
				BroadcastLocalState(player, active, speed, text);
			}
		}

		private static void BroadcastLocalState(Player player, bool active, float speed, string shield)
		{
			ZNetView component = ((Component)player).GetComponent<ZNetView>();
			if (!((Object)(object)component == (Object)null) && component.IsValid())
			{
				EnsureRegistered(component);
				component.InvokeRPC(ZNetView.Everybody, "ShieldSledding_State", new object[3]
				{
					active,
					speed,
					shield ?? string.Empty
				});
			}
		}

		internal bool TryReadRemoteState(Player player, out bool active, out float speed, out string shieldPrefab)
		{
			active = false;
			speed = 0f;
			shieldPrefab = string.Empty;
			if ((Object)(object)player == (Object)null)
			{
				return false;
			}
			ZNetView component = ((Component)player).GetComponent<ZNetView>();
			if ((Object)(object)component != (Object)null && component.IsValid())
			{
				EnsureRegistered(component);
				if (_remoteStates.TryGetValue(((Object)component).GetInstanceID(), out var value))
				{
					active = value.Active;
					speed = value.Speed;
					shieldPrefab = value.ShieldPrefab ?? string.Empty;
					return true;
				}
			}
			ZDO playerZdo = Utility.GetPlayerZdo(player);
			if (playerZdo == null)
			{
				return false;
			}
			active = playerZdo.GetInt(ZdoActive, 0) != 0;
			speed = playerZdo.GetFloat(ZdoSpeed, 0f);
			shieldPrefab = playerZdo.GetString(ZdoShieldPrefab, string.Empty);
			return true;
		}

		internal void ClearRemoteState(ZNetView nview)
		{
			if (!((Object)(object)nview == (Object)null))
			{
				int instanceID = ((Object)nview).GetInstanceID();
				_remoteStates.Remove(instanceID);
				_registeredViews.Remove(instanceID);
			}
		}

		internal void UpdateRemotePlayers()
		{
			List<Player> allPlayers = Player.GetAllPlayers();
			if (allPlayers == null)
			{
				return;
			}
			for (int i = 0; i < allPlayers.Count; i++)
			{
				Player val = allPlayers[i];
				if (!((Object)(object)val == (Object)null) && !((Object)(object)val == (Object)(object)Player.m_localPlayer))
				{
					EnsureRegistered(((Component)val).GetComponent<ZNetView>());
				}
			}
			ShieldSledVisual.UpdateRemotePlayers();
		}

		internal void SendImpactEvent(Player player, Vector3 point, float impact)
		{
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)player == (Object)null))
			{
				ZNetView component = ((Component)player).GetComponent<ZNetView>();
				if (!((Object)(object)component == (Object)null) && component.IsValid())
				{
					EnsureRegistered(component);
					component.InvokeRPC(ZNetView.Everybody, "ShieldSledding_Impact", new object[2] { point, impact });
				}
			}
		}
	}
	internal struct SledRunRecord
	{
		public float Distance;

		public float MaxSpeed;

		public float LongestJump;

		public string TimestampUtc;
	}
	internal struct GhostFrame
	{
		public Vector3 Position;

		public Quaternion Rotation;

		public float Time;
	}
	internal static class GhostReplaySystem
	{
		private static readonly List<GhostFrame> Frames = new List<GhostFrame>();

		private static GameObject _ghost;

		private static float _sessionStart;

		internal static void BeginSession()
		{
			Frames.Clear();
			_sessionStart = Time.time;
			DestroyGhost();
		}

		internal static void RecordFrame(Player player)
		{
		}

		internal static void EndSession()
		{
			Frames.Clear();
			DestroyGhost();
		}

		private static void SpawnGhostPlayback()
		{
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Expected O, but got Unknown
			DestroyGhost();
			_ghost = new GameObject("ShieldSledding_Ghost");
			_ghost.AddComponent<GhostPlayback>().Initialize(Frames.ToArray());
		}

		private static void DestroyGhost()
		{
			if ((Object)(object)_ghost != (Object)null)
			{
				Object.Destroy((Object)(object)_ghost);
				_ghost = null;
			}
		}
	}
	internal sealed class GhostPlayback : MonoBehaviour
	{
		private GhostFrame[] _frames;

		private int _index;

		private float _startTime;

		internal void Initialize(GhostFrame[] frames)
		{
			_frames = frames;
			_index = 0;
			_startTime = Time.time;
			TryCreateGhostVisual(((Component)this).transform);
		}

		private static void TryCreateGhostVisual(Transform parent)
		{
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Expected O, but got Unknown
			//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			Shader val = FindGhostShader();
			if ((Object)(object)val == (Object)null)
			{
				return;
			}
			GameObject val2 = GameObject.CreatePrimitive((PrimitiveType)1);
			val2.transform.SetParent(parent, false);
			val2.transform.localScale = new Vector3(0.5f, 0.9f, 0.5f);
			Collider component = val2.GetComponent<Collider>();
			if ((Object)(object)component != (Object)null)
			{
				Object.Destroy((Object)(object)component);
			}
			Renderer component2 = val2.GetComponent<Renderer>();
			if ((Object)(object)component2 == (Object)null)
			{
				Object.Destroy((Object)(object)val2);
				return;
			}
			Material val3 = new Material(val);
			Color val4 = default(Color);
			((Color)(ref val4))..ctor(0.4f, 0.8f, 1f, 0.35f);
			if (val3.HasProperty("_Color"))
			{
				val3.SetColor("_Color", val4);
			}
			else
			{
				val3.color = val4;
			}
			component2.material = val3;
		}

		private static Shader FindGhostShader()
		{
			string[] array = new string[4] { "Custom/StandardClear", "Custom/StandardDoubleSided", "Unlit/Color", "Sprites/Default" };
			for (int i = 0; i < array.Length; i++)
			{
				Shader val = Shader.Find(array[i]);
				if ((Object)(object)val != (Object)null)
				{
					return val;
				}
			}
			return null;
		}

		private void Update()
		{
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			if (_frames == null || _frames.Length == 0)
			{
				Object.Destroy((Object)(object)((Component)this).gameObject);
				return;
			}
			float num = Time.time - _startTime;
			while (_index < _frames.Length - 1 && _frames[_index + 1].Time <= num)
			{
				_index++;
			}
			GhostFrame ghostFrame = _frames[_index];
			((Component)this).transform.SetPositionAndRotation(ghostFrame.Position, ghostFrame.Rotation);
			if (_index >= _frames.Length - 1 && num > _frames[_frames.Length - 1].Time + 1f)
			{
				Object.Destroy((Object)(object)((Component)this).gameObject);
			}
		}
	}
	internal static class RecordsManager
	{
		private static string FilePath => Path.Combine(Paths.ConfigPath, "ShieldSledding_Records.json");

		internal static void SaveRun(SledRunRecord record)
		{
			List<string> list = new List<string>();
			if (File.Exists(FilePath))
			{
				list.AddRange(File.ReadAllLines(FilePath));
			}
			list.Add($"{record.TimestampUtc}|{record.Distance:F2}|{record.MaxSpeed:F2}|{record.LongestJump:F2}");
			if (list.Count > 200)
			{
				list.RemoveAt(0);
			}
			File.WriteAllLines(FilePath, list);
		}
	}
	internal static class LeaderboardManager
	{
		private static readonly List<SledRunRecord> Entries = new List<SledRunRecord>();

		private static string FilePath => Path.Combine(Paths.ConfigPath, "ShieldSledding_Leaderboard.json");

		internal static void Submit(SledRunRecord record)
		{
			Entries.Add(record);
			Entries.Sort((SledRunRecord a, SledRunRecord b) => b.MaxSpeed.CompareTo(a.MaxSpeed));
			if (Entries.Count > 25)
			{
				Entries.RemoveAt(Entries.Count - 1);
			}
			List<string> list = new List<string>(Entries.Count);
			for (int num = 0; num < Entries.Count; num++)
			{
				SledRunRecord sledRunRecord = Entries[num];
				list.Add($"{sledRunRecord.TimestampUtc}|{sledRunRecord.Distance:F2}|{sledRunRecord.MaxSpeed:F2}|{sledRunRecord.LongestJump:F2}");
			}
			File.WriteAllLines(FilePath, list);
		}
	}
	internal static class TimeTrialManager
	{
		private static float _startTime;

		private static bool _active;

		internal static void BeginSession()
		{
		}

		internal static void EndSession()
		{
		}
	}
	internal static class SledPatches
	{
		internal static bool UpdateMotionPatched;

		internal static void Apply(Harmony harmony)
		{
			PatchMethod(harmony, typeof(Character), "Jump", "CharacterJumpPrefix", "CharacterJumpPostfix");
			PatchMethod(harmony, typeof(Player), "SetControls", "PlayerSetControlsPrefix");
			PatchMethod(harmony, typeof(Humanoid), "UpdateBlock", "HumanoidUpdateBlockPrefix");
			PatchMethod(harmony, typeof(Humanoid), "EquipItem", "HumanoidEquipItemPrefix");
			PatchMethod(harmony, typeof(Humanoid), "UnequipItem", "HumanoidUnequipItemPrefix");
			PatchMethod(harmony, typeof(Character), "UpdateMotion", "CharacterUpdateMotionPrefix", null, new Type[1] { typeof(float) });
			PatchDeclaredMethod(harmony, typeof(Character), "Damage", "CharacterDamagePrefix", null, new Type[1] { typeof(HitData) });
			PatchAllDeclaredMethods(harmony, typeof(Character), "RPC_Damage", "CharacterRpcDamagePrefix");
			PatchMethod(harmony, typeof(Character), "UpdateGroundContact", "CharacterUpdateGroundContactPrefix", "CharacterUpdateGroundContactPostfix");
			PatchMethod(harmony, typeof(Character), "UpdateWater", "CharacterUpdateWaterPrefix");
			PatchMethod(harmony, typeof(Character), "UnderWorldCheck", "CharacterUnderWorldCheckPrefix", null, new Type[1] { typeof(float) });
			PatchMethod(harmony, typeof(VisEquipment), "UpdateEquipmentVisuals", "VisEquipmentUpdateEquipmentVisualsPrefix", "VisEquipmentUpdateEquipmentPostfix");
			PatchMethod(harmony, typeof(ZNetView), "OnDestroy", "ZNetViewOnDestroyPrefix");
			PatchMethod(harmony, typeof(GameCamera), "UpdateCamera", null, "GameCameraUpdateCameraPostfix", new Type[1] { typeof(float) });
		}

		private static void GameCameraUpdateCameraPostfix(GameCamera __instance)
		{
			EffectController.ApplyCameraAfterGameCamera(__instance);
		}

		private static void PatchMethod(Harmony harmony, Type type, string methodName, string prefix = null, string postfix = null, Type[] argTypes = null)
		{
			//IL_007d: 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)
			MethodInfo methodInfo = ((argTypes == null) ? AccessTools.Method(type, methodName, (Type[])null, (Type[])null) : AccessTools.Method(type, methodName, argTypes, (Type[])null));
			if (methodInfo == null)
			{
				Plugin.Log.LogError((object)("Shield Sledding: could not patch " + type.Name + "." + methodName));
				return;
			}
			if (type == typeof(Character) && methodName == "UpdateMotion")
			{
				UpdateMotionPatched = true;
			}
			harmony.Patch((MethodBase)methodInfo, (prefix == null) ? ((HarmonyMethod)null) : new HarmonyMethod(typeof(SledPatches), prefix, (Type[])null), (postfix == null) ? ((HarmonyMethod)null) : new HarmonyMethod(typeof(SledPatches), postfix, (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			Plugin.Log.LogInfo((object)("Shield Sledding: patched " + type.Name + "." + methodName));
		}

		private static void PatchDeclaredMethod(Harmony harmony, Type type, string methodName, string prefix = null, string postfix = null, Type[] argTypes = null)
		{
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			MethodInfo methodInfo = ((argTypes == null) ? AccessTools.DeclaredMethod(type, methodName, (Type[])null, (Type[])null) : AccessTools.DeclaredMethod(type, methodName, argTypes, (Type[])null));
			if (methodInfo == null)
			{
				Plugin.Log.LogError((object)("Shield Sledding: could not patch declared " + type.Name + "." + methodName));
				return;
			}
			harmony.Patch((MethodBase)methodInfo, (prefix == null) ? ((HarmonyMethod)null) : new HarmonyMethod(typeof(SledPatches), prefix, (Type[])null), (postfix == null) ? ((HarmonyMethod)null) : new HarmonyMethod(typeof(SledPatches), postfix, (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			Plugin.Log.LogInfo((object)("Shield Sledding: patched declared " + type.Name + "." + methodName));
		}

		private static void PatchAllDeclaredMethods(Harmony harmony, Type type, string methodName, string prefix = null)
		{
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			int num = 0;
			foreach (MethodInfo declaredMethod in AccessTools.GetDeclaredMethods(type))
			{
				if (!(declaredMethod.Name != methodName))
				{
					harmony.Patch((MethodBase)declaredMethod, (prefix == null) ? ((HarmonyMethod)null) : new HarmonyMethod(typeof(SledPatches), prefix, (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
					ParameterInfo[] parameters = declaredMethod.GetParameters();
					string text = ((parameters.Length == 0) ? string.Empty : string.Join(", ", Array.ConvertAll(parameters, (ParameterInfo p) => p.ParameterType.Name)));
					Plugin.Log.LogInfo((object)("Shield Sledding: patched declared " + type.Name + "." + methodName + "(" + text + ")"));
					num++;
				}
			}
			if (num == 0)
			{
				Plugin.Log.LogInfo((object)("Shield Sledding: skipped optional patch " + type.Name + "." + methodName + " (no matching methods)."));
			}
		}

		private static bool CharacterJumpPrefix(Character __instance)
		{
			if (!Utility.IsLocalPlayer((Player)(object)((__instance is Player) ? __instance : null)))
			{
				return true;
			}
			ShieldSledController instance = ShieldSledController.Instance;
			if (instance == null || !instance.IsSledding)
			{
				return true;
			}
			return false;
		}

		private static void CharacterJumpPostfix(Character __instance)
		{
			if (Utility.IsLocalPlayer((Player)(object)((__instance is Player) ? __instance : null)))
			{
				ShieldSledController.Instance?.NotifyJump();
			}
		}

		private static void PlayerSetControlsPrefix(Player __instance, ref Vector3 movedir, ref bool attack, ref bool attackHold, ref bool secondaryAttack, ref bool secondaryAttackHold, ref bool block, ref bool blockHold, ref bool jump, ref bool crouch, ref bool run, ref bool autoRun, ref bool dodge, ref Vector3 ___m_moveDir)
		{
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: 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)
			if (Utility.IsLocalPlayer(__instance))
			{
				ShieldSledController instance = ShieldSledController.Instance;
				if (((instance != null) & jump) && !instance.IsSledding)
				{
					instance.NotifyJumpIntentFromInput();
				}
				if (instance != null && instance.TryOverrideControls(__instance, ref movedir, ref block, ref jump, ref run))
				{
					___m_moveDir = Vector3.zero;
					movedir = Vector3.zero;
					attack = false;
					attackHold = false;
					secondaryAttack = false;
					secondaryAttackHold = false;
					blockHold = false;
					dodge = false;
					autoRun = false;
					crouch = false;
				}
			}
		}

		private static bool HumanoidUpdateBlockPrefix(Humanoid __instance)
		{
			if (!Utility.IsLocalPlayer((Player)(object)((__instance is Player) ? __instance : null)))
			{
				return true;
			}
			if (ShieldSledController.Instance != null)
			{
				return !ShieldSledController.Instance.IsSledding;
			}
			return true;
		}

		private static bool HumanoidEquipItemPrefix(Humanoid __instance, ItemData item)
		{
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Invalid comparison between Unknown and I4
			if (!Utility.IsLocalPlayer((Playe