Decompiled source of ImmersiveBuildCamera v0.2.3

BepInEx/plugins/ImmersiveBuildCamera/ImmersiveBuildCamera.dll

Decompiled an hour ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyCompany("ImmersiveBuildCamera")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("ImmersiveBuildCamera")]
[assembly: AssemblyTitle("ImmersiveBuildCamera")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.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 ImmersiveBuildCamera
{
	internal static class BuildCameraState
	{
		private static int _toggledShoulderDirection;

		private static readonly FieldInfo? RightItemField = AccessTools.Field(typeof(Humanoid), "m_rightItem");

		internal static bool Active { get; private set; }

		internal static bool PrecisionMovementActive { get; private set; }

		internal static bool ShoulderPeekActive
		{
			get
			{
				if (Active)
				{
					return GetShoulderDirection() != 0;
				}
				return false;
			}
		}

		internal static void Update(Player player)
		{
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)player == (Object)null || (Object)(object)player != (Object)(object)Player.m_localPlayer)
			{
				return;
			}
			if (!CanUseImmersiveCamera(player))
			{
				SetActive(active: false);
				return;
			}
			if (Input.GetKeyDown(Plugin.ToggleCameraKey.Value))
			{
				SetActive(!Active);
			}
			if (Active)
			{
				UpdateShoulderPeekState();
				if (Plugin.EnablePrecisionMovement.Value && Input.GetKeyDown(Plugin.TogglePrecisionMovementKey.Value))
				{
					SetPrecisionMovement(!PrecisionMovementActive);
				}
			}
		}

		internal static int GetShoulderDirection()
		{
			if (!Active)
			{
				return 0;
			}
			if (Plugin.ToggleShoulderPeek.Value)
			{
				return _toggledShoulderDirection;
			}
			return GetHeldShoulderDirection();
		}

		private static void UpdateShoulderPeekState()
		{
			//IL_0018: 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)
			if (!Plugin.ToggleShoulderPeek.Value)
			{
				_toggledShoulderDirection = 0;
				return;
			}
			bool keyDown = Input.GetKeyDown(Plugin.LeftShoulderKey.Value);
			bool keyDown2 = Input.GetKeyDown(Plugin.RightShoulderKey.Value);
			if (keyDown && keyDown2)
			{
				_toggledShoulderDirection = 0;
			}
			else if (keyDown)
			{
				ToggleShoulderDirection(-1);
			}
			else if (keyDown2)
			{
				ToggleShoulderDirection(1);
			}
		}

		private static void ToggleShoulderDirection(int direction)
		{
			if (_toggledShoulderDirection == direction)
			{
				_toggledShoulderDirection = 0;
			}
			else
			{
				_toggledShoulderDirection = direction;
			}
		}

		private static int GetHeldShoulderDirection()
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			bool key = Input.GetKey(Plugin.LeftShoulderKey.Value);
			bool key2 = Input.GetKey(Plugin.RightShoulderKey.Value);
			if (key && !key2)
			{
				return -1;
			}
			if (key2 && !key)
			{
				return 1;
			}
			return 0;
		}

		private static void SetActive(bool active)
		{
			if (Active != active)
			{
				Active = active;
				if (active)
				{
					PrecisionMovementActive = Plugin.EnablePrecisionMovement.Value && Plugin.PrecisionMovementDefaultOn.Value;
				}
				else
				{
					PrecisionMovementActive = false;
					_toggledShoulderDirection = 0;
					PlayerRendererVisibility.ForceVisible();
				}
				Plugin.Log.LogInfo((object)(active ? ("Immersive build camera active. Precision movement: " + (PrecisionMovementActive ? "on" : "off") + ".") : "Immersive build camera inactive."));
			}
		}

		private static void SetPrecisionMovement(bool active)
		{
			if (PrecisionMovementActive != active)
			{
				PrecisionMovementActive = active;
				Plugin.Log.LogInfo((object)(active ? "Precision movement active." : "Precision movement inactive."));
			}
		}

		private static bool CanUseImmersiveCamera(Player player)
		{
			if (!IsSafePlayerState(player))
			{
				return false;
			}
			if (!HasBuildTool(player))
			{
				return false;
			}
			return true;
		}

		private static bool HasBuildTool(Player player)
		{
			if (RightItemField == null)
			{
				Plugin.Log.LogWarning((object)"Could not find Humanoid.m_rightItem.");
				return false;
			}
			object? value = RightItemField.GetValue(player);
			ItemData val = (ItemData)((value is ItemData) ? value : null);
			if (val == null)
			{
				return false;
			}
			if (val.m_shared == null)
			{
				return false;
			}
			return (Object)(object)val.m_shared.m_buildPieces != (Object)null;
		}

		private static bool IsSafePlayerState(Player player)
		{
			if (((Character)player).IsDead())
			{
				return false;
			}
			if (((Character)player).IsAttached())
			{
				return false;
			}
			if (((Character)player).IsSwimming())
			{
				return false;
			}
			if (InventoryGui.IsVisible())
			{
				return false;
			}
			if (Menu.IsVisible())
			{
				return false;
			}
			if (Minimap.IsOpen())
			{
				return false;
			}
			return true;
		}
	}
	[HarmonyPatch(typeof(GameCamera))]
	[HarmonyPatch("UpdateCamera")]
	internal static class GameCameraUpdatePatch
	{
		private static readonly FieldInfo? CameraField = AccessTools.Field(typeof(GameCamera), "m_camera");

		private static float _originalFov;

		private static float _originalNearClip;

		private static bool _savedOriginals;

		private static int _cachedCollisionMask = -1;

		private static void Postfix(GameCamera __instance)
		{
			if ((Object)(object)__instance == (Object)null)
			{
				return;
			}
			Camera val = GetCamera(__instance) ?? Camera.main;
			if (!((Object)(object)val == (Object)null))
			{
				if (!_savedOriginals)
				{
					_originalFov = val.fieldOfView;
					_originalNearClip = val.nearClipPlane;
					_savedOriginals = true;
				}
				if (!BuildCameraState.Active)
				{
					RestoreCamera(val);
				}
				else
				{
					ApplyImmersiveBuildCamera(__instance, val);
				}
			}
		}

		private static Camera? GetCamera(GameCamera gameCamera)
		{
			if (CameraField == null)
			{
				return null;
			}
			object? value = CameraField.GetValue(gameCamera);
			return (Camera?)((value is Camera) ? value : null);
		}

		private static void ApplyImmersiveBuildCamera(GameCamera gameCamera, Camera camera)
		{
			//IL_002e: 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_0034: 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_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: 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_00bf: 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_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: 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_006f: 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_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_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			//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)
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: 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)
			Player localPlayer = Player.m_localPlayer;
			if (!((Object)(object)localPlayer == (Object)null))
			{
				Transform val = (((Object)(object)((Character)localPlayer).m_eye != (Object)null) ? ((Character)localPlayer).m_eye : ((Component)localPlayer).transform);
				Vector3 position = val.position;
				Vector3 val2 = position;
				Quaternion rotation = val.rotation;
				int shoulderDirection = BuildCameraState.GetShoulderDirection();
				if (shoulderDirection != 0)
				{
					float num = Plugin.ShoulderOffsetX.Value * (float)shoulderDirection;
					val2 += val.right * num;
					val2 += val.up * Plugin.ShoulderOffsetY.Value;
					val2 -= val.forward * Plugin.ShoulderDistance.Value;
					val2 = ResolveCameraCollision(position, val2);
				}
				((Component)gameCamera).transform.position = val2;
				((Component)gameCamera).transform.rotation = rotation;
				camera.fieldOfView = Plugin.BuildFov.Value;
				camera.nearClipPlane = Plugin.NearClip.Value;
			}
		}

		private static Vector3 ResolveCameraCollision(Vector3 anchorPosition, Vector3 desiredPosition)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: 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_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: 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_0048: Unknown result type (might be due to invalid IL or missing references)
			Vector3 val = desiredPosition - anchorPosition;
			float magnitude = ((Vector3)(ref val)).magnitude;
			if (magnitude <= 0.001f)
			{
				return desiredPosition;
			}
			Vector3 val2 = val / magnitude;
			RaycastHit val3 = default(RaycastHit);
			if (!Physics.SphereCast(anchorPosition, Mathf.Max(0.01f, Plugin.CollisionRadius.Value), val2, ref val3, magnitude, GetCollisionMask(), (QueryTriggerInteraction)1))
			{
				return desiredPosition;
			}
			float num = Mathf.Max(0f, ((RaycastHit)(ref val3)).distance - Plugin.CollisionRadius.Value);
			return anchorPosition + val2 * num;
		}

		private static int GetCollisionMask()
		{
			if (_cachedCollisionMask != -1)
			{
				return _cachedCollisionMask;
			}
			int num = LayerMask.GetMask(new string[5] { "Default", "static_solid", "terrain", "piece", "piece_nonsolid" });
			if (num == 0)
			{
				num = -5;
				Plugin.Log.LogWarning((object)"Could not resolve Valheim-specific collision layers. Falling back to Physics.DefaultRaycastLayers.");
			}
			_cachedCollisionMask = num;
			return _cachedCollisionMask;
		}

		private static void RestoreCamera(Camera camera)
		{
			if (_savedOriginals)
			{
				camera.fieldOfView = _originalFov;
				camera.nearClipPlane = _originalNearClip;
			}
		}
	}
	[HarmonyPatch(typeof(Player))]
	[HarmonyPatch("Update")]
	internal static class PlayerUpdatePatch
	{
		private static void Postfix(Player __instance)
		{
			BuildCameraState.Update(__instance);
			PlayerRendererVisibility.Update(__instance);
		}
	}
	internal static class PlayerRendererVisibility
	{
		private static readonly Dictionary<Renderer, bool> OriginalRendererStates = new Dictionary<Renderer, bool>();

		private static readonly List<Renderer> DeadRenderers = new List<Renderer>();

		private static readonly FieldInfo? PlacementGhostField = AccessTools.Field(typeof(Player), "m_placementGhost");

		private static Player? _cachedPlayer;

		private static bool _hidden;

		private static float _nextRefreshTime;

		internal static void Update(Player player)
		{
			if (!((Object)(object)player == (Object)null) && !((Object)(object)player != (Object)(object)Player.m_localPlayer))
			{
				bool shouldHide = Plugin.HideLocalPlayerWhenImmersive.Value && BuildCameraState.Active && !BuildCameraState.ShoulderPeekActive;
				Apply(player, shouldHide);
			}
		}

		internal static void ForceVisible()
		{
			RestoreRendererStates();
			ResetCache();
		}

		private static void Apply(Player player, bool shouldHide)
		{
			if ((Object)(object)player == (Object)null)
			{
				ForceVisible();
				return;
			}
			if ((Object)(object)_cachedPlayer != (Object)null && (Object)(object)_cachedPlayer != (Object)(object)player)
			{
				ForceVisible();
			}
			_cachedPlayer = player;
			if (shouldHide)
			{
				HidePlayerRenderers(player);
			}
			else
			{
				RestoreRendererStates();
			}
		}

		private static void HidePlayerRenderers(Player player)
		{
			if (!_hidden)
			{
				OriginalRendererStates.Clear();
				_hidden = true;
				_nextRefreshTime = 0f;
			}
			if (OriginalRendererStates.Count == 0 || Time.unscaledTime >= _nextRefreshTime)
			{
				RefreshRendererCache(player);
				_nextRefreshTime = Time.unscaledTime + 0.25f;
			}
			foreach (Renderer key in OriginalRendererStates.Keys)
			{
				if (!((Object)(object)key == (Object)null) && key.enabled)
				{
					key.enabled = false;
				}
			}
		}

		private static void RefreshRendererCache(Player player)
		{
			RemoveDestroyedRenderers();
			Renderer[] componentsInChildren = ((Component)player).GetComponentsInChildren<Renderer>(true);
			foreach (Renderer val in componentsInChildren)
			{
				if (!((Object)(object)val == (Object)null) && !ShouldSkipRenderer(player, val) && !OriginalRendererStates.ContainsKey(val))
				{
					OriginalRendererStates.Add(val, val.enabled);
				}
			}
		}

		private static bool ShouldSkipRenderer(Player player, Renderer renderer)
		{
			GameObject placementGhost = GetPlacementGhost(player);
			if ((Object)(object)placementGhost != (Object)null && (Object)(object)((Component)renderer).transform != (Object)null && ((Component)renderer).transform.IsChildOf(placementGhost.transform))
			{
				return true;
			}
			return false;
		}

		private static GameObject? GetPlacementGhost(Player player)
		{
			if (PlacementGhostField == null)
			{
				return null;
			}
			object? value = PlacementGhostField.GetValue(player);
			return (GameObject?)((value is GameObject) ? value : null);
		}

		private static void RestoreRendererStates()
		{
			if (!_hidden && OriginalRendererStates.Count == 0)
			{
				return;
			}
			foreach (KeyValuePair<Renderer, bool> originalRendererState in OriginalRendererStates)
			{
				Renderer key = originalRendererState.Key;
				if (!((Object)(object)key == (Object)null))
				{
					key.enabled = originalRendererState.Value;
				}
			}
			OriginalRendererStates.Clear();
			DeadRenderers.Clear();
			_hidden = false;
			_nextRefreshTime = 0f;
		}

		private static void RemoveDestroyedRenderers()
		{
			DeadRenderers.Clear();
			foreach (Renderer key in OriginalRendererStates.Keys)
			{
				if ((Object)(object)key == (Object)null)
				{
					DeadRenderers.Add(key);
				}
			}
			foreach (Renderer deadRenderer in DeadRenderers)
			{
				OriginalRendererStates.Remove(deadRenderer);
			}
			DeadRenderers.Clear();
		}

		private static void ResetCache()
		{
			_cachedPlayer = null;
			_hidden = false;
			_nextRefreshTime = 0f;
			OriginalRendererStates.Clear();
			DeadRenderers.Clear();
		}
	}
	[BepInPlugin("com.geronimo.valheim.immersivebuildcamera", "Immersive Build Camera", "0.2.3")]
	[BepInProcess("valheim.exe")]
	public sealed class Plugin : BaseUnityPlugin
	{
		public const string PluginGuid = "com.geronimo.valheim.immersivebuildcamera";

		public const string PluginName = "Immersive Build Camera";

		public const string PluginVersion = "0.2.3";

		internal static ManualLogSource Log;

		internal static ConfigEntry<KeyCode> ToggleCameraKey;

		internal static ConfigEntry<KeyCode> TogglePrecisionMovementKey;

		internal static ConfigEntry<KeyCode> LeftShoulderKey;

		internal static ConfigEntry<KeyCode> RightShoulderKey;

		internal static ConfigEntry<float> BuildFov;

		internal static ConfigEntry<float> NearClip;

		internal static ConfigEntry<float> ShoulderOffsetX;

		internal static ConfigEntry<float> ShoulderOffsetY;

		internal static ConfigEntry<float> ShoulderDistance;

		internal static ConfigEntry<float> CollisionRadius;

		internal static ConfigEntry<bool> ToggleShoulderPeek;

		internal static ConfigEntry<bool> EnablePrecisionMovement;

		internal static ConfigEntry<bool> PrecisionMovementDefaultOn;

		internal static ConfigEntry<float> PrecisionMoveMultiplier;

		internal static ConfigEntry<bool> HideLocalPlayerWhenImmersive;

		private Harmony _harmony;

		private void Awake()
		{
			//IL_0217: Unknown result type (might be due to invalid IL or missing references)
			//IL_0221: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			ToggleCameraKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Input", "ToggleCameraKey", (KeyCode)308, "Press this while using a build tool to toggle immersive build camera.");
			TogglePrecisionMovementKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Input", "TogglePrecisionMovementKey", (KeyCode)306, "Press this while immersive build camera is active to toggle slow precision movement.");
			LeftShoulderKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Input", "LeftShoulderKey", (KeyCode)113, "Hold or press this while immersive build camera is active to peek left, depending on ToggleShoulderPeek.");
			RightShoulderKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Input", "RightShoulderKey", (KeyCode)101, "Hold or press this while immersive build camera is active to peek right, depending on ToggleShoulderPeek.");
			BuildFov = ((BaseUnityPlugin)this).Config.Bind<float>("Camera", "BuildFov", 68f, "Field of view while immersive build camera is active.");
			NearClip = ((BaseUnityPlugin)this).Config.Bind<float>("Camera", "NearClip", 0.04f, "Near clipping plane while immersive build camera is active.");
			ShoulderOffsetX = ((BaseUnityPlugin)this).Config.Bind<float>("Shoulder Peek", "ShoulderOffsetX", 0.75f, "Horizontal shoulder offset. Higher values make shoulder peek more useful but more likely to hit collision.");
			ShoulderOffsetY = ((BaseUnityPlugin)this).Config.Bind<float>("Shoulder Peek", "ShoulderOffsetY", 0.06f, "Vertical shoulder offset.");
			ShoulderDistance = ((BaseUnityPlugin)this).Config.Bind<float>("Shoulder Peek", "ShoulderDistance", 0.5f, "Backward shoulder camera distance.");
			CollisionRadius = ((BaseUnityPlugin)this).Config.Bind<float>("Shoulder Peek", "CollisionRadius", 0.1f, "Sphere radius used to prevent shoulder peek camera clipping into objects.");
			ToggleShoulderPeek = ((BaseUnityPlugin)this).Config.Bind<bool>("Shoulder Peek", "ToggleShoulderPeek", false, "If false, shoulder peek keys must be held. If true, shoulder peek keys toggle left, right, or centered.");
			EnablePrecisionMovement = ((BaseUnityPlugin)this).Config.Bind<bool>("Movement", "EnablePrecisionMovement", true, "Allow slow precision movement while immersive build camera is active.");
			PrecisionMovementDefaultOn = ((BaseUnityPlugin)this).Config.Bind<bool>("Movement", "PrecisionMovementDefaultOn", true, "Whether slow precision movement starts enabled whenever immersive build camera is toggled on.");
			PrecisionMoveMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("Movement", "PrecisionMoveMultiplier", 0.35f, "Movement input multiplier when precision movement is enabled. Lower means slower.");
			HideLocalPlayerWhenImmersive = ((BaseUnityPlugin)this).Config.Bind<bool>("Local Visibility", "HideLocalPlayerWhenImmersive", true, "Hide only the local player's renderers while immersive build camera is active and shoulder peek is not being used.");
			_harmony = new Harmony("com.geronimo.valheim.immersivebuildcamera");
			_harmony.PatchAll();
			PrecisionMovementPatches.Apply(_harmony);
			Log.LogInfo((object)"Immersive Build Camera loaded.");
		}

		private void OnDestroy()
		{
			PlayerRendererVisibility.ForceVisible();
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}
	}
	internal static class PrecisionMovementPatches
	{
		internal static void Apply(Harmony harmony)
		{
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Expected O, but got Unknown
			MethodInfo methodInfo = AccessTools.Method(typeof(Player), "SetControls", (Type[])null, (Type[])null);
			MethodInfo methodInfo2 = AccessTools.Method(typeof(PrecisionMovementPatches), "PrefixSetControls", (Type[])null, (Type[])null);
			if (methodInfo == null)
			{
				Plugin.Log.LogWarning((object)"Could not find Player.SetControls. Precision movement patch skipped.");
				return;
			}
			if (methodInfo2 == null)
			{
				Plugin.Log.LogWarning((object)"Could not find precision movement prefix.");
				return;
			}
			harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(methodInfo2), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			Plugin.Log.LogInfo((object)"Patched Player.SetControls for precision movement.");
		}

		private static void PrefixSetControls(Player __instance, ref Vector3 movedir, ref bool run, ref bool autoRun)
		{
			//IL_0047: 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)
			if (Plugin.EnablePrecisionMovement.Value && BuildCameraState.Active && BuildCameraState.PrecisionMovementActive && !((Object)(object)__instance != (Object)(object)Player.m_localPlayer))
			{
				float num = Mathf.Clamp(Plugin.PrecisionMoveMultiplier.Value, 0.05f, 1f);
				movedir *= num;
				run = false;
				autoRun = false;
			}
		}
	}
}