Decompiled source of FreeLook v0.1.0

FreeLook.dll

Decompiled 4 days ago
using System;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;
using UnityEngine.UI;

[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: AssemblyCompany("isimp")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyCopyright("Copyright (c) 2026 isimp")]
[assembly: AssemblyDescription("Hold a key to look around while your character keeps facing, and aiming, where it was.")]
[assembly: AssemblyFileVersion("0.1.0.0")]
[assembly: AssemblyInformationalVersion("0.1.0+753d2dbc1973276e29ad501f98ce962b194f37a8")]
[assembly: AssemblyProduct("FreeLook")]
[assembly: AssemblyTitle("FreeLook")]
[assembly: AssemblyVersion("0.1.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace FreeLook
{
	public static class Glance
	{
		private static float _yawVel;

		private static float _pitchVel;

		private const float Negligible = 0.01f;

		private const float FullGlance = 12f;

		private static readonly FieldRef<Player, float> BasePitch = ResolveLookPitch();

		public static bool Active { get; private set; }

		public static float Yaw { get; private set; }

		public static float Pitch { get; private set; }

		public static bool HasOffset
		{
			get
			{
				if (!Active && !(Mathf.Abs(Yaw) > 0.01f))
				{
					return Mathf.Abs(Pitch) > 0.01f;
				}
				return true;
			}
		}

		public static float Turned => Mathf.Clamp01(Mathf.Max(Mathf.Abs(Yaw), Mathf.Abs(Pitch)) / 12f);

		private static FieldRef<Player, float> ResolveLookPitch()
		{
			try
			{
				return AccessTools.FieldRefAccess<Player, float>("m_lookPitch");
			}
			catch (Exception ex)
			{
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogWarning((object)("FreeLook: Player.m_lookPitch could not be read (" + ex.Message + "); the up/down limit will not account for the look you already had."));
				}
				return null;
			}
		}

		public static float PitchOf(Player player)
		{
			if (BasePitch == null)
			{
				return 0f;
			}
			return BasePitch.Invoke(player);
		}

		public static void Update(float dt)
		{
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null)
			{
				Active = false;
				Yaw = (Pitch = 0f);
				_yawVel = (_pitchVel = 0f);
				return;
			}
			Active = Plugin.Enabled && Input.GetKey(Plugin.Key) && !((Character)localPlayer).IsDead() && (!Plugin.BlockInBuildMode || !((Character)localPlayer).InPlaceMode()) && !Blocked();
			if (Active)
			{
				return;
			}
			if (Mathf.Abs(Yaw) <= 0.01f && Mathf.Abs(Pitch) <= 0.01f)
			{
				Yaw = (Pitch = 0f);
				_yawVel = (_pitchVel = 0f);
				return;
			}
			float returnTime = Plugin.ReturnTime;
			if (returnTime <= 0f)
			{
				Yaw = (Pitch = 0f);
				_yawVel = (_pitchVel = 0f);
			}
			else
			{
				Yaw = Mathf.SmoothDamp(Yaw, 0f, ref _yawVel, returnTime, float.PositiveInfinity, dt);
				Pitch = Mathf.SmoothDamp(Pitch, 0f, ref _pitchVel, returnTime, float.PositiveInfinity, dt);
			}
		}

		public static void Accumulate(Vector2 mouseLook, Player player)
		{
			//IL_000b: 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)
			float sensitivity = Plugin.Sensitivity;
			Yaw = Advance(Yaw, mouseLook.x * sensitivity, 0f - Plugin.MaxYaw, Plugin.MaxYaw);
			float num = PitchOf(player);
			float pitchCeiling = Plugin.PitchCeiling;
			float high = Mathf.Min(Plugin.MaxPitch, Mathf.Max(0f, pitchCeiling - num));
			float low = Mathf.Max(0f - Plugin.MaxPitch, Mathf.Min(0f, 0f - pitchCeiling - num));
			Pitch = Advance(Pitch, (0f - mouseLook.y) * sensitivity, low, high);
		}

		private static float Advance(float current, float delta, float low, float high)
		{
			if (delta == 0f)
			{
				return Mathf.Clamp(current, low, high);
			}
			float num = ((delta > 0f) ? high : low);
			float num2 = num - current;
			if (num2 * delta <= 0f)
			{
				return Mathf.Clamp(current, low, high);
			}
			float softLimit = Plugin.SoftLimit;
			if (softLimit > 0f)
			{
				float num3 = Mathf.Abs(num) * softLimit;
				if (num3 > 0f)
				{
					delta *= Mathf.SmoothStep(0f, 1f, Mathf.Clamp01(Mathf.Abs(num2) / num3));
				}
			}
			return Mathf.Clamp(current + delta, low, high);
		}

		private static bool Blocked()
		{
			if (InventoryGui.IsVisible())
			{
				return true;
			}
			if (StoreGui.IsVisible())
			{
				return true;
			}
			if (Menu.IsVisible())
			{
				return true;
			}
			if (Console.IsVisible())
			{
				return true;
			}
			if (TextInput.IsVisible())
			{
				return true;
			}
			if ((Object)(object)Minimap.instance != (Object)null && Minimap.IsOpen())
			{
				return true;
			}
			if (Hud.InRadial())
			{
				return true;
			}
			if ((Object)(object)Chat.instance != (Object)null && Chat.instance.HasFocus())
			{
				return true;
			}
			if (PlayerCustomizaton.IsBarberGuiVisible())
			{
				return true;
			}
			if (GameCamera.InFreeFly())
			{
				return true;
			}
			return false;
		}
	}
	[HarmonyPatch(typeof(Player), "SetMouseLook")]
	internal static class SetMouseLookPatch
	{
		private static bool Prefix(Player __instance, Vector2 mouseLook)
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			if (!Glance.Active)
			{
				return true;
			}
			if ((Object)(object)__instance != (Object)(object)Player.m_localPlayer)
			{
				return true;
			}
			Glance.Accumulate(mouseLook, __instance);
			return false;
		}
	}
	[HarmonyPatch(typeof(GameCamera), "GetCameraPosition")]
	internal static class GetCameraPositionPatch
	{
		private static Transform _borrowed;

		private static Quaternion _saved;

		private static void Prefix()
		{
			//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_005e: 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)
			//IL_0077: 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_008c: Unknown result type (might be due to invalid IL or missing references)
			Restore();
			if (!Glance.HasOffset)
			{
				return;
			}
			Player localPlayer = Player.m_localPlayer;
			if (!((Object)(object)localPlayer == (Object)null))
			{
				Transform eye = ((Character)localPlayer).m_eye;
				if (!((Object)(object)eye == (Object)null))
				{
					float num = Mathf.Clamp(Glance.PitchOf(localPlayer) + Glance.Pitch, 0f - Plugin.PitchCeiling, Plugin.PitchCeiling);
					_borrowed = eye;
					_saved = eye.rotation;
					eye.rotation = ((Character)localPlayer).GetLookYaw() * Quaternion.Euler(0f, Glance.Yaw, 0f) * Quaternion.Euler(num, 0f, 0f);
				}
			}
		}

		private static void Postfix()
		{
			Restore();
		}

		private static void Restore()
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_borrowed == (Object)null)
			{
				_borrowed = null;
				return;
			}
			_borrowed.rotation = _saved;
			_borrowed = null;
		}
	}
	[HarmonyPatch(typeof(Hud), "UpdateCrosshair")]
	internal static class UpdateCrosshairPatch
	{
		private static void Postfix(Hud __instance)
		{
			if (Plugin.FadeCrosshair)
			{
				float turned = Glance.Turned;
				if (!(turned <= 0f))
				{
					float keep = Mathf.Lerp(1f, Plugin.CrosshairAlpha, turned);
					Dim(__instance.m_crosshair, keep);
					Dim(__instance.m_crosshairBow, keep);
				}
			}
		}

		private static void Dim(Image image, float keep)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: 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 (!((Object)(object)image == (Object)null))
			{
				Color color = ((Graphic)image).color;
				color.a *= keep;
				((Graphic)image).color = color;
			}
		}
	}
	[BepInPlugin("isimp.FreeLook", "FreeLook", "0.1.0")]
	[BepInProcess("valheim.exe")]
	public class Plugin : BaseUnityPlugin
	{
		public const string Guid = "isimp.FreeLook";

		public const string Version = "0.1.0";

		public static ManualLogSource Log;

		private static bool _broken;

		private static ConfigEntry<bool> _enabled;

		private static ConfigEntry<KeyCode> _key;

		private static ConfigEntry<bool> _blockInBuildMode;

		private static ConfigEntry<float> _maxYaw;

		private static ConfigEntry<float> _maxPitch;

		private static ConfigEntry<float> _pitchCeiling;

		private static ConfigEntry<float> _softLimit;

		private static ConfigEntry<float> _returnTime;

		private static ConfigEntry<float> _sensitivity;

		private static ConfigEntry<bool> _fadeCrosshair;

		private static ConfigEntry<float> _crosshairAlpha;

		private Harmony _harmony;

		public static bool Enabled
		{
			get
			{
				if (!_broken)
				{
					if (_enabled != null)
					{
						return _enabled.Value;
					}
					return true;
				}
				return false;
			}
		}

		public static KeyCode Key => (KeyCode)(((??)_key?.Value) ?? 308);

		public static bool BlockInBuildMode
		{
			get
			{
				if (_blockInBuildMode != null)
				{
					return _blockInBuildMode.Value;
				}
				return true;
			}
		}

		public static float MaxYaw => _maxYaw?.Value ?? 135f;

		public static float MaxPitch => _maxPitch?.Value ?? 70f;

		public static float PitchCeiling => _pitchCeiling?.Value ?? 89f;

		public static float SoftLimit => _softLimit?.Value ?? 0.35f;

		public static float ReturnTime => _returnTime?.Value ?? 0.15f;

		public static float Sensitivity => _sensitivity?.Value ?? 1f;

		public static bool FadeCrosshair
		{
			get
			{
				if (_fadeCrosshair != null)
				{
					return _fadeCrosshair.Value;
				}
				return true;
			}
		}

		public static float CrosshairAlpha => _crosshairAlpha?.Value ?? 0.15f;

		private void Awake()
		{
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: Expected O, but got Unknown
			//IL_00da: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e4: Expected O, but got Unknown
			//IL_0117: Unknown result type (might be due to invalid IL or missing references)
			//IL_0121: Expected O, but got Unknown
			//IL_0154: Unknown result type (might be due to invalid IL or missing references)
			//IL_015e: Expected O, but got Unknown
			//IL_0191: Unknown result type (might be due to invalid IL or missing references)
			//IL_019b: Expected O, but got Unknown
			//IL_01ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d8: Expected O, but got Unknown
			//IL_022b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0235: Expected O, but got Unknown
			//IL_0240: Unknown result type (might be due to invalid IL or missing references)
			//IL_024a: Expected O, but got Unknown
			//IL_02bf: Unknown result type (might be due to invalid IL or missing references)
			Log = ((BaseUnityPlugin)this).Logger;
			_enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Turn free look off without removing the mod.");
			_key = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("General", "Key", (KeyCode)308, "Hold this to look around. On release, the camera returns to where the character faces.");
			_blockInBuildMode = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "BlockInBuildMode", true, "Disable free look while placing a building piece, so the key keeps its normal alternate placement function.");
			_maxYaw = ((BaseUnityPlugin)this).Config.Bind<float>("Look", "MaxYaw", 135f, new ConfigDescription("How far left or right you can glance, in degrees from where the character faces.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(10f, 180f), Array.Empty<object>()));
			_maxPitch = ((BaseUnityPlugin)this).Config.Bind<float>("Look", "MaxPitch", 70f, new ConfigDescription("How far up or down you can glance, in degrees away from where you were already looking.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(5f, 89f), Array.Empty<object>()));
			_pitchCeiling = ((BaseUnityPlugin)this).Config.Bind<float>("Look", "PitchCeiling", 89f, new ConfigDescription("Hard limit on the resulting up/down angle, counting the pitch you already had. Valheim's own limit is 89, which is also the maximum here.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(30f, 89f), Array.Empty<object>()));
			_softLimit = ((BaseUnityPlugin)this).Config.Bind<float>("Look", "SoftLimit", 0.35f, new ConfigDescription("Fraction of the travel nearest each limit over which the view slows down. 0 is a hard stop; 0.35 slows the last third.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 0.9f), Array.Empty<object>()));
			_sensitivity = ((BaseUnityPlugin)this).Config.Bind<float>("Look", "Sensitivity", 1f, new ConfigDescription("Multiplies mouse movement while free looking, on top of the game's own sensitivity.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 3f), Array.Empty<object>()));
			_returnTime = ((BaseUnityPlugin)this).Config.Bind<float>("Return", "ReturnTime", 0.15f, new ConfigDescription("Seconds the camera takes to ease back to centre after you let go. 0 snaps back instantly.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
			_fadeCrosshair = ((BaseUnityPlugin)this).Config.Bind<bool>("Crosshair", "FadeCrosshair", true, "Fade the crosshair as the camera turns away, since aim stays where the character faces.");
			_crosshairAlpha = ((BaseUnityPlugin)this).Config.Bind<float>("Crosshair", "CrosshairAlpha", 0.15f, new ConfigDescription("How much of the crosshair is left at a full glance, as a fraction of its normal opacity. 0 hides it completely.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
			_harmony = new Harmony("isimp.FreeLook");
			bool num = Apply(typeof(SetMouseLookPatch), "Player.SetMouseLook");
			bool flag = Apply(typeof(GetCameraPositionPatch), "GameCamera.GetCameraPosition");
			Apply(typeof(UpdateCrosshairPatch), "Hud.UpdateCrosshair");
			_broken = !num || !flag;
			if (_broken)
			{
				Log.LogError((object)"FreeLook: free look is off for this session. The game's camera is untouched.");
			}
			else
			{
				Log.LogInfo((object)string.Format("FreeLook {0}: free look on {1}.", "0.1.0", Key));
			}
		}

		private bool Apply(Type patch, string target)
		{
			try
			{
				_harmony.CreateClassProcessor(patch).Patch();
				return true;
			}
			catch (Exception ex)
			{
				Log.LogError((object)("FreeLook: could not patch " + target + " — " + ex.Message));
				return false;
			}
		}

		private void OnDestroy()
		{
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}

		private void Update()
		{
			Glance.Update(Time.deltaTime);
		}
	}
}