Decompiled source of Odins Drunk Cam v0.1.1

plugins/OdinsDrunkCam.dll

Decompiled 17 hours ago
using System;
using System.Collections;
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;
using UnityEngine.PostProcessing;

[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("OdinsDrunkCam")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.1.1.0")]
[assembly: AssemblyInformationalVersion("0.1.1+b492ac54fc9a98cad1d68e239cec6f9028af156d")]
[assembly: AssemblyProduct("OdinsDrunkCam")]
[assembly: AssemblyTitle("OdinsDrunkCam")]
[assembly: AssemblyVersion("0.1.1.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 OdinsDrunkCam
{
	internal class DrunkCam : MonoBehaviour
	{
		private const float MaxRoll = 18f;

		private const float MaxPitch = 5f;

		private const float MaxYaw = 7f;

		private const float MaxFovPulse = 6f;

		private const float RollHz = 0.22f;

		private const float PitchHz = 0.3f;

		private const float YawHz = 0.17f;

		private const float FovHz = 0.12f;

		private const float RiseSeconds = 2.5f;

		private const float FallSeconds = 4f;

		private const float HideSeconds = 0.15f;

		private const float ShowSeconds = 1f;

		private const float MaxVignette = 0.5f;

		private static DrunkCam s_instance;

		private float _level;

		private float _visible;

		private float _swayTime;

		private Transform _swayed;

		private Quaternion _sober;

		private Quaternion _drunk;

		private Camera _fovCam;

		private Camera _fovSky;

		private float _soberFov;

		private float _soberSkyFov;

		private float _drunkFov;

		private readonly VignetteControl _vignette = new VignetteControl();

		private readonly WaitForEndOfFrame _endOfFrame = new WaitForEndOfFrame();

		private float Amount => _level * _visible;

		private void Awake()
		{
			s_instance = this;
		}

		private void OnEnable()
		{
			((MonoBehaviour)this).StartCoroutine(RestoreAtEndOfFrame());
		}

		private void OnDisable()
		{
			Restore();
			_vignette.Release();
		}

		private void OnDestroy()
		{
			if ((Object)(object)s_instance == (Object)(object)this)
			{
				s_instance = null;
			}
		}

		private void Update()
		{
			Restore();
			if (ToggleKeyDown())
			{
				Toggle();
			}
			float unscaledDeltaTime = Time.unscaledDeltaTime;
			Player localPlayer = Player.m_localPlayer;
			float num = 0f;
			if (Plugin.Enabled.Value && (Object)(object)localPlayer != (Object)null)
			{
				num = Mathf.Min(Plugin.MaxIntensity.Value, Plugin.Intensity.Value * Meads.Drunkenness(localPlayer));
			}
			_level = Approach(_level, num, (num > _level) ? 2.5f : 4f, Time.deltaTime);
			bool flag = Plugin.Enabled.Value && (Object)(object)localPlayer != (Object)null && CanSway(localPlayer);
			_visible = Approach(_visible, flag ? 1f : 0f, flag ? 1f : 0.15f, unscaledDeltaTime);
			if (_level < 0.001f && num <= 0f)
			{
				_level = 0f;
			}
			if (Amount > 0f)
			{
				_swayTime += Time.deltaTime;
			}
			_vignette.Apply(Plugin.Vignette.Value ? (Amount * 0.5f) : 0f);
		}

		private void Toggle()
		{
			bool flag = !Plugin.Enabled.Value;
			Plugin.Enabled.Value = flag;
			if (!flag)
			{
				_level = 0f;
				_visible = 0f;
				Restore();
				_vignette.Apply(0f);
			}
			MessageHud instance = MessageHud.instance;
			if (instance != null)
			{
				instance.ShowMessage((MessageType)1, flag ? "Odin's Drunk Cam: on" : "Odin's Drunk Cam: off. The world holds still.", 0, (Sprite)null, false, true);
			}
		}

		private static bool CanSway(Player player)
		{
			if (((Character)player).IsDead() || ((Character)player).InCutscene() || ((Character)player).InIntro() || player.IsSleeping())
			{
				return false;
			}
			if (((Character)player).InPlaceMode() || Hud.IsPieceSelectionVisible() || Hud.InRadial())
			{
				return false;
			}
			if (GameCamera.InFreeFly())
			{
				return false;
			}
			if (!Minimap.IsOpen() && !Menu.IsVisible() && !InventoryGui.IsVisible() && !StoreGui.IsVisible() && !TextInput.IsVisible() && !Console.IsVisible())
			{
				if ((Object)(object)Chat.instance != (Object)null)
				{
					return !Chat.instance.HasFocus();
				}
				return true;
			}
			return false;
		}

		internal static void AfterCameraUpdate(GameCamera camera)
		{
			if ((Object)(object)s_instance != (Object)null)
			{
				s_instance.Sway(camera);
			}
		}

		private void Sway(GameCamera gameCamera)
		{
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
			float amount = Amount;
			if (amount <= 0f || !Plugin.Enabled.Value || GameCamera.InFreeFly())
			{
				return;
			}
			Restore();
			float swayTime = _swayTime;
			float num = Wave(swayTime, 0.22f, 0f) * 18f;
			float num2 = Wave(swayTime, 0.3f, 31.7f) * 5f;
			float num3 = Wave(swayTime, 0.17f, 77.3f) * 7f;
			float num4 = Wave(swayTime, 0.12f, 53.9f) * 6f;
			Transform transform = ((Component)gameCamera).transform;
			_sober = transform.rotation;
			_drunk = _sober * Quaternion.Euler(num2 * amount, num3 * amount, num * amount);
			transform.rotation = _drunk;
			_swayed = transform;
			_fovCam = ((Component)gameCamera).GetComponent<Camera>();
			_fovSky = gameCamera.m_skyCamera;
			if ((Object)(object)_fovCam != (Object)null)
			{
				_soberFov = _fovCam.fieldOfView;
				_drunkFov = Mathf.Clamp(_soberFov + num4 * amount, 20f, 140f);
				_fovCam.fieldOfView = _drunkFov;
				if ((Object)(object)_fovSky != (Object)null)
				{
					_soberSkyFov = _fovSky.fieldOfView;
					_fovSky.fieldOfView = _drunkFov;
				}
			}
		}

		private void Restore()
		{
			//IL_0015: 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_002e: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_swayed == (Object)null)
			{
				return;
			}
			if (_swayed.rotation == _drunk)
			{
				_swayed.rotation = _sober;
			}
			_swayed = null;
			if ((Object)(object)_fovCam != (Object)null && Mathf.Approximately(_fovCam.fieldOfView, _drunkFov))
			{
				_fovCam.fieldOfView = _soberFov;
				if ((Object)(object)_fovSky != (Object)null && Mathf.Approximately(_fovSky.fieldOfView, _drunkFov))
				{
					_fovSky.fieldOfView = _soberSkyFov;
				}
			}
			_fovCam = null;
			_fovSky = null;
		}

		private IEnumerator RestoreAtEndOfFrame()
		{
			while (true)
			{
				yield return _endOfFrame;
				Restore();
			}
		}

		private static float Wave(float t, float hz, float seed)
		{
			float num = Mathf.Sin((t * hz + seed) * 2f * (float)Math.PI);
			float num2 = Mathf.PerlinNoise(t * hz * 0.7f, seed) * 2f - 1f;
			return Mathf.Clamp(num * 0.65f + num2 * 0.35f, -1f, 1f);
		}

		private static float Approach(float from, float to, float seconds, float dt)
		{
			if (seconds <= 0f)
			{
				return to;
			}
			return Mathf.Lerp(from, to, 1f - Mathf.Exp((0f - dt) / seconds));
		}

		private static bool ToggleKeyDown()
		{
			//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_0036: 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_005e: Unknown result type (might be due to invalid IL or missing references)
			if (TextInput.IsVisible() || Console.IsVisible() || ((Object)(object)Chat.instance != (Object)null && Chat.instance.HasFocus()))
			{
				return false;
			}
			KeyboardShortcut value = Plugin.ToggleKey.Value;
			if ((int)((KeyboardShortcut)(ref value)).MainKey == 0 || !ZInput.GetKeyDown(((KeyboardShortcut)(ref value)).MainKey, false))
			{
				return false;
			}
			foreach (KeyCode modifier in ((KeyboardShortcut)(ref value)).Modifiers)
			{
				if (!ZInput.GetKey(modifier, false))
				{
					return false;
				}
			}
			return true;
		}
	}
	[HarmonyPatch(typeof(GameCamera), "UpdateCamera")]
	internal static class GameCameraUpdatePatch
	{
		private static void Postfix(GameCamera __instance)
		{
			DrunkCam.AfterCameraUpdate(__instance);
		}
	}
	internal static class Meads
	{
		private const float PerMead = 0.4f;

		private const float FadeOutSeconds = 30f;

		private static readonly HashSet<int> s_meadEffects = new HashSet<int>();

		private static ZNetScene s_builtFor;

		public static float Drunkenness(Player player)
		{
			if (!EnsureBuilt())
			{
				return 0f;
			}
			float num = 0f;
			foreach (StatusEffect statusEffect in ((Character)player).GetSEMan().GetStatusEffects())
			{
				if (!((Object)(object)statusEffect == (Object)null) && s_meadEffects.Contains(statusEffect.NameHash()))
				{
					float num2 = ((statusEffect.m_ttl > 0f) ? Mathf.Clamp01(statusEffect.GetRemaningTime() / 30f) : 1f);
					num += num2;
				}
			}
			return num * 0.4f;
		}

		private static bool EnsureBuilt()
		{
			ZNetScene instance = ZNetScene.instance;
			if ((Object)(object)instance == (Object)null)
			{
				return false;
			}
			if ((Object)(object)s_builtFor == (Object)(object)instance)
			{
				return true;
			}
			s_meadEffects.Clear();
			List<string> list = new List<string>();
			foreach (GameObject prefab in instance.m_prefabs)
			{
				Fermenter val = (((Object)(object)prefab != (Object)null) ? prefab.GetComponent<Fermenter>() : null);
				if ((Object)(object)val == (Object)null)
				{
					continue;
				}
				foreach (ItemConversion item in val.m_conversion)
				{
					Add(item?.m_to, list);
				}
			}
			if ((Object)(object)ObjectDB.instance != (Object)null)
			{
				foreach (GameObject item2 in ObjectDB.instance.m_items)
				{
					if ((Object)(object)item2 != (Object)null && ((Object)item2).name.StartsWith("Mead"))
					{
						Add(item2.GetComponent<ItemDrop>(), list);
					}
				}
			}
			s_builtFor = instance;
			Plugin.Log.LogInfo((object)string.Format("Found {0} mead effects: {1}", s_meadEffects.Count, string.Join(", ", list)));
			return true;
		}

		private static void Add(ItemDrop drop, List<string> names)
		{
			StatusEffect val = ((!((Object)(object)drop != (Object)null)) ? null : drop.m_itemData?.m_shared?.m_consumeStatusEffect);
			if ((Object)(object)val != (Object)null && s_meadEffects.Add(val.NameHash()))
			{
				names.Add(((Object)drop).name + " (" + ((Object)val).name + ")");
			}
		}
	}
	[BepInPlugin("hevyh2o.valheim.odinsdrunkcam", "Odin's Drunk Cam", "0.1.1")]
	public class Plugin : BaseUnityPlugin
	{
		public const string Guid = "hevyh2o.valheim.odinsdrunkcam";

		public const string Name = "Odin's Drunk Cam";

		public const string Version = "0.1.1";

		internal static ManualLogSource Log;

		internal static ConfigEntry<bool> Enabled;

		internal static ConfigEntry<KeyboardShortcut> ToggleKey;

		internal static ConfigEntry<float> Intensity;

		internal static ConfigEntry<float> MaxIntensity;

		internal static ConfigEntry<bool> Vignette;

		private Harmony _harmony;

		private DrunkCam _cam;

		private void Awake()
		{
			//IL_0045: 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_0091: Expected O, but got Unknown
			//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ce: Expected O, but got Unknown
			//IL_00f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0103: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Master switch. When off, the camera is never touched. ToggleKey flips this too.");
			ToggleKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("General", "ToggleKey", new KeyboardShortcut((KeyCode)285, Array.Empty<KeyCode>()), "Quick-disable: turns the whole effect off at once (no fade-out), or back on. Saved to this file. Can be rebound in game with Configuration Manager (F1).");
			Intensity = ((BaseUnityPlugin)this).Config.Bind<float>("Comfort", "Intensity", 1f, new ConfigDescription("How strongly each mead sways the camera. 0.5 = half as much, 3 = three times. Lower it if you get motion sick.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 3f), Array.Empty<object>()));
			MaxIntensity = ((BaseUnityPlugin)this).Config.Bind<float>("Comfort", "MaxIntensity", 1f, new ConfigDescription("Hard cap on the effect, however many meads are active. 1 = up to about 18 degrees of roll (three meads at default Intensity). 2 = up to about 36 degrees: properly legless.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 2f), Array.Empty<object>()));
			Vignette = ((BaseUnityPlugin)this).Config.Bind<bool>("Comfort", "Vignette", true, "Darken the edges of the screen a little while drunk.");
			_harmony = new Harmony("hevyh2o.valheim.odinsdrunkcam");
			_harmony.PatchAll(typeof(Plugin).Assembly);
			_cam = ((Component)this).gameObject.AddComponent<DrunkCam>();
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Odin's Drunk Cam 0.1.1 loaded. Odin raises a horn; the horizon raises an eyebrow.");
		}

		private void OnDestroy()
		{
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
			if ((Object)(object)_cam != (Object)null)
			{
				Object.Destroy((Object)(object)_cam);
			}
		}
	}
	internal class VignetteControl
	{
		private PostProcessingProfile _profile;

		private bool _wasEnabled;

		private Settings _original;

		public void Apply(float strength)
		{
			//IL_005d: 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_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_00b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: 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)
			if (strength <= 0.001f)
			{
				Release();
				return;
			}
			PostProcessingProfile val = CurrentProfile();
			if ((Object)(object)val != (Object)(object)_profile)
			{
				Release();
				if ((Object)(object)val == (Object)null)
				{
					return;
				}
				_profile = val;
				_wasEnabled = ((PostProcessingModel)val.vignette).enabled;
				_original = val.vignette.settings;
			}
			Settings settings = _original;
			float num = (_wasEnabled ? _original.intensity : 0f);
			if (!_wasEnabled)
			{
				settings = Settings.defaultSettings;
				settings.smoothness = 0.45f;
			}
			settings.intensity = Mathf.Clamp01(num + strength);
			_profile.vignette.settings = settings;
			((PostProcessingModel)_profile.vignette).enabled = true;
		}

		public void Release()
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_profile != (Object)null)
			{
				_profile.vignette.settings = _original;
				((PostProcessingModel)_profile.vignette).enabled = _wasEnabled;
			}
			_profile = null;
		}

		private static PostProcessingProfile CurrentProfile()
		{
			CameraEffects instance = CameraEffects.instance;
			if ((Object)(object)instance == (Object)null)
			{
				return null;
			}
			PostProcessingBehaviour component = ((Component)instance).GetComponent<PostProcessingBehaviour>();
			if (!((Object)(object)component != (Object)null))
			{
				return null;
			}
			return component.profile;
		}
	}
	internal static class BuildInfo
	{
		public const string Version = "0.1.1";
	}
}