Decompiled source of FirstPerson v1.0.14

Landoria.FirstPerson.dll

Decompiled 4 days ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Threading;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Landoria.Shared;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("Landoria.FirstPerson")]
[assembly: AssemblyDescription("Adds first-person view and persistent normal-camera FOV control.")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Landoria")]
[assembly: AssemblyProduct("Landoria.FirstPerson")]
[assembly: AssemblyCopyright("Copyright © 2026 End3rbyte")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("E3AE6231-B01C-4392-AA43-20F1215A876B")]
[assembly: AssemblyFileVersion("1.0.14")]
[assembly: AssemblyInformationalVersion("1.0.14")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyVersion("1.0.14.28373")]
namespace Landoria.Shared
{
	internal static class ConfigWatcher
	{
		private const int ReloadDelayMilliseconds = 250;

		private const int MaximumReloadAttempts = 5;

		private static ConfigFile config;

		private static string configPath;

		private static ManualLogSource logger;

		private static FileSystemWatcher watcher;

		private static long lastChangeTicks;

		private static int reloadPending;

		private static int reloadAttempts;

		private static long ignoredWriteTicks;

		private static string displayName;

		private static Action restoreDefaults;

		private static Action applyConfiguration;

		internal static void Initialize(ConfigFile configFile, ManualLogSource logSource, string configurationDisplayName, Action restoreDefaultsAction, Action applyConfigurationAction = null)
		{
			Dispose();
			config = configFile;
			logger = logSource;
			displayName = configurationDisplayName;
			restoreDefaults = restoreDefaultsAction;
			applyConfiguration = applyConfigurationAction;
			configPath = Path.GetFullPath(config.ConfigFilePath);
			watcher = new FileSystemWatcher(Path.GetDirectoryName(configPath), Path.GetFileName(configPath));
			watcher.NotifyFilter = NotifyFilters.FileName | NotifyFilters.Size | NotifyFilters.LastWrite | NotifyFilters.CreationTime;
			watcher.Changed += OnChanged;
			watcher.Created += OnChanged;
			watcher.Deleted += OnChanged;
			watcher.Renamed += OnRenamed;
			watcher.EnableRaisingEvents = true;
		}

		internal static void IgnoreCurrentFileVersion()
		{
			if (string.IsNullOrEmpty(configPath) || !File.Exists(configPath))
			{
				return;
			}
			try
			{
				Interlocked.Exchange(ref ignoredWriteTicks, File.GetLastWriteTimeUtc(configPath).Ticks);
				Interlocked.Exchange(ref reloadPending, 0);
			}
			catch (IOException)
			{
				ManualLogSource obj = logger;
				if (obj != null)
				{
					obj.LogDebug((object)"Could not mark the internal configuration write.");
				}
			}
		}

		internal static void Update()
		{
			if (Volatile.Read(in reloadPending) == 0 || !ReloadDelayElapsed())
			{
				return;
			}
			Interlocked.Exchange(ref reloadPending, 0);
			try
			{
				bool flag = !File.Exists(configPath);
				if (flag)
				{
					restoreDefaults?.Invoke();
				}
				else
				{
					config.Reload();
				}
				applyConfiguration?.Invoke();
				reloadAttempts = 0;
				logger.LogInfo((object)(flag ? "Recreated the deleted configuration with default values." : "Reloaded configuration after a file change."));
				ShowReloadMessage(flag);
			}
			catch (Exception exception)
			{
				RetryReload(exception);
			}
		}

		internal static void Dispose()
		{
			if (watcher != null)
			{
				watcher.EnableRaisingEvents = false;
				watcher.Dispose();
				watcher = null;
			}
			Interlocked.Exchange(ref reloadPending, 0);
			Interlocked.Exchange(ref ignoredWriteTicks, 0L);
			reloadAttempts = 0;
			displayName = null;
			restoreDefaults = null;
			applyConfiguration = null;
		}

		private static void OnChanged(object sender, FileSystemEventArgs eventArgs)
		{
			QueueReload();
		}

		private static void OnRenamed(object sender, RenamedEventArgs eventArgs)
		{
			QueueReload();
		}

		private static void QueueReload()
		{
			if (!IsIgnoredWrite())
			{
				Interlocked.Exchange(ref lastChangeTicks, DateTime.UtcNow.Ticks);
				Interlocked.Exchange(ref reloadPending, 1);
				reloadAttempts = 0;
			}
		}

		private static bool IsIgnoredWrite()
		{
			try
			{
				long num = (File.Exists(configPath) ? File.GetLastWriteTimeUtc(configPath).Ticks : 0);
				return num != 0L && num == Interlocked.Read(in ignoredWriteTicks);
			}
			catch (IOException)
			{
				return false;
			}
		}

		private static bool ReloadDelayElapsed()
		{
			return DateTime.UtcNow.Ticks - Interlocked.Read(in lastChangeTicks) >= 2500000;
		}

		private static void RetryReload(Exception exception)
		{
			reloadAttempts++;
			logger.LogWarning((object)("Configuration reload failed: " + exception.Message));
			if (reloadAttempts < 5)
			{
				Interlocked.Exchange(ref lastChangeTicks, DateTime.UtcNow.Ticks);
				Interlocked.Exchange(ref reloadPending, 1);
			}
		}

		private static void ShowReloadMessage(bool recreated)
		{
			Player localPlayer = Player.m_localPlayer;
			if (localPlayer != null)
			{
				((Character)localPlayer).Message((MessageType)1, recreated ? (displayName + " configuration restored to defaults.") : (displayName + " configuration reloaded."), 0, (Sprite)null, false);
			}
		}
	}
}
namespace Landoria.FirstPerson
{
	[HarmonyPatch(typeof(GameCamera), "Awake")]
	internal static class CameraAwakePatch
	{
		private static void Prefix(GameCamera __instance)
		{
			Mode.CaptureVanillaDistance(__instance);
		}

		private static void Postfix(GameCamera __instance)
		{
			Mode.Apply(__instance);
		}
	}
	[HarmonyPatch(typeof(GameCamera), "UpdateCamera")]
	internal static class CameraUpdatePatch
	{
		private static void Prefix(ref float ___m_distance)
		{
			Shortcut.Update(ref ___m_distance);
			Shortcut.PrepareDistanceObservation(___m_distance);
		}

		private static void Postfix(GameCamera __instance, Camera ___m_camera, ref float ___m_distance)
		{
			Shortcut.ObserveCameraDistance(ref ___m_distance);
			Player localPlayer = Player.m_localPlayer;
			bool flag = Mode.ShouldActivate(localPlayer, GameCamera.InFreeFly(), ___m_distance);
			Mode.SetActive(flag);
			Mode.ApplyConfiguredFieldOfView(__instance);
			Mode.ApplyNearClipPlane(___m_camera);
			VisibilityController.SetHidden(localPlayer, flag);
			if (flag)
			{
				ViewController.Apply(__instance, localPlayer);
			}
			if (flag)
			{
				HeadBobController.Apply(__instance, localPlayer);
				HelmetLightController.Apply(__instance, localPlayer);
			}
			else
			{
				HeadBobController.Reset();
				HelmetLightController.Restore();
			}
		}
	}
	[HarmonyPatch(typeof(GameCamera), "GetCameraOffset")]
	internal static class CameraOffsetPatch
	{
		private static void Postfix(GameCamera __instance, Player player, ref Vector3 __result)
		{
			//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_0026: Unknown result type (might be due to invalid IL or missing references)
			//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)
			if (Mode.Active && Object.op_Implicit((Object)(object)player))
			{
				Vector3 val = ((Component)((Character)player).m_eye).transform.TransformVector(__instance.m_fpsOffset);
				__result = val;
			}
		}
	}
	[HarmonyPatch(typeof(Player), "AlwaysRotateCamera")]
	internal static class PlayerRotationPatch
	{
		private static void Postfix(Player __instance, ref bool __result)
		{
			if (Mode.Active && (Object)(object)__instance == (Object)(object)Player.m_localPlayer)
			{
				__result = true;
			}
		}
	}
	[HarmonyPatch(typeof(ZNetView), "Awake")]
	internal static class LoadedObjectPatch
	{
		private static void Postfix(ZNetView __instance)
		{
			VegetationController.Apply(((Component)__instance).gameObject);
		}
	}
	[HarmonyPatch(typeof(Character), "SetVisible")]
	internal static class PlayerVisibilityPatch
	{
		private static void Prefix(Character __instance, ref bool visible)
		{
			if (Mode.Active && (Object)(object)__instance == (Object)(object)Player.m_localPlayer)
			{
				visible = true;
			}
		}
	}
	[HarmonyPatch(typeof(VisEquipment), "UpdateVisuals")]
	internal static class VisualVisibilityPatch
	{
		private static void Postfix(VisEquipment __instance, GameObject ___m_leftItemInstance, GameObject ___m_rightItemInstance)
		{
			Player componentInParent = ((Component)__instance).GetComponentInParent<Player>();
			if ((Object)(object)componentInParent == (Object)(object)Player.m_localPlayer)
			{
				VisibilityController.TrackHeldItems(componentInParent, ___m_leftItemInstance, ___m_rightItemInstance);
			}
			if (Mode.Active && (Object)(object)componentInParent == (Object)(object)Player.m_localPlayer)
			{
				HelmetLightController.Refresh(componentInParent);
			}
		}
	}
	[HarmonyPatch(typeof(MonoUpdaters), "LateUpdate")]
	internal static class HelmetLightLateUpdatePatch
	{
		private static void Postfix()
		{
			HelmetLightController.Apply(GameCamera.instance, Player.m_localPlayer);
		}
	}
	[HarmonyPatch(typeof(ConsoleCommand), "RunAction")]
	internal static class FieldOfViewCommandPatch
	{
		private static bool Prefix(ConsoleCommand __instance, ConsoleEventArgs args)
		{
			if (__instance.Command == "fov" && args.Length == 1)
			{
				Terminal context = args.Context;
				if (context != null)
				{
					context.AddString($"FOV: {Preference.FieldOfView:0.#}");
				}
				return false;
			}
			string a = ((args.Length > 1) ? args[1] : null);
			if (!(__instance.Command == "fov") || args.Length != 2 || !string.Equals(a, "reset", StringComparison.OrdinalIgnoreCase))
			{
				float num = default(float);
				bool flag = args.TryParameterFloat(1, ref num);
				if (!(__instance.Command == "fov" && args.Length > 1 && flag) || (!(num < 65f) && !(num > 120f)))
				{
					return true;
				}
				Terminal context2 = args.Context;
				if (context2 != null)
				{
					context2.AddString($"FOV must be between {65f} " + $"and {120f}. " + "The current FOV was not changed.");
				}
				return false;
			}
			Preference.SetFieldOfView(65f);
			Mode.ApplyConfiguredFieldOfView(GameCamera.instance);
			return false;
		}

		private static void Postfix(ConsoleCommand __instance, ConsoleEventArgs args)
		{
			float num = default(float);
			bool flag = args.TryParameterFloat(1, ref num);
			if (__instance.Command == "fov" && args.Length > 1 && flag && num >= 65f && num <= 120f)
			{
				Preference.SetFieldOfView(num);
				Mode.ApplyConfiguredFieldOfView(GameCamera.instance);
			}
		}
	}
	[HarmonyPatch(typeof(Player), "OnSpawned")]
	internal static class PlayerSpawnPatch
	{
		private static void Postfix(Player __instance)
		{
			if ((Object)(object)__instance == (Object)(object)Player.m_localPlayer)
			{
				Mode.SetEnabled(Preference.Enabled);
				Mode.ApplyConfiguredFieldOfView(GameCamera.instance);
			}
		}
	}
	[HarmonyPatch(typeof(ZNet), "OnDestroy")]
	internal static class DisconnectPatch
	{
		private static void Prefix()
		{
			Mode.ResetSession();
		}
	}
	internal static class HeadBobController
	{
		private const float WalkVerticalAmplitude = 4f;

		private const float WalkStepInterval = 0.7f;

		private const float JogVerticalAmplitude = 6f;

		private const float JogStepInterval = 0.433f;

		private const float SprintVerticalAmplitude = 8f;

		private const float SprintStepInterval = 0.333f;

		private const float HorizontalAmplitude = 4f;

		private const float HorizonDistance = 10f;

		private const float MetersPerMillimeter = 0.001f;

		private const float MovementThreshold = 0.01f;

		private const float FadeDuration = 0.12f;

		private static float phase;

		private static float blend;

		internal static void Apply(GameCamera camera, Player player)
		{
			//IL_0029: 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)
			if (Preference.HeadBobStrength > 0 && Mode.Active && Object.op_Implicit((Object)(object)camera) && Object.op_Implicit((Object)(object)player))
			{
				int num;
				if (((Character)player).IsOnGround())
				{
					Vector3 moveDir = ((Character)player).GetMoveDir();
					num = ((((Vector3)(ref moveDir)).sqrMagnitude > 0.01f) ? 1 : 0);
				}
				else
				{
					num = 0;
				}
				bool flag = (byte)num != 0;
				blend = Mathf.MoveTowards(blend, flag ? 1f : 0f, Time.deltaTime / 0.12f);
				bool num2 = ((Character)player).IsRunning();
				float num3 = (num2 ? 0.333f : (((Character)player).IsWalking() ? 0.7f : 0.433f));
				phase = Mathf.Repeat(phase + Time.deltaTime * (float)Math.PI / num3, (float)Math.PI * 2f);
				float num4 = (num2 ? 8f : (((Character)player).IsWalking() ? 4f : 6f)) * 0.001f * (float)Preference.HeadBobStrength;
				float num5 = 0.004f * (float)Preference.HeadBobStrength;
				float horizontal = Mathf.Sin(phase) * num5 * blend;
				float vertical = Mathf.Sin(phase * 2f) * num4 * blend;
				ApplyMovement(((Component)camera).transform, horizontal, vertical);
			}
		}

		private static void ApplyMovement(Transform transform, float horizontal, float vertical)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: 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_001b: 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)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			Vector3 val = transform.position + transform.forward * 10f;
			transform.position += transform.right * horizontal + transform.up * vertical;
			transform.rotation = Quaternion.LookRotation(val - transform.position, Vector3.up);
		}

		internal static void Reset()
		{
			phase = 0f;
			blend = 0f;
		}
	}
	internal static class HelmetLightController
	{
		private struct LightState
		{
			internal Vector3 LocalPosition;

			internal Quaternion LocalRotation;
		}

		private struct FlickerState
		{
			internal float Intensity;

			internal float Movement;
		}

		private static readonly Dictionary<Light, LightState> Lights = new Dictionary<Light, LightState>();

		private static readonly Dictionary<LightFlicker, FlickerState> Flickers = new Dictionary<LightFlicker, FlickerState>();

		private static Player trackedPlayer;

		internal static void Apply(GameCamera camera, Player player)
		{
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			if (!Mode.Active || !Object.op_Implicit((Object)(object)camera) || !Object.op_Implicit((Object)(object)player))
			{
				Restore();
				return;
			}
			if ((Object)(object)trackedPlayer != (Object)(object)player)
			{
				Refresh(player);
			}
			foreach (Light key in Lights.Keys)
			{
				if (Object.op_Implicit((Object)(object)key))
				{
					((Component)key).transform.SetPositionAndRotation(((Component)camera).transform.position, ((Component)camera).transform.rotation);
				}
			}
		}

		internal static void Refresh(Player player)
		{
			if (!Object.op_Implicit((Object)(object)player))
			{
				Restore();
				return;
			}
			if ((Object)(object)trackedPlayer != (Object)(object)player)
			{
				Restore();
				trackedPlayer = player;
			}
			VisEquipment component = ((Component)player).GetComponent<VisEquipment>();
			if (Object.op_Implicit((Object)(object)component) && Object.op_Implicit((Object)(object)component.m_helmet))
			{
				CaptureLights(component.m_helmet);
				CaptureFlickers(component.m_helmet);
			}
		}

		internal static void Restore()
		{
			//IL_0036: 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)
			foreach (KeyValuePair<Light, LightState> light in Lights)
			{
				if (Object.op_Implicit((Object)(object)light.Key))
				{
					((Component)light.Key).transform.localPosition = light.Value.LocalPosition;
					((Component)light.Key).transform.localRotation = light.Value.LocalRotation;
				}
			}
			foreach (KeyValuePair<LightFlicker, FlickerState> flicker in Flickers)
			{
				if (Object.op_Implicit((Object)(object)flicker.Key))
				{
					flicker.Key.m_flickerIntensity = flicker.Value.Intensity;
					flicker.Key.m_movement = flicker.Value.Movement;
				}
			}
			Lights.Clear();
			Flickers.Clear();
			trackedPlayer = null;
		}

		private static void CaptureLights(Transform helmet)
		{
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			Light[] componentsInChildren = ((Component)helmet).GetComponentsInChildren<Light>(true);
			foreach (Light val in componentsInChildren)
			{
				if (Object.op_Implicit((Object)(object)val) && !Lights.ContainsKey(val))
				{
					Lights.Add(val, new LightState
					{
						LocalPosition = ((Component)val).transform.localPosition,
						LocalRotation = ((Component)val).transform.localRotation
					});
				}
			}
		}

		private static void CaptureFlickers(Transform helmet)
		{
			LightFlicker[] componentsInChildren = ((Component)helmet).GetComponentsInChildren<LightFlicker>(true);
			foreach (LightFlicker val in componentsInChildren)
			{
				if (Object.op_Implicit((Object)(object)val) && !Flickers.ContainsKey(val))
				{
					Flickers.Add(val, new FlickerState
					{
						Intensity = val.m_flickerIntensity,
						Movement = val.m_movement
					});
				}
				if (Object.op_Implicit((Object)(object)val))
				{
					val.m_flickerIntensity = 0f;
					val.m_movement = 0f;
				}
			}
		}
	}
	internal static class Mode
	{
		private const float NearClipPlane = 0.09f;

		private const float DistanceThreshold = 0.001f;

		private static float vanillaMinimumDistance;

		private static bool distanceCaptured;

		internal static bool Enabled { get; private set; }

		internal static bool Active { get; private set; }

		internal static void CaptureVanillaDistance(GameCamera camera)
		{
			vanillaMinimumDistance = camera.m_minDistance;
			distanceCaptured = true;
		}

		internal static void Apply(GameCamera camera)
		{
			if (Object.op_Implicit((Object)(object)camera))
			{
				camera.m_minDistance = (Enabled ? 0f : vanillaMinimumDistance);
			}
		}

		internal static void SetEnabled(bool enabled)
		{
			Enabled = enabled;
			if (!enabled)
			{
				SetActive(active: false);
			}
			Apply(GameCamera.instance);
			ApplyConfiguredFieldOfView(GameCamera.instance);
		}

		internal static void SetActive(bool active)
		{
			if (Active != active)
			{
				Active = active;
				if (active)
				{
					VegetationController.Apply();
				}
				else
				{
					VegetationController.Restore();
				}
			}
		}

		internal static bool ShouldActivate(Player player, bool isFreeFly, float cameraDistance)
		{
			if (Enabled && Object.op_Implicit((Object)(object)player) && !((Character)player).IsDead() && !isFreeFly)
			{
				return IsFirstPersonDistance(cameraDistance);
			}
			return false;
		}

		internal static bool IsFirstPersonDistance(float cameraDistance)
		{
			return cameraDistance <= 0.001f;
		}

		internal static void ApplyConfiguredFieldOfView(GameCamera camera)
		{
			float num = Preference.FieldOfView;
			if (Active)
			{
				num += Preference.FirstPersonFieldOfViewBonus;
			}
			if (Object.op_Implicit((Object)(object)camera))
			{
				camera.m_fov = num;
			}
		}

		internal static void ApplyNearClipPlane(Camera camera)
		{
			if (Active && Object.op_Implicit((Object)(object)camera))
			{
				camera.nearClipPlane = 0.09f;
			}
		}

		internal static void ResetSession()
		{
			SetEnabled(enabled: false);
			HeadBobController.Reset();
			HelmetLightController.Restore();
			VisibilityController.Restore();
			Shortcut.Reset();
		}

		internal static void Reset()
		{
			ResetSession();
			if (distanceCaptured && Object.op_Implicit((Object)(object)GameCamera.instance))
			{
				GameCamera.instance.m_minDistance = vanillaMinimumDistance;
			}
			distanceCaptured = false;
		}
	}
	internal static class Preference
	{
		internal const float DefaultFieldOfView = 65f;

		internal const float MinimumFieldOfView = 65f;

		internal const float MaximumFieldOfView = 120f;

		internal const float DefaultFirstPersonFieldOfViewBonus = 15f;

		internal const float MaximumFirstPersonFieldOfViewBonus = 50f;

		internal const int DefaultThirdPersonAutoDistance = 3;

		internal const int DefaultAutomaticReturnDelay = 3;

		internal const int DefaultHeadBobStrength = 2;

		private static ConfigEntry<bool> enabled;

		private static ConfigEntry<float> fieldOfView;

		private static ConfigEntry<float> firstPersonFieldOfViewBonus;

		private static ConfigEntry<KeyboardShortcut> toggleShortcut;

		private static ConfigEntry<KeyboardShortcut> toggleThirdPersonAutoShortcut;

		private static ConfigEntry<bool> thirdPersonAuto;

		private static ConfigEntry<int> thirdPersonAutoDistance;

		private static ConfigEntry<int> automaticReturnDelay;

		private static ConfigEntry<int> headBobStrength;

		internal static bool Enabled => enabled.Value;

		internal static float FieldOfView => fieldOfView.Value;

		internal static float FirstPersonFieldOfViewBonus => firstPersonFieldOfViewBonus.Value;

		internal static KeyboardShortcut ToggleShortcut => toggleShortcut.Value;

		internal static KeyboardShortcut ToggleThirdPersonAutoShortcut => toggleThirdPersonAutoShortcut.Value;

		internal static bool ThirdPersonAuto => thirdPersonAuto.Value;

		internal static int ThirdPersonAutoDistance => thirdPersonAutoDistance.Value;

		internal static int AutomaticReturnDelay => automaticReturnDelay.Value;

		internal static int HeadBobStrength => headBobStrength.Value;

		internal static void Initialize(ConfigFile config)
		{
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Expected O, but got Unknown
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Expected O, but got Unknown
			//IL_00a0: 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_011f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0129: Expected O, but got Unknown
			//IL_014c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0156: Expected O, but got Unknown
			//IL_0178: Unknown result type (might be due to invalid IL or missing references)
			//IL_0182: Expected O, but got Unknown
			enabled = config.Bind<bool>("Camera", "FirstPersonEnabled", false, "Whether first-person view is enabled at minimum camera zoom.");
			fieldOfView = config.Bind<float>("Camera", "FieldOfView", 65f, new ConfigDescription("Field of view shared by first-person, third-person, and free-fly cameras.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(65f, 120f), Array.Empty<object>()));
			firstPersonFieldOfViewBonus = config.Bind<float>("First Person", "FirstPersonFieldOfViewBonus", 15f, new ConfigDescription("Additional field of view applied only in first person.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 50f), Array.Empty<object>()));
			toggleShortcut = config.Bind<KeyboardShortcut>("First Person", "ToggleShortcut", new KeyboardShortcut((KeyCode)287, Array.Empty<KeyCode>()), "Shortcut used to enable or disable automatic first-person view.\n\nExamples: Mouse2 for the middle mouse button.\n\nMouse3/Mouse4 for the Forward/Back side button.\n\nSpace + LeftControl for Left Ctrl + Space.\n\nhttps://docs.unity3d.com/ScriptReference/KeyCode.html");
			toggleThirdPersonAutoShortcut = config.Bind<KeyboardShortcut>("Third Person - Auto", "ToggleThirdPersonAutoShortcut", new KeyboardShortcut((KeyCode)287, (KeyCode[])(object)new KeyCode[1] { (KeyCode)308 }), "Shortcut used to enable or disable automatic third person during actions.");
			thirdPersonAuto = config.Bind<bool>("Third Person - Auto", "ThirdPersonAuto", false, "Whether actions such as attacking, blocking, chopping, mining, or building temporarily move the camera to third person.");
			thirdPersonAutoDistance = config.Bind<int>("Third Person - Auto", "ThirdPersonAutoDistance", 3, new ConfigDescription("Camera distance in meters while attacking or blocking.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 10), Array.Empty<object>()));
			automaticReturnDelay = config.Bind<int>("Third Person - Auto", "AutomaticReturnDelay", 3, new ConfigDescription("ThirdPersonAuto delay before returning to first person after an action.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 10), Array.Empty<object>()));
			headBobStrength = config.Bind<int>("First Person", "HeadBobStrength", 2, new ConfigDescription("First-person head bob strength. Set to 0 to disable head bob.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 3), Array.Empty<object>()));
			SetFieldOfView(fieldOfView.Value);
		}

		internal static void SetEnabled(bool value)
		{
			enabled.Value = value;
			ConfigWatcher.IgnoreCurrentFileVersion();
		}

		internal static void SetThirdPersonAuto(bool value)
		{
			thirdPersonAuto.Value = value;
			ConfigWatcher.IgnoreCurrentFileVersion();
		}

		internal static void SetFieldOfView(float value)
		{
			fieldOfView.Value = Math.Max(65f, Math.Min(value, 120f));
			ConfigWatcher.IgnoreCurrentFileVersion();
		}

		internal static void RestoreDefaults(ConfigFile config)
		{
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			bool saveOnConfigSet = config.SaveOnConfigSet;
			config.SaveOnConfigSet = false;
			try
			{
				enabled.Value = false;
				fieldOfView.Value = 65f;
				firstPersonFieldOfViewBonus.Value = 15f;
				toggleShortcut.Value = new KeyboardShortcut((KeyCode)287, Array.Empty<KeyCode>());
				toggleThirdPersonAutoShortcut.Value = new KeyboardShortcut((KeyCode)287, (KeyCode[])(object)new KeyCode[1] { (KeyCode)308 });
				thirdPersonAuto.Value = false;
				thirdPersonAutoDistance.Value = 3;
				automaticReturnDelay.Value = 3;
				headBobStrength.Value = 2;
			}
			finally
			{
				config.SaveOnConfigSet = saveOnConfigSet;
			}
			config.Save();
			ConfigWatcher.IgnoreCurrentFileVersion();
		}
	}
	[BepInPlugin("Landoria.FirstPerson", "Landoria.FirstPerson", "1.0.14")]
	public sealed class Plugin : BaseUnityPlugin
	{
		private const string PluginGuid = "Landoria.FirstPerson";

		private const string PluginName = "Landoria.FirstPerson";

		private const string PluginVersion = "1.0.14";

		private Harmony _harmony;

		private void Awake()
		{
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Expected O, but got Unknown
			((BaseUnityPlugin)this).Logger.LogInfo((object)$"AssemblyVersion: {((object)this).GetType().Assembly.GetName().Version}.");
			_harmony = new Harmony("Landoria.FirstPerson");
			_harmony.PatchAll();
			Preference.Initialize(((BaseUnityPlugin)this).Config);
			ConfigWatcher.Initialize(((BaseUnityPlugin)this).Config, ((BaseUnityPlugin)this).Logger, "First Person", delegate
			{
				Preference.RestoreDefaults(((BaseUnityPlugin)this).Config);
			}, delegate
			{
				Mode.SetEnabled(Preference.Enabled);
				Mode.ApplyConfiguredFieldOfView(GameCamera.instance);
			});
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Landoria.FirstPerson 1.0.14 is loaded.");
		}

		private void Update()
		{
			ConfigWatcher.Update();
		}

		private void OnDestroy()
		{
			ConfigWatcher.Dispose();
			Mode.Reset();
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
			_harmony = null;
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Landoria.FirstPerson 1.0.14 is unloaded.");
		}
	}
	internal static class Shortcut
	{
		private static bool thirdPersonAutoActive;

		private static bool actionReturnCancelled;

		private static float actionThirdPersonEndTime;

		private static float expectedCameraDistance;

		internal static void Update(ref float cameraDistance)
		{
			//IL_000e: 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)
			UpdateActionCamera(ref cameraDistance);
			if (CanToggle())
			{
				if (IsShortcutDown(Preference.ToggleThirdPersonAutoShortcut))
				{
					ToggleThirdPersonAuto(ref cameraDistance);
				}
				else if (IsShortcutDown(Preference.ToggleShortcut))
				{
					ToggleShortcut();
				}
			}
		}

		internal static void PrepareDistanceObservation(float cameraDistance)
		{
			expectedCameraDistance = cameraDistance;
		}

		internal static void ObserveCameraDistance(ref float cameraDistance)
		{
			if (thirdPersonAutoActive)
			{
				bool num = cameraDistance > expectedCameraDistance + Mathf.Epsilon;
				bool flag = cameraDistance < expectedCameraDistance - Mathf.Epsilon;
				if (flag)
				{
					cameraDistance = 0f;
				}
				if (num || flag)
				{
					thirdPersonAutoActive = false;
					actionReturnCancelled = true;
					actionThirdPersonEndTime = 0f;
				}
			}
		}

		private static void ToggleShortcut()
		{
			thirdPersonAutoActive = false;
			actionReturnCancelled = false;
			actionThirdPersonEndTime = 0f;
			bool enabled = !Mode.Enabled;
			Mode.SetEnabled(enabled);
			Preference.SetEnabled(enabled);
			ShowState(enabled);
		}

		private static void ToggleThirdPersonAuto(ref float cameraDistance)
		{
			bool flag = !Preference.ThirdPersonAuto;
			Preference.SetThirdPersonAuto(flag);
			UpdateActionCamera(ref cameraDistance);
			Player localPlayer = Player.m_localPlayer;
			if (localPlayer != null)
			{
				((Character)localPlayer).Message((MessageType)1, flag ? "Third person auto enabled" : "Third person auto disabled", 0, (Sprite)null, false);
			}
		}

		private static void UpdateActionCamera(ref float cameraDistance)
		{
			if (!Preference.ThirdPersonAuto || !Mode.Enabled)
			{
				if (thirdPersonAutoActive)
				{
					cameraDistance = 0f;
					thirdPersonAutoActive = false;
					actionThirdPersonEndTime = 0f;
				}
				return;
			}
			Player localPlayer = Player.m_localPlayer;
			int num;
			if (Object.op_Implicit((Object)(object)localPlayer))
			{
				if (!((Character)localPlayer).InAttack())
				{
					num = (((Character)localPlayer).IsBlocking() ? 1 : 0);
					if (num == 0)
					{
						goto IL_0052;
					}
				}
				else
				{
					num = 1;
				}
				goto IL_0058;
			}
			num = 0;
			goto IL_0052;
			IL_0058:
			if (num != 0 && !actionReturnCancelled && (thirdPersonAutoActive || Mode.IsFirstPersonDistance(cameraDistance)))
			{
				if (!thirdPersonAutoActive)
				{
					cameraDistance = Preference.ThirdPersonAutoDistance;
				}
				thirdPersonAutoActive = true;
				actionThirdPersonEndTime = Time.unscaledTime + (float)Preference.AutomaticReturnDelay;
			}
			else if (thirdPersonAutoActive && Time.unscaledTime >= actionThirdPersonEndTime)
			{
				cameraDistance = 0f;
				thirdPersonAutoActive = false;
				actionThirdPersonEndTime = 0f;
			}
			return;
			IL_0052:
			actionReturnCancelled = false;
			goto IL_0058;
		}

		internal static void Reset()
		{
			thirdPersonAutoActive = false;
			actionReturnCancelled = false;
			actionThirdPersonEndTime = 0f;
			expectedCameraDistance = 0f;
		}

		private static bool CanToggle()
		{
			Player localPlayer = Player.m_localPlayer;
			if (Object.op_Implicit((Object)(object)localPlayer) && !((Character)localPlayer).IsDead() && !((Character)localPlayer).InCutscene() && !GameCamera.InFreeFly() && !Console.IsVisible() && !Menu.IsVisible() && !InventoryGui.IsVisible() && !StoreGui.IsVisible() && !Minimap.IsOpen() && !Hud.IsPieceSelectionVisible() && !Hud.InRadial())
			{
				if (!((Object)(object)Chat.instance == (Object)null))
				{
					return !Chat.instance.HasFocus();
				}
				return true;
			}
			return false;
		}

		private static bool IsShortcutDown(KeyboardShortcut shortcut)
		{
			//IL_0002: 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_002a: Unknown result type (might be due to invalid IL or missing references)
			if ((int)((KeyboardShortcut)(ref shortcut)).MainKey == 0 || !ZInput.GetKeyDown(((KeyboardShortcut)(ref shortcut)).MainKey, true))
			{
				return false;
			}
			foreach (KeyCode modifier in ((KeyboardShortcut)(ref shortcut)).Modifiers)
			{
				if (!ZInput.GetKey(modifier, true))
				{
					return false;
				}
			}
			return true;
		}

		private static void ShowState(bool enabled)
		{
			Player localPlayer = Player.m_localPlayer;
			if (localPlayer != null)
			{
				((Character)localPlayer).Message((MessageType)1, enabled ? "First person enabled" : "First person disabled", 0, (Sprite)null, false);
			}
		}
	}
	internal static class ViewController
	{
		private const float BackwardOffset = 0.4f;

		internal static void Apply(GameCamera camera, Player player)
		{
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: 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)
			//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_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: 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_0072: Unknown result type (might be due to invalid IL or missing references)
			if (Object.op_Implicit((Object)(object)camera) && Object.op_Implicit((Object)(object)player) && !((Character)player).IsAttached() && !((Character)player).InCutscene())
			{
				Quaternion rotation = ((Component)camera).transform.rotation;
				Vector3 forward = ((Component)camera).transform.forward;
				Vector3 val = Vector3.ProjectOnPlane(forward, Vector3.up);
				if (!Menu.IsVisible() && ((Vector3)(ref val)).sqrMagnitude > Mathf.Epsilon)
				{
					((Character)player).SetLookDir(forward, 0f);
					((Component)player).transform.rotation = Quaternion.LookRotation(val, Vector3.up);
				}
				Transform transform = ((Component)camera).transform;
				transform.position -= forward * 0.4f;
				((Component)camera).transform.rotation = rotation;
			}
		}
	}
	internal static class VegetationController
	{
		private const string CameraCullProperty = "_CamCull";

		private static readonly Dictionary<Material, float> CameraCullValues = new Dictionary<Material, float>();

		internal static void Apply()
		{
			Material[] array = Resources.FindObjectsOfTypeAll<Material>();
			for (int i = 0; i < array.Length; i++)
			{
				Apply(array[i]);
			}
		}

		internal static void Apply(GameObject root)
		{
			if (!Mode.Active || !Object.op_Implicit((Object)(object)root))
			{
				return;
			}
			Renderer[] componentsInChildren = root.GetComponentsInChildren<Renderer>(true);
			for (int i = 0; i < componentsInChildren.Length; i++)
			{
				Material[] sharedMaterials = componentsInChildren[i].sharedMaterials;
				for (int j = 0; j < sharedMaterials.Length; j++)
				{
					Apply(sharedMaterials[j]);
				}
			}
		}

		internal static void Restore()
		{
			foreach (KeyValuePair<Material, float> cameraCullValue in CameraCullValues)
			{
				if (Object.op_Implicit((Object)(object)cameraCullValue.Key))
				{
					cameraCullValue.Key.SetFloat("_CamCull", cameraCullValue.Value);
				}
			}
			CameraCullValues.Clear();
		}

		private static void Apply(Material material)
		{
			if (Object.op_Implicit((Object)(object)material) && material.HasProperty("_CamCull") && !CameraCullValues.ContainsKey(material))
			{
				float value = material.GetFloat("_CamCull");
				CameraCullValues.Add(material, value);
				material.SetFloat("_CamCull", 0f);
			}
		}
	}
	internal static class VisibilityController
	{
		private static readonly HashSet<Renderer> HiddenRenderers = new HashSet<Renderer>();

		private static readonly Dictionary<Animator, AnimatorCullingMode> AnimatorModes = new Dictionary<Animator, AnimatorCullingMode>();

		private static Player hiddenPlayer;

		private static Player equippedPlayer;

		private static GameObject leftHandItem;

		private static GameObject rightHandItem;

		internal static void TrackHeldItems(Player player, GameObject leftItem, GameObject rightItem)
		{
			equippedPlayer = player;
			leftHandItem = leftItem;
			rightHandItem = rightItem;
			if (Object.op_Implicit((Object)(object)player) && (Object)(object)player == (Object)(object)hiddenPlayer)
			{
				HideCurrentVisuals();
			}
		}

		internal static void SetHidden(Player player, bool hidden)
		{
			if (!hidden || !Object.op_Implicit((Object)(object)player))
			{
				Restore();
			}
			else if ((Object)(object)hiddenPlayer != (Object)(object)player)
			{
				Restore();
				hiddenPlayer = player;
				HideCurrentVisuals();
			}
		}

		internal static void Restore()
		{
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			foreach (Renderer hiddenRenderer in HiddenRenderers)
			{
				if (Object.op_Implicit((Object)(object)hiddenRenderer))
				{
					hiddenRenderer.forceRenderingOff = false;
				}
			}
			foreach (KeyValuePair<Animator, AnimatorCullingMode> animatorMode in AnimatorModes)
			{
				if (Object.op_Implicit((Object)(object)animatorMode.Key))
				{
					animatorMode.Key.cullingMode = animatorMode.Value;
				}
			}
			HiddenRenderers.Clear();
			AnimatorModes.Clear();
			hiddenPlayer = null;
		}

		private static void HideCurrentVisuals()
		{
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			Renderer[] componentsInChildren = ((Component)hiddenPlayer).GetComponentsInChildren<Renderer>(true);
			foreach (Renderer val in componentsInChildren)
			{
				if (Object.op_Implicit((Object)(object)val) && !val.forceRenderingOff)
				{
					val.forceRenderingOff = true;
					HiddenRenderers.Add(val);
				}
			}
			Animator[] componentsInChildren2 = ((Component)hiddenPlayer).GetComponentsInChildren<Animator>(true);
			foreach (Animator val2 in componentsInChildren2)
			{
				if (Object.op_Implicit((Object)(object)val2) && !AnimatorModes.ContainsKey(val2))
				{
					AnimatorModes.Add(val2, val2.cullingMode);
					val2.cullingMode = (AnimatorCullingMode)0;
				}
			}
			if ((Object)(object)hiddenPlayer == (Object)(object)equippedPlayer)
			{
				ShowHeldItem(leftHandItem);
				ShowHeldItem(rightHandItem);
			}
		}

		private static void ShowHeldItem(GameObject item)
		{
			if (!Object.op_Implicit((Object)(object)item))
			{
				return;
			}
			Renderer[] componentsInChildren = item.GetComponentsInChildren<Renderer>(true);
			foreach (Renderer val in componentsInChildren)
			{
				if (Object.op_Implicit((Object)(object)val) && HiddenRenderers.Remove(val))
				{
					val.forceRenderingOff = false;
				}
			}
		}
	}
}