Decompiled source of PowerShower v1.1.1

ForsakenPowerDuration.dll

Decompiled 3 weeks ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using TMPro;
using UnityEngine;
using UnityEngine.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("ForsakenPowerDuration")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.1.1.0")]
[assembly: AssemblyInformationalVersion("1.1.1")]
[assembly: AssemblyProduct("ForsakenPowerDuration")]
[assembly: AssemblyTitle("ForsakenPowerDuration")]
[assembly: AssemblyVersion("1.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 ForsakenPowerDuration
{
	[BepInPlugin("local.valheim.forsakenpowerduration", "Forsaken Power Duration", "1.1.1")]
	public sealed class Plugin : BaseUnityPlugin
	{
		private sealed class PowerOptions
		{
			public string Id;

			public string Name;

			public int Hash;

			public ConfigEntry<bool> Enabled;

			public ConfigEntry<KeyboardShortcut> Shortcut;

			public ConfigEntry<float> Cooldown;

			public float DefaultCooldown;
		}

		private sealed class PlayerState
		{
			public readonly Dictionary<int, float> Remaining = new Dictionary<int, float>();

			public PowerOptions Pending;

			public float PendingUntil;

			public bool StartingKey;

			public int ShortcutFrame = -1;

			public bool ShortcutConsumed;
		}

		private sealed class ActivationState
		{
			public StatusEffect Original;

			public float OriginalCooldown;

			public PowerOptions Power;

			public bool Swapped;

			public float Before;
		}

		[HarmonyPatch(typeof(Player), "UpdateGuardianPower")]
		private static class TickCooldowns
		{
			private static void Postfix(Player __instance, float __0)
			{
				//IL_008f: Unknown result type (might be due to invalid IL or missing references)
				//IL_0095: Expected O, but got Unknown
				if ((Object)(object)instance == (Object)null || (Object)(object)__instance != (Object)(object)Player.m_localPlayer)
				{
					return;
				}
				PlayerState playerState = instance.StateFor(__instance);
				foreach (int item in new List<int>(playerState.Remaining.Keys))
				{
					playerState.Remaining[item] = Math.Max(0f, playerState.Remaining[item] - __0);
				}
				StatusEffect val = (StatusEffect)GuardianEffect.GetValue(__instance);
				if ((Object)(object)val != (Object)null && instance.powers.ContainsKey(val.NameHash()))
				{
					float num = Math.Max(Remaining(playerState, val.NameHash()), (float)GuardianCooldown.GetValue(__instance));
					GuardianCooldown.SetValue(__instance, num);
				}
			}
		}

		[HarmonyPatch(typeof(Player), "StartGuardianPower")]
		private static class CheckNativeStart
		{
			private static bool Prefix(Player __instance, ref bool __result)
			{
				//IL_006a: Unknown result type (might be due to invalid IL or missing references)
				//IL_0070: Expected O, but got Unknown
				if ((Object)(object)instance == (Object)null || (Object)(object)__instance != (Object)(object)Player.m_localPlayer)
				{
					return true;
				}
				PlayerState playerState = instance.StateFor(__instance);
				if (playerState.StartingKey)
				{
					return true;
				}
				if (instance.HandleShortcut(__instance, playerState))
				{
					__result = false;
					return false;
				}
				if (playerState.Pending != null && Time.time <= playerState.PendingUntil)
				{
					__result = false;
					return false;
				}
				StatusEffect val = (StatusEffect)GuardianEffect.GetValue(__instance);
				if ((Object)(object)val != (Object)null)
				{
					GuardianCooldown.SetValue(__instance, Math.Max((float)GuardianCooldown.GetValue(__instance), Remaining(playerState, val.NameHash())));
				}
				return true;
			}
		}

		[HarmonyPatch(typeof(Player), "ActivateGuardianPower")]
		private static class ActivatePower
		{
			private static void Prefix(Player __instance, out ActivationState __state)
			{
				//IL_003b: Unknown result type (might be due to invalid IL or missing references)
				//IL_0045: Expected O, but got Unknown
				__state = null;
				if (!((Object)(object)instance == (Object)null) && !((Object)(object)__instance != (Object)(object)Player.m_localPlayer))
				{
					PlayerState playerState = instance.StateFor(__instance);
					ActivationState activationState = (__state = new ActivationState
					{
						Original = (StatusEffect)GuardianEffect.GetValue(__instance),
						OriginalCooldown = (float)GuardianCooldown.GetValue(__instance)
					});
					if (playerState.Pending != null && Time.time <= playerState.PendingUntil)
					{
						activationState.Power = playerState.Pending;
						activationState.Swapped = true;
						GuardianEffect.SetValue(__instance, ObjectDB.instance.GetStatusEffect(activationState.Power.Hash));
						GuardianCooldown.SetValue(__instance, Remaining(playerState, activationState.Power.Hash));
					}
					else if ((Object)(object)activationState.Original != (Object)null)
					{
						instance.powers.TryGetValue(activationState.Original.NameHash(), out activationState.Power);
					}
					playerState.Pending = null;
					activationState.Before = (float)GuardianCooldown.GetValue(__instance);
				}
			}

			private static Exception Finalizer(Player __instance, ActivationState __state, Exception __exception)
			{
				if ((Object)(object)instance == (Object)null || __state == null)
				{
					return __exception;
				}
				PlayerState playerState = instance.StateFor(__instance);
				float num = (float)GuardianCooldown.GetValue(__instance);
				if (__exception == null && __state.Power != null && __state.Before <= 0f && num > 0f)
				{
					float num2 = __state.Power.Cooldown.Value;
					if (float.IsNaN(num2) || float.IsInfinity(num2) || num2 < 0f)
					{
						num2 = __state.Power.DefaultCooldown;
					}
					playerState.Remaining[__state.Power.Hash] = num2;
					GuardianCooldown.SetValue(__instance, num2);
				}
				if (__state.Swapped)
				{
					GuardianEffect.SetValue(__instance, __state.Original);
					float num3 = __state.OriginalCooldown;
					if ((Object)(object)__state.Original != (Object)null)
					{
						num3 = Math.Max(num3, Remaining(playerState, __state.Original.NameHash()));
					}
					GuardianCooldown.SetValue(__instance, num3);
				}
				return __exception;
			}
		}

		[HarmonyPatch(typeof(Player), "SetGuardianPower")]
		private static class SwitchEquippedPower
		{
			private static void Prefix(Player __instance)
			{
				//IL_0026: Unknown result type (might be due to invalid IL or missing references)
				//IL_002c: Expected O, but got Unknown
				if (!((Object)(object)instance == (Object)null) && !((Object)(object)__instance != (Object)(object)Player.m_localPlayer))
				{
					StatusEffect val = (StatusEffect)GuardianEffect.GetValue(__instance);
					if ((Object)(object)val != (Object)null)
					{
						PlayerState playerState = instance.StateFor(__instance);
						playerState.Remaining[val.NameHash()] = Math.Max(Remaining(playerState, val.NameHash()), (float)GuardianCooldown.GetValue(__instance));
					}
				}
			}

			private static void Postfix(Player __instance)
			{
				//IL_0026: Unknown result type (might be due to invalid IL or missing references)
				//IL_002c: Expected O, but got Unknown
				if (!((Object)(object)instance == (Object)null) && !((Object)(object)__instance != (Object)(object)Player.m_localPlayer))
				{
					StatusEffect val = (StatusEffect)GuardianEffect.GetValue(__instance);
					if ((Object)(object)val != (Object)null && instance.powers.ContainsKey(val.NameHash()))
					{
						GuardianCooldown.SetValue(__instance, Remaining(instance.StateFor(__instance), val.NameHash()));
					}
				}
			}
		}

		[HarmonyPatch(typeof(Player), "Save")]
		private static class SaveCooldowns
		{
			private static void Prefix(Player __instance)
			{
				if ((Object)(object)instance == (Object)null || (Object)(object)__instance != (Object)(object)Player.m_localPlayer)
				{
					return;
				}
				PlayerState state = instance.StateFor(__instance);
				foreach (PowerOptions value in instance.powers.Values)
				{
					__instance.m_customData["local.valheim.forsakenpowerduration.cooldown." + value.Id] = Remaining(state, value.Hash).ToString("R", CultureInfo.InvariantCulture);
				}
			}
		}

		[HarmonyPatch(typeof(Player), "Load")]
		private static class ReloadCooldowns
		{
			private static void Postfix(Player __instance)
			{
				instance?.playerStates.Remove(__instance);
			}
		}

		[HarmonyPatch(typeof(ObjectDB), "Awake")]
		private static class DatabaseAwakePatch
		{
			private static void Postfix(ObjectDB __instance)
			{
				instance?.RegisterPowers(__instance);
			}
		}

		[HarmonyPatch(typeof(ObjectDB), "CopyOtherDB")]
		private static class DatabaseCopyPatch
		{
			private static void Postfix(ObjectDB __instance)
			{
				instance?.RegisterPowers(__instance);
			}
		}

		[HarmonyPatch(typeof(SEMan), "Internal_AddStatusEffect")]
		private static class AddPowerPatch
		{
			private static void Postfix(SEMan __instance, int __0, bool __1, StatusEffect __result)
			{
				instance?.ApplyDuration(__instance, __0, __1, __result);
			}
		}

		public enum PowerHudDirection
		{
			Up,
			Right
		}

		private sealed class PowerHudEntry
		{
			public RectTransform Root;

			public Image Icon;

			public TMP_Text Name;

			public TMP_Text Timer;
		}

		[HarmonyPatch(typeof(Hud), "UpdateGuardianPower")]
		private static class MultiPowerHudPatch
		{
			private static void Postfix(Hud __instance, Player __0, RectTransform ___m_gpRoot, Image ___m_gpIcon, TMP_Text ___m_gpName, TMP_Text ___m_gpCooldown)
			{
				if ((Object)(object)instance != (Object)null)
				{
					instance.UpdatePowerHud(__instance, __0, ___m_gpRoot, ___m_gpIcon, ___m_gpName, ___m_gpCooldown);
				}
			}
		}

		[HarmonyPatch(typeof(Hud), "OnDestroy")]
		private static class DestroyPowerHudPatch
		{
			private static void Prefix(Hud __instance)
			{
				if ((Object)(object)instance != (Object)null && (Object)(object)instance.hudOwner == (Object)(object)__instance)
				{
					instance.ClearPowerHud(restoreOriginal: false);
				}
			}
		}

		private readonly Dictionary<int, PowerOptions> powers = new Dictionary<int, PowerOptions>();

		private readonly ConditionalWeakTable<Player, PlayerState> playerStates = new ConditionalWeakTable<Player, PlayerState>();

		private static readonly FieldInfo GuardianEffect = AccessTools.Field(typeof(Player), "m_guardianSE");

		private static readonly FieldInfo GuardianCooldown = AccessTools.Field(typeof(Player), "m_guardianPowerCooldown");

		private static readonly MethodInfo StartPower = AccessTools.Method(typeof(Player), "StartGuardianPower", (Type[])null, (Type[])null);

		private static readonly MethodInfo TakeInput = AccessTools.Method(typeof(Player), "TakeInput", (Type[])null, (Type[])null);

		private const string SavePrefix = "local.valheim.forsakenpowerduration.cooldown.";

		public const string Guid = "local.valheim.forsakenpowerduration";

		private static Plugin instance;

		private Harmony harmony;

		private readonly Dictionary<int, ConfigEntry<float>> durations = new Dictionary<int, ConfigEntry<float>>();

		private readonly Dictionary<int, float> defaults = new Dictionary<int, float>();

		private static readonly string[] PowerIds = new string[8] { "GP_Eikthyr", "GP_TheElder", "GP_Bonemass", "GP_Moder", "GP_Yagluth", "GP_Queen", "GP_Ashlands", "GP_DeepNorth" };

		private static readonly string[] PowerNames = new string[8] { "Eikthyr", "The Elder", "Bonemass", "Moder", "Yagluth", "The Queen", "Fader", "Deep North" };

		private ConfigEntry<bool> showPowerHud;

		private ConfigEntry<PowerHudDirection> hudDirection;

		private ConfigEntry<float> hudSpacing;

		private ConfigEntry<float> hudScale;

		private ConfigEntry<float> hudOffsetX;

		private ConfigEntry<float> hudOffsetY;

		private Hud hudOwner;

		private RectTransform hudTemplate;

		private readonly Dictionary<int, PowerHudEntry> hudEntries = new Dictionary<int, PowerHudEntry>();

		private void RegisterActivationSettings(StatusEffect prefab, string id, string name, int index)
		{
			//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d7: 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_0139: Unknown result type (might be due to invalid IL or missing references)
			//IL_016b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0175: Expected O, but got Unknown
			string text = (index + 1).ToString("00") + " - " + name;
			PowerOptions powerOptions = new PowerOptions
			{
				Id = id,
				Name = name,
				Hash = prefab.NameHash(),
				DefaultCooldown = prefab.m_cooldown
			};
			powerOptions.Cooldown = ((BaseUnityPlugin)this).Config.Bind<float>(text, "Cooldown (seconds)", prefab.m_cooldown, new ConfigDescription("Time before this power can be activated again, separate from its active duration. 60 seconds = 1 minute; 1200 seconds = 20 minutes. Vanilla: " + prefab.m_cooldown.ToString("0.##", CultureInfo.InvariantCulture) + " seconds. Applies on the next activation; does not shorten an existing cooldown. Cooldowns are independent for each power and saved with your character.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, Math.Max(86400f, prefab.m_cooldown)), new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Order = 30,
					IsAdvanced = false,
					ShowRangeAsPercent = false
				}
			}));
			powerOptions.Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>(text, "Enable separate keybind", false, new ConfigDescription("Allow this power to be activated using the keybind below, alongside other enabled powers. You must first unlock it by selecting it at its boss stone. Different active powers can overlap. Disabled by default. Your normally equipped power still works with the game's power button.", (AcceptableValueBase)null, new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Order = 20,
					IsAdvanced = false
				}
			}));
			powerOptions.Shortcut = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>(text, "Activation keybind", new KeyboardShortcut((KeyCode)0, Array.Empty<KeyCode>()), new ConfigDescription("Choose a key or key combination for this power. None means unassigned. Requires Enable separate keybind and the power to be unlocked. Choose unique shortcuts. An assigned shortcut takes priority over the game's normal power button, including on cooldown. Shortcuts are ignored while game menus or text input are open. If several shortcuts match, only the first power in boss order is attempted.", (AcceptableValueBase)null, new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Order = 10,
					IsAdvanced = false
				}
			}));
			powers.Add(powerOptions.Hash, powerOptions);
		}

		private PlayerState StateFor(Player player)
		{
			return playerStates.GetValue(player, delegate(Player p)
			{
				//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
				//IL_00ad: Expected O, but got Unknown
				PlayerState playerState = new PlayerState();
				foreach (PowerOptions value3 in powers.Values)
				{
					if (p.m_customData.TryGetValue("local.valheim.forsakenpowerduration.cooldown." + value3.Id, out var value) && float.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out var result) && !float.IsNaN(result) && !float.IsInfinity(result) && result > 0f)
					{
						playerState.Remaining[value3.Hash] = result;
					}
				}
				StatusEffect val = (StatusEffect)GuardianEffect.GetValue(p);
				if ((Object)(object)val != (Object)null)
				{
					float val2 = (float)GuardianCooldown.GetValue(p);
					playerState.Remaining.TryGetValue(val.NameHash(), out var value2);
					playerState.Remaining[val.NameHash()] = Math.Max(val2, value2);
				}
				return playerState;
			});
		}

		private static float Remaining(PlayerState state, int hash)
		{
			if (!state.Remaining.TryGetValue(hash, out var value))
			{
				return 0f;
			}
			return value;
		}

		private void Update()
		{
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer != (Object)null)
			{
				HandleShortcut(localPlayer, StateFor(localPlayer));
			}
		}

		private bool HandleShortcut(Player player, PlayerState state)
		{
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_0100: Expected O, but got Unknown
			if (Cursor.visible || !(bool)TakeInput.Invoke(player, null))
			{
				return false;
			}
			if (state.ShortcutFrame == Time.frameCount)
			{
				return state.ShortcutConsumed;
			}
			foreach (PowerOptions value2 in powers.Values)
			{
				if (!value2.Enabled.Value)
				{
					continue;
				}
				KeyboardShortcut value = value2.Shortcut.Value;
				if (!((KeyboardShortcut)(ref value)).IsDown())
				{
					continue;
				}
				state.ShortcutFrame = Time.frameCount;
				state.ShortcutConsumed = true;
				if (state.Pending != null && Time.time <= state.PendingUntil)
				{
					return true;
				}
				state.Pending = null;
				if (!((Humanoid)player).HaveUniqueKey(value2.Id))
				{
					((Character)player).Message((MessageType)2, "Unlock " + value2.Name + " at its boss stone first.", 0, (Sprite)null, false);
					return true;
				}
				float num = Remaining(state, value2.Hash);
				StatusEffect val = (StatusEffect)GuardianEffect.GetValue(player);
				if ((Object)(object)val != (Object)null && val.NameHash() == value2.Hash)
				{
					num = Math.Max(num, (float)GuardianCooldown.GetValue(player));
				}
				if (num > 0f)
				{
					((Character)player).Message((MessageType)2, value2.Name + ": " + Math.Ceiling(num) + "s cooldown remaining.", 0, (Sprite)null, false);
					return true;
				}
				StatusEffect statusEffect = ObjectDB.instance.GetStatusEffect(value2.Hash);
				if ((Object)(object)statusEffect == (Object)null)
				{
					return true;
				}
				float num2 = (float)GuardianCooldown.GetValue(player);
				try
				{
					state.StartingKey = true;
					GuardianEffect.SetValue(player, statusEffect);
					GuardianCooldown.SetValue(player, 0f);
					if ((bool)StartPower.Invoke(player, null))
					{
						state.Pending = value2;
						state.PendingUntil = Time.time + 10f;
					}
				}
				finally
				{
					state.StartingKey = false;
					GuardianEffect.SetValue(player, val);
					GuardianCooldown.SetValue(player, num2);
				}
				return true;
			}
			return false;
		}

		private void Awake()
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Expected O, but got Unknown
			instance = this;
			RegisterHudSettings();
			harmony = new Harmony("local.valheim.forsakenpowerduration");
			harmony.PatchAll(typeof(Plugin).Assembly);
			if ((Object)(object)ObjectDB.instance != (Object)null)
			{
				RegisterPowers(ObjectDB.instance);
			}
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Forsaken Power Duration loaded. Settings appear once the game loads its powers.");
		}

		private void RegisterPowers(ObjectDB db)
		{
			//IL_013c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0146: Expected O, but got Unknown
			int i;
			for (i = 0; i < PowerIds.Length; i++)
			{
				StatusEffect val = db.m_StatusEffects.Find((StatusEffect effect) => (Object)(object)effect != (Object)null && ((Object)effect).name == PowerIds[i]);
				if (!((Object)(object)val == (Object)null) && !durations.ContainsKey(val.NameHash()))
				{
					float ttl = val.m_ttl;
					if (!(ttl <= 0f) && !float.IsNaN(ttl) && !float.IsInfinity(ttl))
					{
						string text = ttl.ToString("0.##", CultureInfo.InvariantCulture);
						ConfigEntry<float> value = ((BaseUnityPlugin)this).Config.Bind<float>((i + 1).ToString("00") + " - " + PowerNames[i], "Duration (seconds)", ttl, new ConfigDescription("How long " + PowerNames[i] + "'s Forsaken power lasts after activation. Measured in seconds: 60 = 1 minute, 300 = 5 minutes, 600 = 10 minutes. Vanilla duration in this game version: " + text + " seconds. Default/Reset restores this value. Changes apply on the next activation or refresh, including powers received from nearby players. Set activation cooldown separately below. Multiplayer uses each recipient's own settings.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, Math.Max(86400f, ttl)), new object[1]
						{
							new ConfigurationManagerAttributes
							{
								Order = 40,
								IsAdvanced = false,
								ShowRangeAsPercent = false
							}
						}));
						durations.Add(val.NameHash(), value);
						defaults.Add(val.NameHash(), ttl);
						RegisterActivationSettings(val, PowerIds[i], PowerNames[i], i);
					}
				}
			}
		}

		private void ApplyDuration(SEMan manager, int hash, bool reset, StatusEffect result)
		{
			if (!durations.TryGetValue(hash, out var value))
			{
				if ((Object)(object)ObjectDB.instance != (Object)null)
				{
					RegisterPowers(ObjectDB.instance);
				}
				if (!durations.TryGetValue(hash, out value))
				{
					return;
				}
			}
			if (!((Object)(object)result == (Object)null) || reset)
			{
				StatusEffect val = (((Object)(object)result != (Object)null) ? result : manager.GetStatusEffect(hash));
				if (!((Object)(object)val == (Object)null))
				{
					float value2 = value.Value;
					val.m_ttl = ((float.IsNaN(value2) || float.IsInfinity(value2) || value2 < 1f) ? defaults[hash] : value2);
				}
			}
		}

		private void OnDestroy()
		{
			ClearPowerHud(restoreOriginal: true);
			Harmony obj = harmony;
			if (obj != null)
			{
				obj.UnpatchSelf();
			}
			instance = null;
		}

		private void RegisterHudSettings()
		{
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Expected O, but got Unknown
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Expected O, but got Unknown
			showPowerHud = ((BaseUnityPlugin)this).Config.Bind<bool>("00 - Power display", "Show all usable and active powers", true, new ConfigDescription("Replace the single equipped-power display with an entry for each enabled, unlocked power, plus your equipped power, active power buffs, and powers still on cooldown. Shows the keybind, cooldown or Ready, and remaining active duration. A green icon means the buff is active. Turn off to restore the game's original display. This setting only changes the HUD.", (AcceptableValueBase)null, new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Order = 60
				}
			}));
			hudDirection = ((BaseUnityPlugin)this).Config.Bind<PowerHudDirection>("00 - Power display", "Layout direction", PowerHudDirection.Up, new ConfigDescription("Up stacks entries above the original power area, avoiding food slots to the right. Right arranges them horizontally. Entries follow boss progression order.", (AcceptableValueBase)null, new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Order = 50
				}
			}));
			hudSpacing = HudNumber("00 - Power display", "Entry spacing", 90f, 60f, 240f, "Distance between entries in HUD units before scaling. Increase if names or timers overlap.", 40);
			hudScale = HudNumber("00 - Power display", "Display scale", 1f, 0.5f, 2f, "Size of the power entries relative to the game's original display. 1 = original size.", 30);
			hudOffsetX = HudNumber("00 - Power display", "Horizontal offset", 0f, -1000f, 1000f, "Move all entries relative to the original power position. Positive moves right; negative moves left.", 20);
			hudOffsetY = HudNumber("00 - Power display", "Vertical offset", 0f, -1000f, 1000f, "Move all entries relative to the original power position. Positive moves up; negative moves down. Layout settings apply immediately and can be adjusted for other HUD mods.", 10);
		}

		private ConfigEntry<float> HudNumber(string section, string name, float value, float min, float max, string description, int order)
		{
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Expected O, but got Unknown
			return ((BaseUnityPlugin)this).Config.Bind<float>(section, name, value, new ConfigDescription(description, (AcceptableValueBase)(object)new AcceptableValueRange<float>(min, max), new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Order = order,
					ShowRangeAsPercent = false
				}
			}));
		}

		private PowerHudEntry CreateHudEntry(RectTransform template, Image icon, TMP_Text name, TMP_Text timer, int hash)
		{
			//IL_012d: 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_0156: Unknown result type (might be due to invalid IL or missing references)
			//IL_0162: Unknown result type (might be due to invalid IL or missing references)
			//IL_0171: Unknown result type (might be due to invalid IL or missing references)
			//IL_0176: Unknown result type (might be due to invalid IL or missing references)
			RectTransform val = Object.Instantiate<RectTransform>(template, ((Transform)template).parent, false);
			((Object)val).name = "ForsakenPowerDuration_" + hash;
			PowerHudEntry powerHudEntry = new PowerHudEntry
			{
				Root = val,
				Icon = MapHudComponent<Image>(val, template, icon),
				Name = MapHudComponent<TMP_Text>(val, template, name),
				Timer = MapHudComponent<TMP_Text>(val, template, timer)
			};
			UIInputHandler[] componentsInChildren = ((Component)val).GetComponentsInChildren<UIInputHandler>(true);
			for (int i = 0; i < componentsInChildren.Length; i++)
			{
				((Behaviour)componentsInChildren[i]).enabled = false;
			}
			Button[] componentsInChildren2 = ((Component)val).GetComponentsInChildren<Button>(true);
			for (int i = 0; i < componentsInChildren2.Length; i++)
			{
				((Selectable)componentsInChildren2[i]).interactable = false;
			}
			Graphic[] componentsInChildren3 = ((Component)val).GetComponentsInChildren<Graphic>(true);
			for (int i = 0; i < componentsInChildren3.Length; i++)
			{
				componentsInChildren3[i].raycastTarget = false;
			}
			powerHudEntry.Name.enableWordWrapping = false;
			powerHudEntry.Name.overflowMode = (TextOverflowModes)0;
			powerHudEntry.Timer.enableWordWrapping = false;
			powerHudEntry.Timer.overflowMode = (TextOverflowModes)0;
			powerHudEntry.Timer.fontSize = Math.Min(powerHudEntry.Timer.fontSize, 14f);
			RectTransform rectTransform = powerHudEntry.Timer.rectTransform;
			rectTransform.sizeDelta = new Vector2(Math.Max(rectTransform.sizeDelta.x, 120f), Math.Max(rectTransform.sizeDelta.y, 36f));
			rectTransform.anchoredPosition += new Vector2(0f, 8f);
			((Component)val).gameObject.SetActive(false);
			return powerHudEntry;
		}

		private static string ChildPath(Transform root, Transform child)
		{
			List<string> list = new List<string>();
			Transform val = child;
			while ((Object)(object)val != (Object)null && (Object)(object)val != (Object)(object)root)
			{
				list.Add(((Object)val).name);
				val = val.parent;
			}
			list.Reverse();
			return string.Join("/", list);
		}

		private static T MapHudComponent<T>(RectTransform clone, RectTransform original, T component) where T : Component
		{
			return ((Component)(((Object)(object)((Component)component).transform == (Object)(object)original) ? ((object)clone) : ((object)((Transform)clone).Find(ChildPath((Transform)(object)original, ((Component)component).transform))))).GetComponent<T>();
		}

		private unsafe void UpdatePowerHud(Hud hud, Player player, RectTransform template, Image icon, TMP_Text name, TMP_Text timer)
		{
			//IL_010b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0111: Expected O, but got Unknown
			//IL_02a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_02aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_02cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f7: Unknown result type (might be due to invalid IL or missing references)
			//IL_02fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0313: Unknown result type (might be due to invalid IL or missing references)
			//IL_0318: Unknown result type (might be due to invalid IL or missing references)
			//IL_0384: Unknown result type (might be due to invalid IL or missing references)
			//IL_0369: Unknown result type (might be due to invalid IL or missing references)
			//IL_034e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0399: Unknown result type (might be due to invalid IL or missing references)
			//IL_039e: Unknown result type (might be due to invalid IL or missing references)
			//IL_03a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_03b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_03bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0429: Unknown result type (might be due to invalid IL or missing references)
			//IL_042e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0432: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)hudOwner != (Object)(object)hud)
			{
				ClearPowerHud(restoreOriginal: false);
				hudOwner = hud;
				hudTemplate = template;
			}
			if (!showPowerHud.Value || (Object)(object)player == (Object)null || (Object)(object)player != (Object)(object)Player.m_localPlayer || (Object)(object)ObjectDB.instance == (Object)null)
			{
				foreach (PowerHudEntry value5 in hudEntries.Values)
				{
					if ((Object)(object)value5.Root != (Object)null)
					{
						((Component)value5.Root).gameObject.SetActive(false);
					}
				}
				return;
			}
			foreach (PowerHudEntry value6 in hudEntries.Values)
			{
				if ((Object)(object)value6.Root != (Object)null)
				{
					((Component)value6.Root).gameObject.SetActive(false);
				}
			}
			StatusEffect val = (StatusEffect)GuardianEffect.GetValue(player);
			PlayerState state = StateFor(player);
			int num = 0;
			string[] powerIds = PowerIds;
			foreach (string id in powerIds)
			{
				StatusEffect val2 = ObjectDB.instance.m_StatusEffects.Find((StatusEffect effect) => (Object)(object)effect != (Object)null && ((Object)effect).name == id);
				if ((Object)(object)val2 == (Object)null || !powers.TryGetValue(val2.NameHash(), out var value))
				{
					continue;
				}
				bool flag = (Object)(object)val != (Object)null && val.NameHash() == value.Hash;
				StatusEffect statusEffect = ((Character)player).GetSEMan().GetStatusEffect(value.Hash);
				float num2 = (((Object)(object)statusEffect != (Object)null) ? Math.Max(0f, statusEffect.GetRemaningTime()) : 0f);
				float num3 = Remaining(state, value.Hash);
				if (flag)
				{
					num3 = Math.Max(num3, (float)GuardianCooldown.GetValue(player));
				}
				bool flag2 = value.Enabled.Value && ((Humanoid)player).HaveUniqueKey(value.Id);
				if (!flag && !flag2 && num2 <= 0f && num3 <= 0f)
				{
					continue;
				}
				if (!hudEntries.TryGetValue(value.Hash, out var value2) || (Object)(object)value2.Root == (Object)null)
				{
					value2 = CreateHudEntry(template, icon, name, timer, value.Hash);
					hudEntries[value.Hash] = value2;
				}
				float value3 = hudScale.Value;
				((Transform)value2.Root).localScale = ((Transform)template).localScale * value3;
				Vector2 val3 = ((hudDirection.Value == PowerHudDirection.Up) ? Vector2.up : Vector2.right);
				value2.Root.anchoredPosition = template.anchoredPosition + new Vector2(hudOffsetX.Value, hudOffsetY.Value) + val3 * ((float)num++ * hudSpacing.Value * value3);
				value2.Icon.sprite = val2.m_icon;
				((Graphic)value2.Icon).color = (Color)((num2 > 0f) ? new Color(0.65f, 1f, 0.65f, 1f) : ((num3 > 0f) ? new Color(0.55f, 0.55f, 0.55f, 1f) : Color.white));
				KeyboardShortcut value4;
				object obj;
				if (flag2)
				{
					value4 = value.Shortcut.Value;
					if ((int)((KeyboardShortcut)(ref value4)).MainKey != 0)
					{
						value4 = value.Shortcut.Value;
						obj = ((object)(*(KeyboardShortcut*)(&value4))/*cast due to .constrained prefix*/).ToString();
						goto IL_03cb;
					}
				}
				obj = "";
				goto IL_03cb;
				IL_046f:
				object obj2;
				string text = (string)obj2;
				value2.Timer.text = text + ((num2 > 0f) ? ("\nActive " + StatusEffect.GetTimeString(num2, false, false)) : "");
				((Component)value2.Root).gameObject.SetActive(true);
				continue;
				IL_03cb:
				string text2 = (string)obj;
				value2.Name.text = Localization.instance.Localize(val2.m_name) + ((text2.Length > 0) ? (" [" + text2 + "]") : "");
				if (!(num3 > 0f))
				{
					if (flag)
					{
						goto IL_044b;
					}
					if (flag2)
					{
						value4 = value.Shortcut.Value;
						if ((int)((KeyboardShortcut)(ref value4)).MainKey != 0)
						{
							goto IL_044b;
						}
					}
					obj2 = (flag2 ? "Unbound" : "Not selected");
				}
				else
				{
					obj2 = "CD " + StatusEffect.GetTimeString(num3, false, false);
				}
				goto IL_046f;
				IL_044b:
				obj2 = Localization.instance.Localize("$hud_ready");
				goto IL_046f;
			}
			if (num > 0)
			{
				((Component)template).gameObject.SetActive(false);
			}
		}

		private void ClearPowerHud(bool restoreOriginal)
		{
			foreach (PowerHudEntry value in hudEntries.Values)
			{
				if ((Object)(object)value.Root != (Object)null)
				{
					Object.Destroy((Object)(object)((Component)value.Root).gameObject);
				}
			}
			hudEntries.Clear();
			if (restoreOriginal && (Object)(object)hudTemplate != (Object)null)
			{
				((Component)hudTemplate).gameObject.SetActive(true);
			}
			hudTemplate = null;
			hudOwner = null;
		}
	}
	internal sealed class ConfigurationManagerAttributes
	{
		public int? Order;

		public bool? IsAdvanced;

		public bool? ShowRangeAsPercent;
	}
}