Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of FreeLook v0.1.0
FreeLook.dll
Decompiled 4 days agousing 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); } } }