Decompiled source of ForsakenRadial v0.3.1

plugins/ForsakenRadial.dll

Decompiled 5 days ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
using Valheim.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("ForsakenRadial")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.3.1.0")]
[assembly: AssemblyInformationalVersion("0.3.1")]
[assembly: AssemblyProduct("ForsakenRadial")]
[assembly: AssemblyTitle("ForsakenRadial")]
[assembly: AssemblyVersion("0.3.1.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[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 ForsakenRadial
{
	[HarmonyPatch(typeof(ValheimRadialConfig), "InitRadialConfig")]
	internal static class ForsakenPowerRadial
	{
		private sealed class PowerDefinition
		{
			internal readonly string Power;

			internal readonly string DefeatedKey;

			internal PowerDefinition(string power, string defeatedKey)
			{
				Power = power;
				DefeatedKey = defeatedKey;
			}
		}

		private sealed class ForsakenPowerChoice : IRadialConfig
		{
			internal readonly string Power;

			private readonly string _name;

			internal readonly string Description;

			internal GroupElement? Element;

			public string LocalizedName
			{
				get
				{
					if (Localization.instance != null)
					{
						return Localization.instance.Localize(_name);
					}
					return _name;
				}
			}

			public Sprite Sprite { get; }

			internal ForsakenPowerChoice(string power, string name, Sprite sprite, string description)
			{
				Power = power;
				_name = name;
				Sprite = sprite;
				Description = description;
			}

			public void InitRadialConfig(RadialBase radial)
			{
			}
		}

		private sealed class ForsakenRadialConfig : IRadialConfig
		{
			private Player? _player;

			private List<ForsakenPowerChoice> _choices = new List<ForsakenPowerChoice>();

			private RadialBase? _radial;

			private DescriptionPanel? _descriptionPanel;

			private ForsakenPowerChoice? _describedChoice;

			private bool _descriptionUnavailable;

			internal RadialBase? Radial => _radial;

			public string LocalizedName
			{
				get
				{
					if (Localization.instance != null)
					{
						return Localization.instance.Localize("Forsaken Powers");
					}
					return "Forsaken Powers";
				}
			}

			public Sprite? Sprite
			{
				get
				{
					string activePower = (((Object)(object)_player == (Object)null) ? string.Empty : _player.GetGuardianPowerName());
					return _choices.Find((ForsakenPowerChoice choice) => choice.Power == activePower)?.Sprite ?? _choices[0].Sprite;
				}
			}

			internal void Initialize(Player? player, List<ForsakenPowerChoice> choices)
			{
				_player = player;
				_choices = choices;
			}

			public void InitRadialConfig(RadialBase radial)
			{
				_radial = radial;
				List<RadialMenuElement> list = new List<RadialMenuElement>(_choices.Count);
				foreach (ForsakenPowerChoice choice in _choices)
				{
					GroupElement val = Object.Instantiate<GroupElement>(RadialData.SO.GroupElement);
					val.Init((IRadialConfig)(object)choice, (IRadialConfig)null, radial);
					choice.Element = val;
					((RadialMenuElement)val).Interact = delegate
					{
						if ((Object)(object)_player == (Object)null || (Object)(object)_player != (Object)(object)Player.m_localPlayer)
						{
							return false;
						}
						try
						{
							_player.SetGuardianPower(choice.Power);
							ForsakenRadialPlugin.Log.LogInfo((object)("Selected Forsaken power '" + choice.Power + "'."));
							return true;
						}
						catch (Exception arg)
						{
							ForsakenRadialPlugin.Log.LogError((object)$"Could not select Forsaken power '{choice.Power}': {arg}");
							return false;
						}
					};
					((RadialMenuElement)val).CloseOnInteract = () => true;
					list.Add((RadialMenuElement)(object)val);
				}
				radial.ConstructRadial(list);
			}

			internal void UpdateDescription(RadialBase radial)
			{
				//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
				if (_descriptionUnavailable)
				{
					return;
				}
				ForsakenPowerChoice forsakenPowerChoice = _choices.Find((ForsakenPowerChoice choice) => (Object)(object)choice.Element == (Object)(object)radial.Selected);
				if (forsakenPowerChoice == null)
				{
					_describedChoice = null;
					_descriptionPanel?.Hide();
					return;
				}
				try
				{
					if (forsakenPowerChoice != _describedChoice)
					{
						_describedChoice = forsakenPowerChoice;
						if (_descriptionPanel == null)
						{
							_descriptionPanel = new DescriptionPanel(Hud.instance, radial);
						}
						_descriptionPanel.Show(forsakenPowerChoice.Description);
					}
					_descriptionPanel?.Position(((RadialMenuElement)forsakenPowerChoice.Element).ElementTransform, radial.InfoPosition);
				}
				catch (Exception arg)
				{
					_descriptionUnavailable = true;
					_descriptionPanel?.Dispose();
					_descriptionPanel = null;
					ForsakenRadialPlugin.Log.LogWarning((object)$"Could not display the focused Forsaken power description: {arg}");
				}
			}

			internal void DisposeDescription()
			{
				_descriptionPanel?.Dispose();
				_descriptionPanel = null;
				_describedChoice = null;
			}
		}

		[HarmonyPatch(typeof(RadialBase), "Update")]
		private static class RadialUpdatePatch
		{
			private static void Postfix(RadialBase __instance)
			{
				UpdateDescription(__instance);
			}
		}

		[HarmonyPatch(typeof(RadialBase), "Open")]
		private static class RadialOpenPatch
		{
			private static void Prefix(RadialBase __instance)
			{
				HideDescription(__instance);
			}
		}

		[HarmonyPatch(typeof(RadialBase), "Close")]
		private static class RadialClosePatch
		{
			private static void Prefix(RadialBase __instance)
			{
				HideDescription(__instance);
			}
		}

		private sealed class DescriptionPanel : IDisposable
		{
			private readonly GameObject _root;

			private readonly RectTransform _layoutRoot;

			private readonly RectTransform _panel;

			private readonly TMP_Text _text;

			private readonly Vector3[] _corners = (Vector3[])(object)new Vector3[4];

			private readonly Vector3 _baseScale;

			internal DescriptionPanel(Hud? hud, RadialBase radial)
			{
				//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
				//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
				UITooltip val = (((Object)(object)hud?.m_pieceIconPrefab != (Object)null) ? hud.m_pieceIconPrefab.GetComponent<UITooltip>() : null);
				if ((Object)(object)val?.m_tooltipPrefab == (Object)null)
				{
					throw new InvalidOperationException("The HUD tooltip template is unavailable.");
				}
				_root = Object.Instantiate<GameObject>(val.m_tooltipPrefab, ((Component)radial).transform);
				try
				{
					((Object)_root).name = "ForsakenRadial_Description";
					_root.SetActive(false);
					_root.transform.SetAsLastSibling();
					_baseScale = _root.transform.localScale;
					ref RectTransform layoutRoot = ref _layoutRoot;
					Transform transform = _root.transform;
					layoutRoot = (RectTransform)(object)(((transform is RectTransform) ? transform : null) ?? throw new InvalidOperationException("The HUD tooltip layout is unavailable."));
					ref RectTransform panel = ref _panel;
					Transform child = _root.transform.GetChild(0);
					panel = (RectTransform)(object)(((child is RectTransform) ? child : null) ?? throw new InvalidOperationException("The HUD tooltip panel is unavailable."));
					Transform val2 = FindChild(_root.transform, "Topic");
					if ((Object)(object)val2 != (Object)null)
					{
						((Component)val2).gameObject.SetActive(false);
					}
					Transform? obj = FindChild(_root.transform, "Text");
					_text = ((obj != null) ? ((Component)obj).GetComponent<TMP_Text>() : null) ?? _root.GetComponentInChildren<TMP_Text>(true) ?? throw new InvalidOperationException("The HUD tooltip text is unavailable.");
					if ((Object)(object)val2 == (Object)null)
					{
						_panel = _layoutRoot;
						HorizontalOrVerticalLayoutGroup component = _root.GetComponent<HorizontalOrVerticalLayoutGroup>();
						if ((Object)(object)component != (Object)null)
						{
							component.childControlHeight = true;
						}
						ContentSizeFitter component2 = _root.GetComponent<ContentSizeFitter>();
						if ((Object)(object)component2 != (Object)null)
						{
							component2.verticalFit = (FitMode)2;
						}
					}
					Graphic[] componentsInChildren = _root.GetComponentsInChildren<Graphic>(true);
					for (int i = 0; i < componentsInChildren.Length; i++)
					{
						componentsInChildren[i].raycastTarget = false;
					}
				}
				catch
				{
					Object.Destroy((Object)(object)_root);
					throw;
				}
			}

			internal void Show(string text)
			{
				if (string.IsNullOrWhiteSpace(text))
				{
					Hide();
					return;
				}
				_text.text = text;
				_root.SetActive(true);
				LayoutRebuilder.ForceRebuildLayoutImmediate(_layoutRoot);
			}

			internal void Position(RectTransform icon, Vector2 center)
			{
				//IL_005e: Unknown result type (might be due to invalid IL or missing references)
				//IL_0063: Unknown result type (might be due to invalid IL or missing references)
				//IL_0070: Unknown result type (might be due to invalid IL or missing references)
				//IL_0092: Unknown result type (might be due to invalid IL or missing references)
				//IL_009e: 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_00a8: 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_00c4: Unknown result type (might be due to invalid IL or missing references)
				//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
				//IL_0104: 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_0136: Unknown result type (might be due to invalid IL or missing references)
				//IL_0142: Unknown result type (might be due to invalid IL or missing references)
				//IL_0147: Unknown result type (might be due to invalid IL or missing references)
				//IL_014c: Unknown result type (might be due to invalid IL or missing references)
				//IL_0151: Unknown result type (might be due to invalid IL or missing references)
				//IL_0154: Unknown result type (might be due to invalid IL or missing references)
				//IL_015e: Unknown result type (might be due to invalid IL or missing references)
				//IL_017a: Unknown result type (might be due to invalid IL or missing references)
				//IL_0197: Unknown result type (might be due to invalid IL or missing references)
				//IL_01f9: Unknown result type (might be due to invalid IL or missing references)
				//IL_020c: Unknown result type (might be due to invalid IL or missing references)
				//IL_0216: Unknown result type (might be due to invalid IL or missing references)
				//IL_0239: Unknown result type (might be due to invalid IL or missing references)
				//IL_025b: Unknown result type (might be due to invalid IL or missing references)
				//IL_0260: Unknown result type (might be due to invalid IL or missing references)
				//IL_0269: Unknown result type (might be due to invalid IL or missing references)
				//IL_026e: Unknown result type (might be due to invalid IL or missing references)
				//IL_0273: Unknown result type (might be due to invalid IL or missing references)
				//IL_0278: Unknown result type (might be due to invalid IL or missing references)
				//IL_027d: Unknown result type (might be due to invalid IL or missing references)
				//IL_01b9: Unknown result type (might be due to invalid IL or missing references)
				if (_root.activeSelf)
				{
					icon.GetWorldCorners(_corners);
					Rect val = Rect.MinMaxRect(_corners[0].x, _corners[0].y, _corners[2].x, _corners[2].y);
					_root.transform.localScale = _baseScale;
					_panel.GetWorldCorners(_corners);
					Vector2 val2 = Vector2.op_Implicit(_corners[2] - _corners[0]);
					float num = Mathf.Min(1f, Mathf.Min(((float)Screen.width - 16f) / Mathf.Max(1f, val2.x), ((float)Screen.height - 16f) / Mathf.Max(1f, val2.y)));
					_root.transform.localScale = _baseScale * Mathf.Max(0.01f, num);
					_panel.GetWorldCorners(_corners);
					val2 = Vector2.op_Implicit(_corners[2] - _corners[0]);
					bool flag = ((Rect)(ref val)).center.x >= center.x;
					float num2 = (flag ? (((Rect)(ref val)).xMax + 8f) : (((Rect)(ref val)).xMin - 8f - val2.x));
					if (flag && num2 + val2.x > (float)Screen.width - 8f)
					{
						num2 = ((Rect)(ref val)).xMin - 8f - val2.x;
					}
					else if (!flag && num2 < 8f)
					{
						num2 = ((Rect)(ref val)).xMax + 8f;
					}
					Vector2 val3 = default(Vector2);
					((Vector2)(ref val3))..ctor(Mathf.Clamp(num2, 8f, Mathf.Max(8f, (float)Screen.width - 8f - val2.x)), Mathf.Clamp(((Rect)(ref val)).center.y - val2.y * 0.5f, 8f, Mathf.Max(8f, (float)Screen.height - 8f - val2.y)));
					Transform transform = _root.transform;
					transform.position += Vector2.op_Implicit(val3 - Vector2.op_Implicit(_corners[0]));
				}
			}

			internal void Hide()
			{
				if ((Object)(object)_root != (Object)null)
				{
					_root.SetActive(false);
				}
			}

			public void Dispose()
			{
				if (!((Object)(object)_root == (Object)null))
				{
					_root.SetActive(false);
					Object.Destroy((Object)(object)_root);
				}
			}

			private static Transform? FindChild(Transform root, string name)
			{
				//IL_000f: Unknown result type (might be due to invalid IL or missing references)
				//IL_0015: Expected O, but got Unknown
				foreach (Transform item in root)
				{
					Transform val = item;
					if (((Object)val).name == name)
					{
						return val;
					}
					Transform val2 = FindChild(val, name);
					if ((Object)(object)val2 != (Object)null)
					{
						return val2;
					}
				}
				return null;
			}
		}

		private static readonly PowerDefinition[] Powers = new PowerDefinition[7]
		{
			new PowerDefinition("GP_Eikthyr", "defeated_eikthyr"),
			new PowerDefinition("GP_TheElder", "defeated_gdking"),
			new PowerDefinition("GP_Bonemass", "defeated_bonemass"),
			new PowerDefinition("GP_Moder", "defeated_dragon"),
			new PowerDefinition("GP_Yagluth", "defeated_goblinking"),
			new PowerDefinition("GP_Queen", "defeated_queen"),
			new PowerDefinition("GP_Fader", "defeated_fader")
		};

		private static readonly MethodInfo ConstructRadialMethod = AccessTools.Method(typeof(RadialBase), "ConstructRadial", new Type[1] { typeof(List<RadialMenuElement>) }, (Type[])null);

		private static readonly MethodInfo AppendTileAndConstructMethod = AccessTools.Method(typeof(ForsakenPowerRadial), "AppendTileAndConstruct", (Type[])null, (Type[])null);

		private static ForsakenRadialConfig? _tooltipConfig;

		private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
		{
			bool patched = false;
			foreach (CodeInstruction instruction in instructions)
			{
				if (!patched && CodeInstructionExtensions.Calls(instruction, ConstructRadialMethod))
				{
					yield return new CodeInstruction(OpCodes.Call, (object)AppendTileAndConstructMethod);
					patched = true;
				}
				else
				{
					yield return instruction;
				}
			}
			if (!patched)
			{
				throw new InvalidOperationException("Could not find the vanilla radial construction call to add the Forsaken Powers tile.");
			}
		}

		private static void AppendTileAndConstruct(RadialBase radial, List<RadialMenuElement> elements)
		{
			ZoneSystem instance = ZoneSystem.instance;
			ObjectDB instance2 = ObjectDB.instance;
			if ((Object)(object)instance == (Object)null || (Object)(object)instance2 == (Object)null || radial.CurrentConfig == null || (Object)(object)RadialData.SO == (Object)null || (Object)(object)RadialData.SO.GroupElement == (Object)null)
			{
				radial.ConstructRadial(elements);
				return;
			}
			List<ForsakenPowerChoice> list = new List<ForsakenPowerChoice>();
			PowerDefinition[] powers = Powers;
			foreach (PowerDefinition powerDefinition in powers)
			{
				if (instance.GetGlobalKey(powerDefinition.DefeatedKey))
				{
					StatusEffect statusEffect = instance2.GetStatusEffect(StringExtensionMethods.GetStableHashCode(powerDefinition.Power));
					if ((Object)(object)statusEffect == (Object)null || (Object)(object)statusEffect.m_icon == (Object)null)
					{
						ForsakenRadialPlugin.Log.LogWarning((object)("The status effect or icon for '" + powerDefinition.Power + "' is missing; its radial entry was skipped."));
					}
					else
					{
						list.Add(new ForsakenPowerChoice(powerDefinition.Power, statusEffect.m_name, statusEffect.m_icon, GetEffectDescription(statusEffect)));
					}
				}
			}
			if (list.Count > 0)
			{
				ForsakenRadialConfig forsakenRadialConfig = new ForsakenRadialConfig();
				forsakenRadialConfig.Initialize(Player.m_localPlayer, list);
				GroupElement val = Object.Instantiate<GroupElement>(RadialData.SO.GroupElement);
				val.Init((IRadialConfig)(object)forsakenRadialConfig, radial.CurrentConfig, radial);
				elements.Add((RadialMenuElement)(object)val);
			}
			radial.ConstructRadial(elements);
		}

		private static string GetEffectDescription(StatusEffect effect)
		{
			string text = effect.GetTooltipString() ?? effect.m_tooltip ?? string.Empty;
			if (Localization.instance != null)
			{
				text = Localization.instance.Localize(text);
			}
			text = text.Replace("\r\n", "\n").Replace('\r', '\n');
			int num = text.IndexOf("\n\n", StringComparison.Ordinal);
			text = ((num < 0) ? string.Empty : text.Substring(num + 2).Trim());
			AddAttribute((StatusAttribute)4, "Tailwind while sailing");
			AddAttribute((StatusAttribute)8, "Faster taming of nearby creatures");
			AddAttribute((StatusAttribute)2, "Incoming collision damage: <color=orange>×2</color>");
			return text;
			void AddAttribute(StatusAttribute attribute, string description)
			{
				//IL_0006: Unknown result type (might be due to invalid IL or missing references)
				if (effect.HaveAttribute(attribute))
				{
					string text2 = ((Localization.instance == null) ? description : Localization.instance.Localize(description));
					text = ((text.Length == 0) ? text2 : (text + "\n" + text2));
				}
			}
		}

		internal static void UpdateDescription(RadialBase radial)
		{
			if (radial.CurrentConfig is ForsakenRadialConfig forsakenRadialConfig)
			{
				_tooltipConfig = forsakenRadialConfig;
				forsakenRadialConfig.UpdateDescription(radial);
			}
			else
			{
				HideDescription(radial);
			}
		}

		internal static void HideDescription(RadialBase radial)
		{
			if (_tooltipConfig != null && _tooltipConfig.Radial == radial)
			{
				_tooltipConfig.DisposeDescription();
				_tooltipConfig = null;
			}
		}

		internal static void Shutdown()
		{
			if ((Object)(object)_tooltipConfig?.Radial != (Object)null)
			{
				HideDescription(_tooltipConfig.Radial);
			}
		}
	}
	[BepInPlugin("com.thedefside.forsakenradial", "ForsakenRadial", "0.3.1")]
	public sealed class ForsakenRadialPlugin : BaseUnityPlugin
	{
		public const string PluginGuid = "com.thedefside.forsakenradial";

		public const string PluginName = "ForsakenRadial";

		public const string PluginVersion = "0.3.1";

		internal static ManualLogSource Log;

		private Harmony? _harmony;

		private bool _patchesApplied;

		private void Awake()
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			_harmony = new Harmony("com.thedefside.forsakenradial");
			try
			{
				_harmony.PatchAll(typeof(ForsakenRadialPlugin).Assembly);
				_patchesApplied = true;
			}
			catch (Exception arg)
			{
				Log.LogError((object)$"Could not install radial integration patches: {arg}");
				try
				{
					_harmony.UnpatchSelf();
				}
				catch (Exception arg2)
				{
					Log.LogError((object)$"Could not clean up failed radial integration patches: {arg2}");
				}
				_harmony = null;
				return;
			}
			Log.LogInfo((object)"ForsakenRadial loaded. Forsaken Powers is added to the vanilla radial.");
		}

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