Decompiled source of ValheimBubble v1.1.1

ValheimBubble.dll

Decompiled 2 weeks ago
using System;
using System.Collections;
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 UnityEngine;

[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("ValheimBubble")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.1.1.0")]
[assembly: AssemblyInformationalVersion("1.1.1+7370c90c7ac5d69be715e71aca3187b4136c95fe")]
[assembly: AssemblyProduct("Valheim Bubble")]
[assembly: AssemblyTitle("ValheimBubble")]
[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 ValheimBubble
{
	internal class BubbleCycle : MonoBehaviour
	{
		private class HandSnapshot
		{
			public ItemData Right;

			public ItemData Left;

			public bool Hidden;
		}

		private static readonly FieldRef<Humanoid, ItemData> RightItem = AccessTools.FieldRefAccess<Humanoid, ItemData>("m_rightItem");

		private static readonly FieldRef<Humanoid, ItemData> LeftItem = AccessTools.FieldRefAccess<Humanoid, ItemData>("m_leftItem");

		private static readonly FieldRef<Humanoid, ItemData> HiddenRightItem = AccessTools.FieldRefAccess<Humanoid, ItemData>("m_hiddenRightItem");

		private static readonly FieldRef<Humanoid, ItemData> HiddenLeftItem = AccessTools.FieldRefAccess<Humanoid, ItemData>("m_hiddenLeftItem");

		private const float RestoreTimeout = 30f;

		private bool _running;

		private void Update()
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			if (!_running && IsShortcutDown(ValheimBubblePlugin.Keybind.Value))
			{
				Player localPlayer = Player.m_localPlayer;
				if (!((Object)(object)localPlayer == (Object)null) && CanTakeInput(localPlayer))
				{
					((MonoBehaviour)this).StartCoroutine(Run(localPlayer));
				}
			}
		}

		private static bool IsShortcutDown(KeyboardShortcut shortcut)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			if ((int)((KeyboardShortcut)(ref shortcut)).MainKey != 0 && ZInput.GetKeyDown(((KeyboardShortcut)(ref shortcut)).MainKey, false))
			{
				return ((KeyboardShortcut)(ref shortcut)).Modifiers.All((KeyCode key) => ZInput.GetKey(key, false));
			}
			return false;
		}

		private static bool CanTakeInput(Player player)
		{
			if ((Object.op_Implicit((Object)(object)Chat.instance) && Chat.instance.HasFocus()) || Console.IsVisible() || TextInput.IsVisible() || StoreGui.IsVisible() || InventoryGui.IsVisible() || Menu.IsVisible() || Minimap.IsOpen())
			{
				return false;
			}
			if (!((Character)player).IsDead() && !((Character)player).InCutscene())
			{
				return !((Character)player).IsTeleporting();
			}
			return false;
		}

		private IEnumerator Run(Player player)
		{
			_running = true;
			try
			{
				ItemData staff = FindStaff(player);
				if (staff == null)
				{
					yield break;
				}
				HandSnapshot snapshot = Snapshot(player);
				if (RightItem.Invoke((Humanoid)(object)player) != staff)
				{
					((Humanoid)player).EquipItem(staff, true);
				}
				float timeout = ValheimBubblePlugin.CastTimeout.Value;
				float deadline = Time.time + timeout;
				bool started = false;
				while (!started && Time.time < deadline && Object.op_Implicit((Object)(object)player) && !((Character)player).IsDead())
				{
					yield return null;
					if (RightItem.Invoke((Humanoid)(object)player) == staff)
					{
						started = ((Character)player).StartAttack((Character)null, false);
					}
				}
				if (started)
				{
					deadline = Time.time + timeout;
					while (Object.op_Implicit((Object)(object)player) && !((Character)player).InAttack() && Time.time < deadline)
					{
						yield return null;
					}
					while (Object.op_Implicit((Object)(object)player) && ((Character)player).InAttack() && Time.time < deadline)
					{
						yield return null;
					}
					yield return null;
				}
				else if (Object.op_Implicit((Object)(object)player) && !((Character)player).IsDead())
				{
					Fail(player, "Could not cast the protection staff");
				}
				deadline = Time.time + 30f;
				while (Object.op_Implicit((Object)(object)player) && !((Character)player).IsDead() && !Restore(player, snapshot))
				{
					if (Time.time >= deadline)
					{
						Fail(player, "Could not re-equip your previous weapon");
						break;
					}
					yield return null;
				}
			}
			finally
			{
				_running = false;
			}
		}

		internal static ItemData BestStaff(Player player)
		{
			string prefab = ValheimBubblePlugin.StaffPrefab.Value;
			return (from item in ((Humanoid)player).GetInventory().GetAllItems()
				where (Object)(object)item.m_dropPrefab != (Object)null && ((Object)item.m_dropPrefab).name == prefab && (!item.m_shared.m_useDurability || item.m_durability > 0f)
				orderby item.m_quality descending, item.m_durability descending
				select item).FirstOrDefault();
		}

		private static ItemData FindStaff(Player player)
		{
			if (((Character)player).InAttack() || ((Character)player).InDodge() || ((Character)player).InMinorAction() || ((Character)player).IsAttached() || (((Character)player).IsSwimming() && !((Character)player).IsOnGround()))
			{
				return null;
			}
			ItemData val = BestStaff(player);
			if (val == null)
			{
				Fail(player, "Missing " + ValheimBubblePlugin.StaffPrefab.Value + " in inventory");
				return null;
			}
			float attackEitr = val.m_shared.m_attack.GetAttackEitr((Character)(object)player, val);
			if (attackEitr <= 0f)
			{
				return val;
			}
			if (((Character)player).GetMaxEitr() <= 0f)
			{
				((Character)player).Message((MessageType)2, "$hud_eitrrequired", 0, (Sprite)null, false);
				return null;
			}
			if (!((Character)player).HaveEitr(attackEitr + 0.1f))
			{
				Hud.instance.EitrBarEmptyFlash();
				ValheimBubblePlugin.Log.LogInfo((object)$"Not enough Eitr to cast ({player.GetEitr():0}/{attackEitr:0}).");
				return null;
			}
			return val;
		}

		private static HandSnapshot Snapshot(Player player)
		{
			return new HandSnapshot
			{
				Right = (RightItem.Invoke((Humanoid)(object)player) ?? HiddenRightItem.Invoke((Humanoid)(object)player)),
				Left = (LeftItem.Invoke((Humanoid)(object)player) ?? HiddenLeftItem.Invoke((Humanoid)(object)player)),
				Hidden = (RightItem.Invoke((Humanoid)(object)player) == null && LeftItem.Invoke((Humanoid)(object)player) == null && (HiddenRightItem.Invoke((Humanoid)(object)player) != null || HiddenLeftItem.Invoke((Humanoid)(object)player) != null))
			};
		}

		private static bool Restore(Player player, HandSnapshot snapshot)
		{
			if (((Character)player).InAttack() || ((Character)player).InDodge() || (((Character)player).IsSwimming() && !((Character)player).IsOnGround()))
			{
				return false;
			}
			Inventory inventory = ((Humanoid)player).GetInventory();
			ItemData val = Expected(inventory, snapshot.Right);
			ItemData val2 = Expected(inventory, snapshot.Left);
			if (!snapshot.Hidden || RightItem.Invoke((Humanoid)(object)player) != null || LeftItem.Invoke((Humanoid)(object)player) != null)
			{
				ItemData val3 = RightItem.Invoke((Humanoid)(object)player);
				if (val != null)
				{
					((Humanoid)player).EquipItem(val, true);
				}
				if (val2 != null)
				{
					((Humanoid)player).EquipItem(val2, true);
				}
				if (val3 != null && val3 != val && RightItem.Invoke((Humanoid)(object)player) == val3)
				{
					((Humanoid)player).UnequipItem(val3, true);
				}
				if (snapshot.Hidden)
				{
					((Humanoid)player).HideHandItems(false, false);
				}
			}
			if (!snapshot.Hidden)
			{
				if (RightItem.Invoke((Humanoid)(object)player) == val)
				{
					return LeftItem.Invoke((Humanoid)(object)player) == val2;
				}
				return false;
			}
			if (RightItem.Invoke((Humanoid)(object)player) == null && LeftItem.Invoke((Humanoid)(object)player) == null && HiddenRightItem.Invoke((Humanoid)(object)player) == val)
			{
				return HiddenLeftItem.Invoke((Humanoid)(object)player) == val2;
			}
			return false;
		}

		private static ItemData Expected(Inventory inventory, ItemData item)
		{
			if (item == null || !inventory.ContainsItem(item))
			{
				return null;
			}
			return item;
		}

		private static void Fail(Player player, string reason)
		{
			((Character)player).Message((MessageType)2, reason, 0, (Sprite)null, false);
			ValheimBubblePlugin.Log.LogInfo((object)reason);
		}
	}
	internal static class Chime
	{
		private const int SampleRate = 44100;

		private const float ToneSeconds = 0.35f;

		public static AudioClip Build(string name, params float[] tones)
		{
			//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d0: Expected O, but got Unknown
			int num = 15435;
			float[] data = new float[num * tones.Length];
			for (int i = 0; i < tones.Length; i++)
			{
				for (int j = 0; j < num; j++)
				{
					float num2 = (float)j / 44100f;
					float num3 = Mathf.Min(1f, (float)j / 220f) * Mathf.Exp((0f - num2) * 7f);
					float num4 = Mathf.Sin((float)Math.PI * 2f * tones[i] * num2) + 0.3f * Mathf.Sin((float)Math.PI * 4f * tones[i] * num2);
					data[i * num + j] = 0.5f * num3 * num4;
				}
			}
			int position = 0;
			return AudioClip.Create(name, data.Length, 1, 44100, false, (PCMReaderCallback)delegate(float[] buffer)
			{
				for (int k = 0; k < buffer.Length; k++)
				{
					buffer[k] = ((position < data.Length) ? data[position++] : 0f);
				}
			});
		}

		public static AudioSource AddSource(GameObject host)
		{
			AudioSource obj = host.AddComponent<AudioSource>();
			obj.playOnAwake = false;
			obj.spatialBlend = 0f;
			return obj;
		}
	}
	internal class FoodWarning : MonoBehaviour
	{
		private AudioSource _audio;

		private AudioClip _chime;

		private readonly Dictionary<Food, bool> _armed = new Dictionary<Food, bool>();

		private void Awake()
		{
			_audio = Chime.AddSource(((Component)this).gameObject);
			_chime = Chime.Build("ValheimBubbleFood", 523f, 784f);
		}

		private void Update()
		{
			Player localPlayer = Player.m_localPlayer;
			if (!ValheimBubblePlugin.FoodEnabled.Value || (Object)(object)localPlayer == (Object)null || ((Character)localPlayer).IsDead())
			{
				_armed.Clear();
				return;
			}
			List<Food> foods = localPlayer.GetFoods();
			foreach (Food item in _armed.Keys.Where((Food food) => !foods.Contains(food)).ToList())
			{
				_armed.Remove(item);
			}
			float num = (float)ValheimBubblePlugin.FoodWarnPercent.Value / 100f;
			foreach (Food item2 in foods)
			{
				SharedData shared = item2.m_item.m_shared;
				if (!(shared.m_food <= 0f) || !(shared.m_foodStamina <= 0f))
				{
					bool flag = Strength(item2) > num;
					if (!_armed.TryGetValue(item2, out var value) || flag)
					{
						_armed[item2] = flag;
					}
					else if (value)
					{
						_armed[item2] = false;
						Warn(localPlayer, item2);
					}
				}
			}
		}

		private static float Strength(Food food)
		{
			float foodBurnTime = food.m_item.m_shared.m_foodBurnTime;
			if (!(foodBurnTime > 0f))
			{
				return 1f;
			}
			return Mathf.Pow(Mathf.Clamp01(food.m_time / foodBurnTime), 0.3f);
		}

		private void Warn(Player player, Food food)
		{
			SharedData shared = food.m_item.m_shared;
			string text = ((shared.m_food > 0f && shared.m_foodStamina > 0f) ? "Health & Stamina" : ((shared.m_food > 0f) ? "Health" : "Stamina"));
			((Character)player).Message((MessageType)2, "Low " + text + " - eat!\n" + shared.m_name + " is wearing off", 0, (Sprite)null, false);
			_audio.PlayOneShot(_chime, ValheimBubblePlugin.FoodVolume.Value);
		}
	}
	internal class LowEitrWarning : MonoBehaviour
	{
		private const string Message = "Low Eitr - eat!";

		private AudioSource _audio;

		private AudioClip _chime;

		private bool _armed;

		private void Awake()
		{
			_audio = Chime.AddSource(((Component)this).gameObject);
			_chime = Chime.Build("ValheimBubbleLowEitr", 988f, 740f);
		}

		private void Update()
		{
			Player localPlayer = Player.m_localPlayer;
			if (!ValheimBubblePlugin.LowEitrEnabled.Value || (Object)(object)localPlayer == (Object)null || ((Character)localPlayer).IsDead())
			{
				_armed = false;
				return;
			}
			ItemData val = BubbleCycle.BestStaff(localPlayer);
			if (val == null)
			{
				return;
			}
			float attackEitr = val.m_shared.m_attack.GetAttackEitr((Character)(object)localPlayer, val);
			if (!(attackEitr <= 0f))
			{
				float num = attackEitr * (float)ValheimBubblePlugin.LowEitrCasts.Value;
				if (((Character)localPlayer).GetMaxEitr() >= num)
				{
					_armed = true;
				}
				else if (_armed)
				{
					_armed = false;
					((Character)localPlayer).Message((MessageType)2, "Low Eitr - eat!", 0, (Sprite)null, false);
					_audio.PlayOneShot(_chime, ValheimBubblePlugin.LowEitrVolume.Value);
				}
			}
		}
	}
	public enum ExpiryCue
	{
		None,
		Flash,
		Sound,
		Both
	}
	[BepInPlugin("com.syloreon.valheimbubble", "Valheim Bubble", "1.1.1")]
	public class ValheimBubblePlugin : BaseUnityPlugin
	{
		public const string PluginGuid = "com.syloreon.valheimbubble";

		public const string PluginName = "Valheim Bubble";

		public const string PluginVersion = "1.1.1";

		internal static ManualLogSource Log;

		internal static ConfigEntry<KeyboardShortcut> Keybind;

		internal static ConfigEntry<string> StaffPrefab;

		internal static ConfigEntry<float> CastTimeout;

		internal static ConfigEntry<bool> TimerEnabled;

		internal static ConfigEntry<float> TimerOffsetX;

		internal static ConfigEntry<float> TimerOffsetY;

		internal static ConfigEntry<int> TimerFontSize;

		internal static ConfigEntry<ExpiryCue> Cue;

		internal static ConfigEntry<float> CueVolume;

		internal static ConfigEntry<float> WarningSeconds;

		internal static ConfigEntry<bool> LowEitrEnabled;

		internal static ConfigEntry<int> LowEitrCasts;

		internal static ConfigEntry<float> LowEitrVolume;

		internal static ConfigEntry<bool> FoodEnabled;

		internal static ConfigEntry<int> FoodWarnPercent;

		internal static ConfigEntry<float> FoodVolume;

		private void Awake()
		{
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_0158: Unknown result type (might be due to invalid IL or missing references)
			//IL_0162: Expected O, but got Unknown
			//IL_0195: Unknown result type (might be due to invalid IL or missing references)
			//IL_019f: Expected O, but got Unknown
			//IL_01e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f1: Expected O, but got Unknown
			//IL_0224: Unknown result type (might be due to invalid IL or missing references)
			//IL_022e: Expected O, but got Unknown
			//IL_0277: Unknown result type (might be due to invalid IL or missing references)
			//IL_0281: Expected O, but got Unknown
			//IL_02b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_02be: Expected O, but got Unknown
			//IL_030c: Unknown result type (might be due to invalid IL or missing references)
			Log = ((BaseUnityPlugin)this).Logger;
			Keybind = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("General", "Keybind", new KeyboardShortcut((KeyCode)287, Array.Empty<KeyCode>()), "Key that swaps to the protection staff, casts the bubble and swaps back to your weapon. A key, optionally with modifiers held first, e.g. F6, F12, or A + LeftControl.");
			StaffPrefab = ((BaseUnityPlugin)this).Config.Bind<string>("Gear", "StaffPrefab", "StaffShield", "Prefab name of the staff to cast with (vanilla Staff of Protection = StaffShield).");
			CastTimeout = ((BaseUnityPlugin)this).Config.Bind<float>("Gear", "CastTimeoutSeconds", 6f, "Give up and restore gear if the cast has not finished after this many seconds.");
			TimerEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Timer", "Enabled", true, "Show a countdown while a protection bubble is active.");
			TimerOffsetX = ((BaseUnityPlugin)this).Config.Bind<float>("Timer", "OffsetX", 0f, "Horizontal offset in pixels from the status-effect bar.");
			TimerOffsetY = ((BaseUnityPlugin)this).Config.Bind<float>("Timer", "OffsetY", 70f, "Vertical offset in pixels below the status-effect bar.");
			TimerFontSize = ((BaseUnityPlugin)this).Config.Bind<int>("Timer", "FontSize", 20, "Font size of the countdown.");
			Cue = ((BaseUnityPlugin)this).Config.Bind<ExpiryCue>("Timer", "ExpiryCue", ExpiryCue.Both, "Flash: \"Bubble down!\" when the bubble expires or breaks. Sound: a chime WarningSeconds before the bubble expires.");
			CueVolume = ((BaseUnityPlugin)this).Config.Bind<float>("Timer", "CueVolume", 0.4f, new ConfigDescription("Volume of the warning chime.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
			WarningSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("Timer", "WarningSeconds", 5f, new ConfigDescription("Seconds before the bubble expires that the chime plays.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 60f), Array.Empty<object>()));
			LowEitrEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("LowEitr", "Enabled", true, "Chime and show \"Low Eitr - eat!\" when your Eitr food has worn down too far to keep casting bubbles.");
			LowEitrCasts = ((BaseUnityPlugin)this).Config.Bind<int>("LowEitr", "CastsLeft", 2, new ConfigDescription("Warn when your maximum Eitr can no longer hold this many bubble casts.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 10), Array.Empty<object>()));
			LowEitrVolume = ((BaseUnityPlugin)this).Config.Bind<float>("LowEitr", "Volume", 0.4f, new ConfigDescription("Volume of the Low Eitr chime.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
			FoodEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Food", "Enabled", true, "Chime and show \"Low Health - eat!\" or \"Low Stamina - eat!\" when a health or stamina food wears off.");
			FoodWarnPercent = ((BaseUnityPlugin)this).Config.Bind<int>("Food", "WarnAtPercent", 50, new ConfigDescription("Warn when a food's health/stamina has faded to this percent of its full value.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(5, 95), Array.Empty<object>()));
			FoodVolume = ((BaseUnityPlugin)this).Config.Bind<float>("Food", "Volume", 0.4f, new ConfigDescription("Volume of the food chime.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
			((Component)this).gameObject.AddComponent<BubbleCycle>();
			((Component)this).gameObject.AddComponent<ShieldTimerHud>();
			((Component)this).gameObject.AddComponent<LowEitrWarning>();
			((Component)this).gameObject.AddComponent<FoodWarning>();
			Log.LogInfo((object)string.Format("{0} v{1} loaded. Keybind: {2}", "Valheim Bubble", "1.1.1", Keybind.Value));
			Keybind.SettingChanged += delegate
			{
				//IL_000f: Unknown result type (might be due to invalid IL or missing references)
				Log.LogInfo((object)$"Keybind changed to {Keybind.Value}");
			};
		}
	}
	internal class ShieldTimerHud : MonoBehaviour
	{
		private const float FlashSeconds = 3f;

		private StatusEffect _shield;

		private float _remaining;

		private bool _warned;

		private AudioClip _cueClip;

		private float _flashUntil;

		private AudioSource _audio;

		private GUIStyle _style;

		private void Awake()
		{
			_audio = ((Component)this).gameObject.AddComponent<AudioSource>();
			_audio.playOnAwake = false;
			_audio.spatialBlend = 0f;
		}

		private void Update()
		{
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Expected O, but got Unknown
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null)
			{
				_shield = null;
				return;
			}
			StatusEffect val = ((IEnumerable<StatusEffect>)((Character)localPlayer).GetSEMan().GetStatusEffects()).FirstOrDefault((Func<StatusEffect, bool>)((StatusEffect se) => se is SE_Shield));
			if ((Object)(object)val != (Object)null)
			{
				if ((Object)(object)val != (Object)(object)_shield)
				{
					_cueClip = FindClip((SE_Shield)val);
				}
				_shield = val;
				_remaining = val.GetRemaningTime();
				UpdateWarning();
			}
			else if ((Object)(object)_shield != (Object)null)
			{
				_shield = null;
				_warned = false;
				ExpiryCue value = ValheimBubblePlugin.Cue.Value;
				if (value == ExpiryCue.Flash || value == ExpiryCue.Both)
				{
					_flashUntil = Time.time + 3f;
				}
			}
		}

		private void UpdateWarning()
		{
			if (_remaining > ValheimBubblePlugin.WarningSeconds.Value)
			{
				_warned = false;
			}
			else if (!_warned)
			{
				_warned = true;
				ExpiryCue value = ValheimBubblePlugin.Cue.Value;
				if ((value == ExpiryCue.Sound || value == ExpiryCue.Both) && (Object)(object)_cueClip != (Object)null)
				{
					_audio.PlayOneShot(_cueClip, ValheimBubblePlugin.CueVolume.Value);
				}
			}
		}

		private static AudioClip FindClip(SE_Shield shield)
		{
			return shield.m_breakEffects.m_effectPrefabs.Where((EffectData effect) => (Object)(object)effect.m_prefab != (Object)null).SelectMany((EffectData effect) => effect.m_prefab.GetComponentsInChildren<ZSFX>(true)).SelectMany((ZSFX sfx) => sfx.m_audioClips)
				.FirstOrDefault((Func<AudioClip, bool>)((AudioClip clip) => (Object)(object)clip != (Object)null));
		}

		private void OnGUI()
		{
			//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: 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_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_014a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0161: Unknown result type (might be due to invalid IL or missing references)
			//IL_019d: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01db: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_0115: Unknown result type (might be due to invalid IL or missing references)
			//IL_011a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0121: Unknown result type (might be due to invalid IL or missing references)
			//IL_012d: Expected O, but got Unknown
			if (!ValheimBubblePlugin.TimerEnabled.Value || (Object)(object)Hud.instance == (Object)null || Hud.instance.m_userHidden || !Hud.instance.m_rootObject.activeInHierarchy)
			{
				return;
			}
			string text;
			Color val = default(Color);
			if ((Object)(object)_shield != (Object)null)
			{
				int num = Mathf.CeilToInt(Mathf.Max(0f, _remaining));
				text = $"Bubble {num / 60}:{num % 60:00}";
				val = (Color)((_remaining <= Mathf.Max(10f, ValheimBubblePlugin.WarningSeconds.Value)) ? new Color(1f, 0.75f, 0.2f) : Color.white);
			}
			else
			{
				if (!(Time.time < _flashUntil))
				{
					return;
				}
				text = "Bubble down!";
				((Color)(ref val))..ctor(1f, 0.25f, 0.25f, Mathf.PingPong(Time.time * 4f, 1f) * 0.7f + 0.3f);
			}
			if (_style == null)
			{
				_style = new GUIStyle(GUI.skin.label)
				{
					alignment = (TextAnchor)2,
					fontStyle = (FontStyle)1
				};
			}
			_style.fontSize = ValheimBubblePlugin.TimerFontSize.Value;
			Vector2 val2 = StatusBarAnchor();
			Rect val3 = default(Rect);
			((Rect)(ref val3))..ctor(val2.x - 300f + ValheimBubblePlugin.TimerOffsetX.Value, val2.y + ValheimBubblePlugin.TimerOffsetY.Value, 300f, (float)_style.fontSize * 2f);
			GUI.color = new Color(0f, 0f, 0f, val.a * 0.8f);
			GUI.Label(new Rect(((Rect)(ref val3)).x + 2f, ((Rect)(ref val3)).y + 2f, ((Rect)(ref val3)).width, ((Rect)(ref val3)).height), text, _style);
			GUI.color = val;
			GUI.Label(val3, text, _style);
			GUI.color = Color.white;
		}

		private static Vector2 StatusBarAnchor()
		{
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: 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_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			RectTransform statusEffectListRoot = Hud.instance.m_statusEffectListRoot;
			if ((Object)(object)statusEffectListRoot == (Object)null)
			{
				return new Vector2((float)Screen.width - 20f, 20f);
			}
			Canvas componentInParent = ((Component)statusEffectListRoot).GetComponentInParent<Canvas>();
			Vector2 val = RectTransformUtility.WorldToScreenPoint(((Object)(object)componentInParent != (Object)null && (int)componentInParent.renderMode != 0) ? componentInParent.worldCamera : null, ((Transform)statusEffectListRoot).position);
			return new Vector2(val.x, (float)Screen.height - val.y);
		}
	}
}