Decompiled source of SurvivalView v1.0.1

SurvivalView.dll

Decompiled a day ago
using System;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyVersion("0.0.0.0")]
namespace SurvivalView;

[BepInPlugin("com.marccunningham.survivalview", "Survival View", "1.1.0")]
[BepInProcess("valheim.exe")]
public sealed class SurvivalViewPlugin : BaseUnityPlugin
{
	public const string PluginGuid = "com.marccunningham.survivalview";

	public const string PluginName = "Survival View";

	public const string PluginVersion = "1.1.0";

	internal static SurvivalViewPlugin Instance;

	private ConfigEntry<bool> enabled;

	private ConfigEntry<KeyCode> diagnosticKey;

	private ConfigEntry<bool> requireClosestZoom;

	private ConfigEntry<float> closestZoomTolerance;

	private ConfigEntry<float> heightAdjustmentMeters;

	private ConfigEntry<float> headForwardOffsetMeters;

	private ConfigEntry<float> headSideOffsetMeters;

	private ConfigEntry<bool> overrideFieldOfView;

	private ConfigEntry<float> survivalFieldOfView;

	private ConfigEntry<bool> enableGroundedMovement;

	private ConfigEntry<bool> movementRequiresSurvivalView;

	private ConfigEntry<float> backwardSpeedMultiplier;

	private ConfigEntry<float> sidewaysSpeedMultiplier;

	private ConfigEntry<float> accelerationSeconds;

	private ConfigEntry<float> decelerationSeconds;

	private ConfigEntry<float> sprintBuildUpSeconds;

	private ConfigEntry<float> sprintStartSpeedMultiplier;

	private ConfigEntry<float> sprintRecoverySeconds;

	private ConfigEntry<bool> lowStaminaExertionEnabled;

	private ConfigEntry<float> lowStaminaThreshold;

	private ConfigEntry<float> lowStaminaMovementMultiplier;

	private Harmony harmony;

	private bool getCameraPositionPatchInstalled;

	private bool cameraLensPatchInstalled;

	private bool controlsPatchInstalled;

	private bool jogSpeedPatchInstalled;

	private bool runSpeedPatchInstalled;

	private float lastKnownCameraDistance = float.PositiveInfinity;

	private float lastKnownMinimumCameraDistance = float.PositiveInfinity;

	private bool lastKnownViewActive;

	private bool viewStateLogged;

	private bool lastLoggedViewState;

	private Vector3 lastVanillaClosestCameraPosition;

	private Vector3 lastRaisedHeadCameraPosition;

	private Vector3 smoothedMoveDirection;

	private float sprintBuild;

	private void Awake()
	{
		Instance = this;
		BindConfig();
		InstallPatches();
		((BaseUnityPlugin)this).Logger.LogInfo((object)"Survival View 1.1.0 loaded. Scroll fully in to enter Raised Head View; scroll out to return to vanilla third person. Raised Head View keeps the exact vertical height of Valheim's closest third-person camera while placing the camera on the local player's head X/Z position. F11 writes diagnostics.");
	}

	private void OnDestroy()
	{
		if (harmony != null)
		{
			harmony.UnpatchSelf();
		}
		if ((Object)(object)Instance == (Object)(object)this)
		{
			Instance = null;
		}
	}

	private void Update()
	{
		//IL_0007: 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_0077: Unknown result type (might be due to invalid IL or missing references)
		//IL_007c: Unknown result type (might be due to invalid IL or missing references)
		if (Input.GetKeyDown(diagnosticKey.Value))
		{
			WriteDiagnostic();
		}
		Player localPlayer = Player.m_localPlayer;
		if ((Object)(object)localPlayer == (Object)null || !IsGroundedMovementActive(localPlayer))
		{
			sprintBuild = Mathf.MoveTowards(sprintBuild, 0f, Time.deltaTime * 4f);
			smoothedMoveDirection = Vector3.MoveTowards(smoothedMoveDirection, Vector3.zero, Time.deltaTime * 6f);
		}
		else
		{
			UpdateSprintBuild(localPlayer);
		}
	}

	private void BindConfig()
	{
		enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Turns Survival View and its optional grounded movement changes on or off.");
		diagnosticKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("General", "DiagnosticKey", (KeyCode)292, "Writes camera and movement diagnostics to BepInEx/LogOutput.log.");
		requireClosestZoom = ((BaseUnityPlugin)this).Config.Bind<bool>("Camera", "RequireClosestZoom", true, "When true, Raised Head View activates only when Valheim's camera is fully zoomed in using the normal mouse-wheel limit.");
		closestZoomTolerance = ((BaseUnityPlugin)this).Config.Bind<float>("Camera", "ClosestZoomToleranceMeters", 0.05f, "Small tolerance above GameCamera.m_minDistance used to recognise the exact closest normal third-person zoom.");
		heightAdjustmentMeters = ((BaseUnityPlugin)this).Config.Bind<float>("Camera", "HeightAdjustmentMeters", 0f, "Extra height above the closest vanilla third-person camera. Leave at 0 for the exact vanilla closest-zoom height requested by the player.");
		headForwardOffsetMeters = ((BaseUnityPlugin)this).Config.Bind<float>("Camera", "HeadForwardOffsetMeters", 0f, "Moves the Raised Head View a small horizontal amount forward from the head. Leave at 0 for exact head X/Z placement.");
		headSideOffsetMeters = ((BaseUnityPlugin)this).Config.Bind<float>("Camera", "HeadSideOffsetMeters", 0f, "Moves the Raised Head View sideways. Leave at 0 for a centered view.");
		overrideFieldOfView = ((BaseUnityPlugin)this).Config.Bind<bool>("Camera", "OverrideFieldOfView", true, "Uses SurvivalFieldOfView while Raised Head View is active. Disable this to keep Valheim's current FOV exactly.");
		survivalFieldOfView = ((BaseUnityPlugin)this).Config.Bind<float>("Camera", "SurvivalFieldOfView", 70f, "Vertical FOV while Raised Head View is active. A moderately narrower value makes the visible body, tools and weapon swings appear more natural and substantial.");
		enableGroundedMovement = ((BaseUnityPlugin)this).Config.Bind<bool>("Movement", "EnableGroundedMovement", true, "Enables slower backward and sideways movement, smooth acceleration, sprint build-up and exertion slowdown.");
		movementRequiresSurvivalView = ((BaseUnityPlugin)this).Config.Bind<bool>("Movement", "OnlyApplyMovementInSurvivalView", true, "When true, movement changes apply only while Raised Head View is active.");
		backwardSpeedMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("Movement", "BackwardSpeedMultiplier", 0.72f, "Movement speed multiplier while moving directly backward.");
		sidewaysSpeedMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("Movement", "SidewaysSpeedMultiplier", 0.86f, "Movement speed multiplier while strafing directly sideways.");
		accelerationSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("Movement", "AccelerationSeconds", 0.22f, "Time for movement input to build toward full strength. Higher values feel heavier and smoother.");
		decelerationSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("Movement", "DecelerationSeconds", 0.14f, "Time for movement input to settle after releasing a movement key.");
		sprintBuildUpSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("Movement", "SprintBuildUpSeconds", 0.38f, "Time for a sprint to build from its starting speed to full speed.");
		sprintStartSpeedMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("Movement", "SprintStartSpeedMultiplier", 0.76f, "Sprint speed multiplier at the instant sprinting begins. It rises to full speed during SprintBuildUpSeconds.");
		sprintRecoverySeconds = ((BaseUnityPlugin)this).Config.Bind<float>("Movement", "SprintRecoverySeconds", 0.3f, "Time before the sprint build-up resets after you stop running.");
		lowStaminaExertionEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Movement", "LowStaminaExertionLink", true, "Makes low stamina reduce movement and sprint build-up. This stacks naturally with You're Injured and is ready for a future You're Tired bridge.");
		lowStaminaThreshold = ((BaseUnityPlugin)this).Config.Bind<float>("Movement", "LowStaminaThreshold", 0.25f, "Stamina percentage below which exertion begins affecting movement. 0.25 means 25 percent.");
		lowStaminaMovementMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("Movement", "LowStaminaMovementMultiplier", 0.82f, "Minimum movement multiplier at zero stamina when LowStaminaExertionLink is enabled.");
	}

	private void InstallPatches()
	{
		//IL_0007: Unknown result type (might be due to invalid IL or missing references)
		//IL_0011: Expected O, but got Unknown
		//IL_006f: Unknown result type (might be due to invalid IL or missing references)
		//IL_007e: Expected O, but got Unknown
		//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
		//IL_00cc: Expected O, but got Unknown
		//IL_00f8: Unknown result type (might be due to invalid IL or missing references)
		//IL_0108: Expected O, but got Unknown
		//IL_0139: Unknown result type (might be due to invalid IL or missing references)
		//IL_0148: Expected O, but got Unknown
		//IL_0179: Unknown result type (might be due to invalid IL or missing references)
		//IL_0188: Expected O, but got Unknown
		harmony = new Harmony("com.marccunningham.survivalview");
		getCameraPositionPatchInstalled = TryPatch(FindMethod(typeof(GameCamera), "GetCameraPosition", new Type[3]
		{
			typeof(float),
			typeof(Vector3).MakeByRefType(),
			typeof(Quaternion).MakeByRefType()
		}), null, new HarmonyMethod(typeof(SurvivalViewPatches), "GameCameraGetCameraPositionPostfix", (Type[])null), "GameCamera.GetCameraPosition(float, out Vector3, out Quaternion)");
		cameraLensPatchInstalled = TryPatch(FindMethod(typeof(GameCamera), "UpdateCamera", new Type[1] { typeof(float) }), null, new HarmonyMethod(typeof(SurvivalViewPatches), "GameCameraUpdateCameraPostfix", (Type[])null), "GameCamera.UpdateCamera(float)");
		controlsPatchInstalled = TryPatch(FindMethod(typeof(Player), "SetControls", null), new HarmonyMethod(typeof(SurvivalViewPatches), "PlayerSetControlsPrefix", (Type[])null), null, "Player.SetControls(...)");
		jogSpeedPatchInstalled = TryPatch(FindMethod(typeof(Character), "GetJogSpeedFactor", Type.EmptyTypes), null, new HarmonyMethod(typeof(SurvivalViewPatches), "CharacterGetJogSpeedFactorPostfix", (Type[])null), "Character.GetJogSpeedFactor()");
		runSpeedPatchInstalled = TryPatch(FindMethod(typeof(Character), "GetRunSpeedFactor", Type.EmptyTypes), null, new HarmonyMethod(typeof(SurvivalViewPatches), "CharacterGetRunSpeedFactorPostfix", (Type[])null), "Character.GetRunSpeedFactor()");
	}

	private bool TryPatch(MethodInfo target, HarmonyMethod prefix, HarmonyMethod postfix, string targetName)
	{
		if (target == null)
		{
			((BaseUnityPlugin)this).Logger.LogError((object)("Survival View: could not find " + targetName + ". That feature is disabled."));
			return false;
		}
		try
		{
			harmony.Patch((MethodBase)target, prefix, postfix, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			((BaseUnityPlugin)this).Logger.LogInfo((object)("Survival View: patched " + targetName + "."));
			return true;
		}
		catch (Exception ex)
		{
			((BaseUnityPlugin)this).Logger.LogError((object)("Survival View: failed to patch " + targetName + ": " + ex));
			return false;
		}
	}

	private static MethodInfo FindMethod(Type type, string methodName, Type[] parameterTypes)
	{
		if (parameterTypes != null)
		{
			return type.GetMethod(methodName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, parameterTypes, null);
		}
		MethodInfo[] methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
		for (int i = 0; i < methods.Length; i++)
		{
			if (methods[i].Name == methodName)
			{
				return methods[i];
			}
		}
		return null;
	}

	internal void ApplyRaisedHeadView(Player player, ref Vector3 vanillaCameraPosition, float cameraDistance, float minimumCameraDistance)
	{
		//IL_000e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0013: Unknown result type (might be due to invalid IL or missing references)
		//IL_0058: Unknown result type (might be due to invalid IL or missing references)
		//IL_005d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0060: 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_0093: Unknown result type (might be due to invalid IL or missing references)
		//IL_0098: 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_0041: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f7: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f8: Unknown result type (might be due to invalid IL or missing references)
		//IL_0114: Unknown result type (might be due to invalid IL or missing references)
		//IL_0119: Unknown result type (might be due to invalid IL or missing references)
		//IL_011e: 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_0120: Unknown result type (might be due to invalid IL or missing references)
		//IL_013c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0141: Unknown result type (might be due to invalid IL or missing references)
		//IL_0146: Unknown result type (might be due to invalid IL or missing references)
		//IL_0148: Unknown result type (might be due to invalid IL or missing references)
		//IL_0149: Unknown result type (might be due to invalid IL or missing references)
		//IL_014f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0150: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
		bool flag = UpdateViewState(player, cameraDistance, minimumCameraDistance);
		lastVanillaClosestCameraPosition = vanillaCameraPosition;
		if (!flag || (Object)(object)player == (Object)null || (Object)(object)((Character)player).m_eye == (Object)null)
		{
			lastRaisedHeadCameraPosition = vanillaCameraPosition;
			return;
		}
		Transform transform = ((Component)((Character)player).m_eye).transform;
		Vector3 position = transform.position;
		Vector3 val = default(Vector3);
		((Vector3)(ref val))..ctor(position.x, vanillaCameraPosition.y + Mathf.Clamp(heightAdjustmentMeters.Value, -1f, 1f), position.z);
		Vector3 forward = transform.forward;
		forward.y = 0f;
		if (((Vector3)(ref forward)).sqrMagnitude <= 0.0001f)
		{
			forward = ((Component)player).transform.forward;
			forward.y = 0f;
		}
		((Vector3)(ref forward)).Normalize();
		Vector3 val2 = Vector3.Cross(Vector3.up, forward);
		Vector3 normalized = ((Vector3)(ref val2)).normalized;
		val += forward * Mathf.Clamp(headForwardOffsetMeters.Value, -0.5f, 0.5f);
		val = (lastRaisedHeadCameraPosition = (vanillaCameraPosition = val + normalized * Mathf.Clamp(headSideOffsetMeters.Value, -0.5f, 0.5f)));
	}

	internal void ApplyFieldOfView(Camera camera)
	{
		if (!((Object)(object)camera == (Object)null) && lastKnownViewActive && overrideFieldOfView.Value)
		{
			camera.fieldOfView = Mathf.Clamp(survivalFieldOfView.Value, 55f, 100f);
		}
	}

	private bool UpdateViewState(Player player, float cameraDistance, float minimumCameraDistance)
	{
		lastKnownCameraDistance = cameraDistance;
		lastKnownMinimumCameraDistance = minimumCameraDistance;
		bool flag = (lastKnownViewActive = IsSurvivalViewActive(player, cameraDistance, minimumCameraDistance));
		if (!viewStateLogged || flag != lastLoggedViewState)
		{
			((BaseUnityPlugin)this).Logger.LogInfo((object)("Survival View: " + (flag ? "entered" : "left") + " Raised Head View. distance=" + cameraDistance.ToString("0.000") + ", minDistance=" + minimumCameraDistance.ToString("0.000") + ", activationThreshold=" + GetActivationThreshold(minimumCameraDistance).ToString("0.000")));
			lastLoggedViewState = flag;
			viewStateLogged = true;
		}
		return flag;
	}

	private bool IsSurvivalViewActive(Player player, float cameraDistance, float minimumCameraDistance)
	{
		if (!enabled.Value || (Object)(object)player == (Object)null || (Object)(object)player != (Object)(object)Player.m_localPlayer)
		{
			return false;
		}
		if (((Character)player).IsDead() || ((Character)player).IsAttached())
		{
			return false;
		}
		if (!requireClosestZoom.Value)
		{
			return true;
		}
		return cameraDistance <= GetActivationThreshold(minimumCameraDistance);
	}

	private float GetActivationThreshold(float minimumCameraDistance)
	{
		float num = Mathf.Max(0f, minimumCameraDistance);
		float num2 = Mathf.Clamp(closestZoomTolerance.Value, 0.001f, 0.5f);
		return num + num2;
	}

	private bool IsGroundedMovementActive(Player player)
	{
		if (!enabled.Value || !enableGroundedMovement.Value || (Object)(object)player == (Object)null || (Object)(object)player != (Object)(object)Player.m_localPlayer)
		{
			return false;
		}
		if (((Character)player).IsDead() || ((Character)player).IsAttached() || ((Character)player).IsSwimming() || ((Character)player).IsFlying())
		{
			return false;
		}
		return !movementRequiresSurvivalView.Value || lastKnownViewActive;
	}

	internal Vector3 SmoothMoveDirection(Player player, Vector3 rawMoveDirection)
	{
		//IL_0012: Unknown result type (might be due to invalid IL or missing references)
		//IL_0013: 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_003e: Unknown result type (might be due to invalid IL or missing references)
		//IL_002f: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
		//IL_0043: Unknown result type (might be due to invalid IL or missing references)
		//IL_008b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0090: Unknown result type (might be due to invalid IL or missing references)
		//IL_0098: Unknown result type (might be due to invalid IL or missing references)
		//IL_009d: 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_00ad: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
		if (!IsGroundedMovementActive(player))
		{
			return rawMoveDirection;
		}
		float num = Mathf.Clamp01(((Vector3)(ref rawMoveDirection)).magnitude);
		Vector3 val = ((num > 0.001f) ? (((Vector3)(ref rawMoveDirection)).normalized * num) : Vector3.zero);
		float num2 = ((((Vector3)(ref val)).sqrMagnitude > ((Vector3)(ref smoothedMoveDirection)).sqrMagnitude) ? Mathf.Max(0.01f, accelerationSeconds.Value) : Mathf.Max(0.01f, decelerationSeconds.Value));
		smoothedMoveDirection = Vector3.MoveTowards(smoothedMoveDirection, val, Time.deltaTime / num2);
		return Vector3.ClampMagnitude(smoothedMoveDirection, 1f);
	}

	private void UpdateSprintBuild(Player player)
	{
		//IL_000a: Unknown result type (might be due to invalid IL or missing references)
		//IL_000f: Unknown result type (might be due to invalid IL or missing references)
		int num;
		if (((Character)player).IsRunning())
		{
			Vector3 moveDir = ((Character)player).GetMoveDir();
			num = ((((Vector3)(ref moveDir)).sqrMagnitude > 0.001f) ? 1 : 0);
		}
		else
		{
			num = 0;
		}
		bool flag = (byte)num != 0;
		float num2 = (flag ? 1f : 0f);
		float num3 = ((num2 > sprintBuild) ? Mathf.Max(0.01f, sprintBuildUpSeconds.Value) : Mathf.Max(0.01f, sprintRecoverySeconds.Value));
		if (flag)
		{
			num3 /= Mathf.Max(0.15f, GetLowStaminaExertionMultiplier(player));
		}
		sprintBuild = Mathf.MoveTowards(sprintBuild, num2, Time.deltaTime / num3);
	}

	internal float GetJogSpeedMultiplier(Player player)
	{
		if (!IsGroundedMovementActive(player))
		{
			return 1f;
		}
		return GetDirectionalSpeedMultiplier(player) * GetLowStaminaExertionMultiplier(player);
	}

	internal float GetRunSpeedMultiplier(Player player)
	{
		if (!IsGroundedMovementActive(player))
		{
			return 1f;
		}
		float num = Mathf.Clamp(sprintStartSpeedMultiplier.Value, 0.2f, 1f);
		float num2 = Mathf.Lerp(num, 1f, Mathf.Clamp01(sprintBuild));
		return GetDirectionalSpeedMultiplier(player) * num2 * GetLowStaminaExertionMultiplier(player);
	}

	private float GetDirectionalSpeedMultiplier(Player player)
	{
		//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_0040: Unknown result type (might be due to invalid IL or missing references)
		//IL_0045: Unknown result type (might be due to invalid IL or missing references)
		//IL_004a: Unknown result type (might be due to invalid IL or missing references)
		//IL_004b: 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_0064: Unknown result type (might be due to invalid IL or missing references)
		Vector3 moveDir = ((Character)player).GetMoveDir();
		moveDir.y = 0f;
		if (((Vector3)(ref moveDir)).sqrMagnitude <= 0.0001f)
		{
			return 1f;
		}
		Vector3 val = ((Component)player).transform.InverseTransformDirection(((Vector3)(ref moveDir)).normalized);
		float num = Mathf.Clamp01(val.z);
		float num2 = Mathf.Clamp01(0f - val.z);
		float num3 = Mathf.Clamp01(Mathf.Abs(val.x));
		float num4 = Mathf.Clamp(backwardSpeedMultiplier.Value, 0.2f, 1f);
		float num5 = Mathf.Clamp(sidewaysSpeedMultiplier.Value, 0.2f, 1f);
		if (num2 > 0.01f)
		{
			return Mathf.Lerp(num5, num4, num2);
		}
		return Mathf.Lerp(num5, 1f, num);
	}

	private float GetLowStaminaExertionMultiplier(Player player)
	{
		float multiplier = SurvivalViewFatigueBridge.GetMultiplier(player);
		if ((Object)(object)player == (Object)null || !lowStaminaExertionEnabled.Value)
		{
			return multiplier;
		}
		float num = Mathf.Clamp(lowStaminaThreshold.Value, 0.01f, 1f);
		float num2 = Mathf.Clamp01(((Character)player).GetStaminaPercentage());
		if (num2 >= num)
		{
			return multiplier;
		}
		float num3 = Mathf.Clamp(lowStaminaMovementMultiplier.Value, 0.2f, 1f);
		float num4 = Mathf.Lerp(num3, 1f, num2 / num);
		return num4 * multiplier;
	}

	private unsafe void WriteDiagnostic()
	{
		//IL_00a2: 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_00ee: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
		//IL_010e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0113: Unknown result type (might be due to invalid IL or missing references)
		//IL_012e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0133: Unknown result type (might be due to invalid IL or missing references)
		Player localPlayer = Player.m_localPlayer;
		string text = "no local player";
		string text2 = "no camera sample";
		if ((Object)(object)localPlayer != (Object)null)
		{
			text = "running=" + ((Character)localPlayer).IsRunning() + ", swimming=" + ((Character)localPlayer).IsSwimming() + ", attached=" + ((Character)localPlayer).IsAttached() + ", stamina=" + ((Character)localPlayer).GetStaminaPercentage().ToString("0.00") + ", move=" + ((object)((Character)localPlayer).GetMoveDir()/*cast due to .constrained prefix*/).ToString();
			if ((Object)(object)((Character)localPlayer).m_eye != (Object)null)
			{
				string[] obj = new string[8]
				{
					"head=",
					((object)((Component)((Character)localPlayer).m_eye).transform.position/*cast due to .constrained prefix*/).ToString(),
					", vanillaClosest=",
					null,
					null,
					null,
					null,
					null
				};
				Vector3 val = lastVanillaClosestCameraPosition;
				obj[3] = ((object)(*(Vector3*)(&val))/*cast due to .constrained prefix*/).ToString();
				obj[4] = ", raisedHead=";
				val = lastRaisedHeadCameraPosition;
				obj[5] = ((object)(*(Vector3*)(&val))/*cast due to .constrained prefix*/).ToString();
				obj[6] = ", yDifference=";
				obj[7] = (lastRaisedHeadCameraPosition.y - lastVanillaClosestCameraPosition.y).ToString("0.000");
				text2 = string.Concat(obj);
			}
		}
		((BaseUnityPlugin)this).Logger.LogInfo((object)("Survival View diagnostic: enabled=" + enabled.Value + ", cameraDistance=" + lastKnownCameraDistance.ToString("0.000") + ", minDistance=" + lastKnownMinimumCameraDistance.ToString("0.000") + ", threshold=" + GetActivationThreshold(lastKnownMinimumCameraDistance).ToString("0.000") + ", viewActive=" + lastKnownViewActive + ", sprintBuild=" + sprintBuild.ToString("0.00") + ", patches=[cameraPosition=" + getCameraPositionPatchInstalled + ", lens=" + cameraLensPatchInstalled + ", controls=" + controlsPatchInstalled + ", jog=" + jogSpeedPatchInstalled + ", run=" + runSpeedPatchInstalled + "], " + text + ", " + text2));
	}
}
public static class SurvivalViewFatigueBridge
{
	public static Func<Player, float> FatigueMovementMultiplier;

	public static float GetMultiplier(Player player)
	{
		if (FatigueMovementMultiplier == null)
		{
			return 1f;
		}
		try
		{
			return Mathf.Clamp(FatigueMovementMultiplier(player), 0.2f, 1f);
		}
		catch
		{
			return 1f;
		}
	}
}
internal static class SurvivalViewPatches
{
	public static void GameCameraGetCameraPositionPostfix(GameCamera __instance, ref Vector3 pos, float ___m_distance, float ___m_minDistance)
	{
		SurvivalViewPlugin instance = SurvivalViewPlugin.Instance;
		if ((Object)(object)instance != (Object)null)
		{
			instance.ApplyRaisedHeadView(Player.m_localPlayer, ref pos, ___m_distance, ___m_minDistance);
		}
	}

	public static void GameCameraUpdateCameraPostfix(GameCamera __instance, Camera ___m_camera)
	{
		SurvivalViewPlugin instance = SurvivalViewPlugin.Instance;
		if ((Object)(object)instance != (Object)null)
		{
			instance.ApplyFieldOfView(___m_camera);
		}
	}

	public static void PlayerSetControlsPrefix(Player __instance, ref Vector3 __0)
	{
		//IL_0017: 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)
		SurvivalViewPlugin instance = SurvivalViewPlugin.Instance;
		if ((Object)(object)instance != (Object)null)
		{
			__0 = instance.SmoothMoveDirection(__instance, __0);
		}
	}

	public static void CharacterGetJogSpeedFactorPostfix(Character __instance, ref float __result)
	{
		SurvivalViewPlugin instance = SurvivalViewPlugin.Instance;
		Player val = (Player)(object)((__instance is Player) ? __instance : null);
		if ((Object)(object)instance != (Object)null && (Object)(object)val != (Object)null)
		{
			__result *= instance.GetJogSpeedMultiplier(val);
		}
	}

	public static void CharacterGetRunSpeedFactorPostfix(Character __instance, ref float __result)
	{
		SurvivalViewPlugin instance = SurvivalViewPlugin.Instance;
		Player val = (Player)(object)((__instance is Player) ? __instance : null);
		if ((Object)(object)instance != (Object)null && (Object)(object)val != (Object)null)
		{
			__result *= instance.GetRunSpeedMultiplier(val);
		}
	}
}