Decompiled source of SkipIntroCinematic v1.0.2

SkipIntroCinematic.dll

Decompiled 2 months ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
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 TMPro;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;

[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.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyCompany("SkipIntroCinematic")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("1.0.2.0")]
[assembly: AssemblyInformationalVersion("1.0.2")]
[assembly: AssemblyProduct("SkipIntroCinematic")]
[assembly: AssemblyTitle("SkipIntroCinematic")]
[assembly: AssemblyVersion("1.0.2.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 Moopfam.SkipIntroCinematic
{
	[BepInPlugin("Moopfam.SkipIntroCinematic", "SkipIntroCinematic", "1.0.2")]
	[BepInProcess("valheim.exe")]
	public sealed class SkipIntroCinematicPlugin : BaseUnityPlugin
	{
		public const string PluginGuid = "Moopfam.SkipIntroCinematic";

		public const string PluginName = "SkipIntroCinematic";

		public const string PluginVersion = "1.0.2";

		internal const string AccessibilityToggleName = "Moopfam.SkipIntroCinematic.AccessibilityToggle";

		internal const string ToggleLabel = "Skip intro cinematic";

		internal const string ToggleTooltip = "Skip Valheim's startup cinematic and go directly to the main menu. Enabled by default.";

		private Harmony _harmony;

		private static bool _cinematicsReflectionResolved;

		private static Type _cinematicsManagerType;

		private static FieldInfo _cinematicsInstanceField;

		private static FieldInfo _introOnStartupField;

		private static bool _fejdReflectionResolved;

		private static Type _fejdStartupType;

		private static FieldInfo _fejdInstanceField;

		private static FieldInfo _fejdMainMenuField;

		private static bool _gameReflectionResolved;

		private static FieldInfo _gameHasStartedOnceField;

		internal static SkipIntroCinematicPlugin Instance { get; private set; }

		internal static ConfigEntry<bool> Enabled { get; private set; }

		internal static ManualLogSource Log { get; private set; }

		private static void ResolveCinematicsReflection()
		{
			if (!_cinematicsReflectionResolved)
			{
				_cinematicsReflectionResolved = true;
				_cinematicsManagerType = AccessTools.TypeByName("CinematicsManager");
				if (!(_cinematicsManagerType == null))
				{
					_cinematicsInstanceField = AccessTools.Field(_cinematicsManagerType, "s_instance");
					_introOnStartupField = AccessTools.Field(_cinematicsManagerType, "m_introOnStartup");
				}
			}
		}

		private static void ResolveFejdReflection()
		{
			if (!_fejdReflectionResolved)
			{
				_fejdReflectionResolved = true;
				_fejdStartupType = AccessTools.TypeByName("FejdStartup");
				if (!(_fejdStartupType == null))
				{
					_fejdInstanceField = AccessTools.Field(_fejdStartupType, "instance");
					_fejdMainMenuField = AccessTools.Field(_fejdStartupType, "m_mainMenu");
				}
			}
		}

		private static void ResolveGameReflection()
		{
			if (!_gameReflectionResolved)
			{
				_gameReflectionResolved = true;
				Type type = AccessTools.TypeByName("Game");
				if (type != null)
				{
					_gameHasStartedOnceField = AccessTools.Field(type, "m_hasStartedOnce");
				}
			}
		}

		private void Awake()
		{
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Expected O, but got Unknown
			if (!Application.isBatchMode)
			{
				Instance = this;
				Log = ((BaseUnityPlugin)this).Logger;
				Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Moopfam", "Enabled", true, "When true, skips Valheim's startup cinematic. The in-game Accessibility toggle controls this setting.");
				Enabled.SettingChanged += OnEnabledChanged;
				_harmony = new Harmony("Moopfam.SkipIntroCinematic");
				_harmony.PatchAll(typeof(SkipIntroCinematicPlugin).Assembly);
				ApplyStartupCinematicSetting();
				((BaseUnityPlugin)this).Logger.LogInfo((object)string.Format("{0} {1} loaded. Skip intro: {2}", "SkipIntroCinematic", "1.0.2", Enabled.Value));
			}
		}

		private void OnDestroy()
		{
			if (Enabled != null)
			{
				Enabled.SettingChanged -= OnEnabledChanged;
			}
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
			if (Instance == this)
			{
				Instance = null;
			}
		}

		private static void OnEnabledChanged(object sender, EventArgs e)
		{
			ApplyStartupCinematicSetting();
			RefreshAccessibilityToggles();
		}

		internal static bool ShouldSkipIntro()
		{
			return Enabled == null || Enabled.Value;
		}

		internal static void SetSkipIntro(bool value)
		{
			if (Enabled == null)
			{
				return;
			}
			if (Enabled.Value != value)
			{
				Enabled.Value = value;
				SkipIntroCinematicPlugin instance = Instance;
				if (instance != null)
				{
					((BaseUnityPlugin)instance).Config.Save();
				}
			}
			ApplyStartupCinematicSetting();
		}

		internal static void ApplyStartupCinematicSetting()
		{
			try
			{
				ResolveCinematicsReflection();
				if (!(_cinematicsInstanceField == null) && !(_introOnStartupField == null))
				{
					object value = _cinematicsInstanceField.GetValue(null);
					if (value != null)
					{
						_introOnStartupField.SetValue(value, !ShouldSkipIntro());
					}
				}
			}
			catch (Exception ex)
			{
				ManualLogSource log = Log;
				if (log != null)
				{
					log.LogDebug((object)("Could not apply cinematic preference yet: " + ex.Message));
				}
			}
		}

		internal static void DisableFirstStartupMenuAnimation(object startup)
		{
			if (!ShouldSkipIntro() || startup == null)
			{
				return;
			}
			try
			{
				object obj = AccessTools.Field(startup.GetType(), "m_menuAnimator")?.GetValue(startup);
				obj?.GetType().GetMethod("SetBool", BindingFlags.Instance | BindingFlags.Public, null, new Type[2]
				{
					typeof(string),
					typeof(bool)
				}, null)?.Invoke(obj, new object[2] { "FirstStartup", false });
			}
			catch (Exception ex)
			{
				ManualLogSource log = Log;
				if (log != null)
				{
					log.LogDebug((object)("Could not disable first-start menu animation: " + ex.Message));
				}
			}
		}

		internal static bool TryShowMainMenuForSkippedVideo()
		{
			try
			{
				ResolveFejdReflection();
				if (_fejdInstanceField == null || _fejdMainMenuField == null)
				{
					return false;
				}
				object value = _fejdInstanceField.GetValue(null);
				if (value == null)
				{
					return false;
				}
				object? value2 = _fejdMainMenuField.GetValue(value);
				GameObject val = (GameObject)((value2 is GameObject) ? value2 : null);
				if ((Object)(object)val == (Object)null)
				{
					return false;
				}
				val.SetActive(true);
				DisableFirstStartupMenuAnimation(value);
				return true;
			}
			catch (Exception ex)
			{
				ManualLogSource log = Log;
				if (log != null)
				{
					log.LogDebug((object)("Could not show main menu while skipping video: " + ex.Message));
				}
				return false;
			}
		}

		internal static bool HasGameStartedOnce()
		{
			try
			{
				ResolveGameReflection();
				object obj = _gameHasStartedOnceField?.GetValue(null);
				bool flag = default(bool);
				int num;
				if (obj is bool)
				{
					flag = (bool)obj;
					num = 1;
				}
				else
				{
					num = 0;
				}
				return (byte)((uint)num & (flag ? 1u : 0u)) != 0;
			}
			catch
			{
				return false;
			}
		}

		internal static void AddAccessibilityToggle(Component settingsRoot)
		{
			if ((Object)(object)settingsRoot == (Object)null)
			{
				return;
			}
			Toggle val = ((IEnumerable<Toggle>)settingsRoot.GetComponentsInChildren<Toggle>(true)).FirstOrDefault((Func<Toggle, bool>)((Toggle t) => (Object)(object)t != (Object)null && ((Object)((Component)t).gameObject).name == "Moopfam.SkipIntroCinematic.AccessibilityToggle"));
			if ((Object)(object)val != (Object)null)
			{
				ConfigureToggle(val);
				return;
			}
			Toggle val2 = FindAccessibilityTemplate(settingsRoot);
			if ((Object)(object)val2 == (Object)null || (Object)(object)((Component)val2).transform.parent == (Object)null)
			{
				ManualLogSource log = Log;
				if (log != null)
				{
					log.LogWarning((object)"Could not find an Accessibility toggle to clone. Intro skipping still works from the BepInEx config file.");
				}
				return;
			}
			Transform parent = ((Component)val2).transform.parent;
			Toggle val3 = Object.Instantiate<Toggle>(val2, parent, false);
			((Object)((Component)val3).gameObject).name = "Moopfam.SkipIntroCinematic.AccessibilityToggle";
			((Component)val3).transform.SetSiblingIndex(((Component)val2).transform.GetSiblingIndex() + 1);
			PositionAfterTemplateIfNeeded(val2, val3);
			ConfigureToggle(val3);
			RectTransform val4 = (RectTransform)(object)((parent is RectTransform) ? parent : null);
			if ((Object)(object)val4 != (Object)null)
			{
				LayoutRebuilder.ForceRebuildLayoutImmediate(val4);
			}
			ManualLogSource log2 = Log;
			if (log2 != null)
			{
				log2.LogDebug((object)"Added Skip intro cinematic toggle to Accessibility settings.");
			}
		}

		private static Toggle FindAccessibilityTemplate(Component settingsRoot)
		{
			Type type = ((object)settingsRoot).GetType();
			BindingFlags bindingAttr = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
			List<FieldInfo> list = (from f in type.GetFields(bindingAttr)
				where typeof(Toggle).IsAssignableFrom(f.FieldType)
				select f).ToList();
			string[] array = new string[5] { "block", "flashing", "immersive", "motion", "camera" };
			string[] array2 = array;
			foreach (string value in array2)
			{
				foreach (FieldInfo item in list)
				{
					if (item.Name.IndexOf(value, StringComparison.OrdinalIgnoreCase) >= 0)
					{
						object? value2 = item.GetValue(settingsRoot);
						Toggle val = (Toggle)((value2 is Toggle) ? value2 : null);
						if ((Object)(object)val != (Object)null)
						{
							return val;
						}
					}
				}
			}
			Toggle[] componentsInChildren = settingsRoot.GetComponentsInChildren<Toggle>(true);
			if (componentsInChildren.Length == 0)
			{
				return null;
			}
			float maxX = componentsInChildren.Where((Toggle t) => (Object)(object)t != (Object)null).Max((Toggle t) => ((Component)t).transform.position.x);
			return (from t in componentsInChildren
				where (Object)(object)t != (Object)null && Math.Abs(((Component)t).transform.position.x - maxX) < 150f
				orderby ((Component)t).transform.position.y
				select t).FirstOrDefault() ?? ((IEnumerable<Toggle>)componentsInChildren).LastOrDefault((Func<Toggle, bool>)((Toggle t) => (Object)(object)t != (Object)null));
		}

		private static void ConfigureToggle(Toggle toggle)
		{
			if ((Object)(object)toggle == (Object)null)
			{
				return;
			}
			((UnityEventBase)toggle.onValueChanged).RemoveAllListeners();
			toggle.SetIsOnWithoutNotify(ShouldSkipIntro());
			((UnityEvent<bool>)(object)toggle.onValueChanged).AddListener((UnityAction<bool>)SetSkipIntro);
			TMP_Text[] componentsInChildren = ((Component)toggle).GetComponentsInChildren<TMP_Text>(true);
			foreach (TMP_Text val in componentsInChildren)
			{
				if ((Object)(object)val != (Object)null)
				{
					val.text = "Skip intro cinematic";
				}
			}
			ConfigureSettingsTooltipByReflection(toggle);
		}

		private static void ConfigureSettingsTooltipByReflection(Toggle toggle)
		{
			Component[] componentsInChildren = ((Component)toggle).GetComponentsInChildren<Component>(true);
			Component[] array = componentsInChildren;
			foreach (Component val in array)
			{
				if ((Object)(object)val == (Object)null || ((object)val).GetType().Name != "SettingsTooltip")
				{
					continue;
				}
				try
				{
					((object)val).GetType().GetMethod("SetTexts", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[2]
					{
						typeof(string),
						typeof(string)
					}, null)?.Invoke(val, new object[2] { "Skip intro cinematic", "Skip Valheim's startup cinematic and go directly to the main menu. Enabled by default." });
				}
				catch (Exception ex)
				{
					ManualLogSource log = Log;
					if (log != null)
					{
						log.LogDebug((object)("Could not set Accessibility tooltip text: " + ex.Message));
					}
				}
			}
		}

		private static void PositionAfterTemplateIfNeeded(Toggle template, Toggle clone)
		{
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)template == (Object)null || (Object)(object)clone == (Object)null || (Object)(object)((Component)template).transform.parent == (Object)null)
			{
				return;
			}
			Transform parent = ((Component)template).transform.parent;
			if (!((Object)(object)((Component)parent).GetComponent<LayoutGroup>() != (Object)null))
			{
				Transform transform = ((Component)template).transform;
				RectTransform val = (RectTransform)(object)((transform is RectTransform) ? transform : null);
				Transform transform2 = ((Component)clone).transform;
				RectTransform val2 = (RectTransform)(object)((transform2 is RectTransform) ? transform2 : null);
				if (!((Object)(object)val == (Object)null) && !((Object)(object)val2 == (Object)null))
				{
					float num = EstimateVerticalSpacing(parent, val);
					Vector2 anchoredPosition = val.anchoredPosition;
					val2.anchoredPosition = new Vector2(anchoredPosition.x, anchoredPosition.y - num);
				}
			}
		}

		private static float EstimateVerticalSpacing(Transform parent, RectTransform template)
		{
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: 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)
			List<float> list = new List<float>();
			Toggle[] componentsInChildren = ((Component)parent).GetComponentsInChildren<Toggle>(true);
			foreach (Toggle val in componentsInChildren)
			{
				if (!((Object)(object)val == (Object)null) && !((Object)(object)((Component)val).transform.parent != (Object)(object)parent))
				{
					Transform transform = ((Component)val).transform;
					RectTransform val2 = (RectTransform)(object)((transform is RectTransform) ? transform : null);
					if (!((Object)(object)val2 == (Object)null) && !(Math.Abs(val2.anchoredPosition.x - template.anchoredPosition.x) > 40f))
					{
						list.Add(val2.anchoredPosition.y);
					}
				}
			}
			float num = float.MaxValue;
			for (int j = 0; j < list.Count; j++)
			{
				for (int k = j + 1; k < list.Count; k++)
				{
					float num2 = Math.Abs(list[j] - list[k]);
					if (num2 > 8f && num2 < num)
					{
						num = num2;
					}
				}
			}
			return (num < float.MaxValue) ? num : 68f;
		}

		internal static void RefreshAccessibilityToggles()
		{
			Toggle[] array = Resources.FindObjectsOfTypeAll<Toggle>();
			foreach (Toggle val in array)
			{
				if ((Object)(object)val != (Object)null && ((Object)((Component)val).gameObject).name == "Moopfam.SkipIntroCinematic.AccessibilityToggle")
				{
					val.SetIsOnWithoutNotify(ShouldSkipIntro());
				}
			}
		}
	}
	[HarmonyPatch]
	internal static class CinematicsManagerAwakePatch
	{
		private static MethodBase TargetMethod()
		{
			Type type = AccessTools.TypeByName("CinematicsManager");
			return (type == null) ? null : AccessTools.Method(type, "Awake", (Type[])null, (Type[])null);
		}

		private static bool Prepare()
		{
			return TargetMethod() != null;
		}

		private static void Postfix()
		{
			SkipIntroCinematicPlugin.ApplyStartupCinematicSetting();
		}
	}
	[HarmonyPatch]
	internal static class FejdStartupPlayIntroCinematicPatch
	{
		private static MethodBase TargetMethod()
		{
			Type type = AccessTools.TypeByName("FejdStartup");
			return (type == null) ? null : AccessTools.Method(type, "PlayIntroCinematic", (Type[])null, (Type[])null);
		}

		private static bool Prepare()
		{
			return TargetMethod() != null;
		}

		private static void Prefix(object __instance)
		{
			SkipIntroCinematicPlugin.ApplyStartupCinematicSetting();
			SkipIntroCinematicPlugin.DisableFirstStartupMenuAnimation(__instance);
		}
	}
	[HarmonyPatch]
	internal static class CinematicsManagerPlayPatch
	{
		private static MethodBase TargetMethod()
		{
			Type type = AccessTools.TypeByName("CinematicsManager");
			if (type == null)
			{
				return null;
			}
			return type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault(delegate(MethodInfo method)
			{
				if (method.Name != "Play" || method.ReturnType != typeof(bool))
				{
					return false;
				}
				ParameterInfo[] parameters = method.GetParameters();
				return parameters.Length == 2 && parameters[0].ParameterType.IsEnum;
			});
		}

		private static bool Prepare()
		{
			return TargetMethod() != null;
		}

		private static bool Prefix(object __0, ref bool __result)
		{
			if (!SkipIntroCinematicPlugin.ShouldSkipIntro() || __0 == null)
			{
				return true;
			}
			int num;
			try
			{
				num = Convert.ToInt32(__0);
			}
			catch
			{
				return true;
			}
			if (num != 2 || SkipIntroCinematicPlugin.HasGameStartedOnce())
			{
				return true;
			}
			if (!SkipIntroCinematicPlugin.TryShowMainMenuForSkippedVideo())
			{
				return true;
			}
			__result = true;
			return false;
		}
	}
	[HarmonyPatch]
	internal static class AccessibilitySettingsInitializePatch
	{
		private static Type AccessibilitySettingsType => AccessTools.TypeByName("Valheim.SettingsGui.AccessibilitySettings");

		private static MethodBase TargetMethod()
		{
			Type accessibilitySettingsType = AccessibilitySettingsType;
			return (accessibilitySettingsType == null) ? null : AccessTools.Method(accessibilitySettingsType, "Initialize", (Type[])null, (Type[])null);
		}

		private static bool Prepare()
		{
			return TargetMethod() != null;
		}

		private static void Postfix(object __instance)
		{
			Component val = (Component)((__instance is Component) ? __instance : null);
			if ((Object)(object)val != (Object)null)
			{
				SkipIntroCinematicPlugin.AddAccessibilityToggle(val);
			}
		}
	}
}