Decompiled source of Eternal v1.1.2

plugins/Eternal.dll

Decompiled 2 weeks ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using Eternal.Commands;
using Eternal.Logic;
using HarmonyLib;
using Jotunn.Configs;
using Jotunn.Entities;
using Jotunn.Managers;
using Jotunn.Utils;
using Microsoft.CodeAnalysis;
using TMPro;
using UnityEngine;
using UnityEngine.Audio;
using UnityEngine.Rendering;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: IgnoresAccessChecksTo("assembly_utils")]
[assembly: IgnoresAccessChecksTo("assembly_valheim")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyCompany("Eternal")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.1.2.0")]
[assembly: AssemblyInformationalVersion("1.1.2")]
[assembly: AssemblyProduct("Eternal")]
[assembly: AssemblyTitle("Eternal")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.1.2.0")]
[module: UnverifiableCode]
[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 Eternal
{
	internal static class AssetLoader
	{
		private static readonly MethodInfo s_setAudioData = typeof(AudioClip).GetMethod("SetData", new Type[2]
		{
			typeof(float[]),
			typeof(int)
		});

		private static readonly Dictionary<string, Texture2D> s_textures = new Dictionary<string, Texture2D>();

		private static readonly Dictionary<string, Sprite> s_sprites = new Dictionary<string, Sprite>();

		private static readonly Dictionary<string, AudioClip> s_clips = new Dictionary<string, AudioClip>();

		private static readonly Dictionary<string, Mesh> s_meshes = new Dictionary<string, Mesh>();

		private static string s_root;

		internal static void Init(string pluginDirectory)
		{
			s_root = Path.Combine(pluginDirectory, "Assets");
		}

		internal static Texture2D Texture(string relativePath, bool linear, bool normalMap, FilterMode filter)
		{
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Expected O, but got Unknown
			//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Expected O, but got Unknown
			//IL_011e: Unknown result type (might be due to invalid IL or missing references)
			if (s_textures.TryGetValue(relativePath, out var value))
			{
				return value;
			}
			byte[] array = ReadFile(relativePath);
			if (array == null)
			{
				return null;
			}
			Texture2D val = new Texture2D(2, 2, (TextureFormat)4, true);
			if (!AssetUtils.LoadImage(val, array))
			{
				Log.Error("Assets", relativePath + " is not a valid image");
				Object.Destroy((Object)(object)val);
				return null;
			}
			Texture2D val2 = val;
			if (linear || normalMap)
			{
				Color32[] pixels = val.GetPixels32();
				if (normalMap)
				{
					PackNormals(pixels);
				}
				val2 = new Texture2D(((Texture)val).width, ((Texture)val).height, (TextureFormat)4, true, true);
				val2.SetPixels32(pixels);
				Object.Destroy((Object)(object)val);
			}
			((Object)val2).name = "Eternal_" + Path.GetFileNameWithoutExtension(relativePath);
			((Texture)val2).wrapMode = (TextureWrapMode)0;
			((Texture)val2).filterMode = filter;
			((Texture)val2).anisoLevel = 1;
			val2.Apply(true, false);
			val2.Compress(true);
			val2.Apply(false, true);
			s_textures[relativePath] = val2;
			Log.Info("Assets", string.Format("{0}: {1}x{2} {3} {4}", relativePath, ((Texture)val2).width, ((Texture)val2).height, (linear || normalMap) ? "linear" : "sRGB", val2.format));
			return val2;
		}

		internal static Sprite Sprite(string relativePath)
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Expected O, but got Unknown
			//IL_00a2: 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)
			if (s_sprites.TryGetValue(relativePath, out var value) && (Object)(object)value != (Object)null)
			{
				return value;
			}
			byte[] array = ReadFile(relativePath);
			if (array == null)
			{
				return null;
			}
			Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false);
			if (!AssetUtils.LoadImage(val, array))
			{
				Log.Error("Assets", relativePath + " is not a valid image");
				Object.Destroy((Object)(object)val);
				return null;
			}
			((Object)val).name = "Eternal_" + Path.GetFileNameWithoutExtension(relativePath);
			((Texture)val).filterMode = (FilterMode)1;
			((Texture)val).wrapMode = (TextureWrapMode)1;
			val.Apply(false, true);
			Rect val2 = default(Rect);
			((Rect)(ref val2))..ctor(0f, 0f, (float)((Texture)val).width, (float)((Texture)val).height);
			Sprite val3 = Sprite.Create(val, val2, new Vector2(0.5f, 0.5f), 100f);
			s_sprites[relativePath] = val3;
			return val3;
		}

		internal static AudioClip Clip(string relativePath)
		{
			if (s_clips.TryGetValue(relativePath, out var value))
			{
				return value;
			}
			byte[] array = ReadFile(relativePath);
			if (array == null)
			{
				return null;
			}
			try
			{
				AudioClip val = ParseWav(Path.GetFileNameWithoutExtension(relativePath), array);
				s_clips[relativePath] = val;
				Log.Info("Assets", $"{relativePath}: {val.length:0.00} s, {val.channels} ch, {val.frequency} Hz");
				return val;
			}
			catch (Exception ex)
			{
				Log.Error("Assets", relativePath + " could not be read: " + ex.Message);
				return null;
			}
		}

		internal static Mesh Mesh(string relativePath)
		{
			if (s_meshes.TryGetValue(relativePath, out var value) && (Object)(object)value != (Object)null)
			{
				return value;
			}
			string text = Path.Combine(s_root, relativePath);
			if (!File.Exists(text))
			{
				Log.Error("Assets", "missing file " + text);
				return null;
			}
			try
			{
				Mesh val = ParseObj(Path.GetFileNameWithoutExtension(relativePath), File.ReadAllLines(text));
				s_meshes[relativePath] = val;
				Log.Info("Assets", $"{relativePath}: {val.vertexCount} vertices, {val.triangles.Length / 3} triangles");
				return val;
			}
			catch (Exception ex)
			{
				Log.Error("Assets", relativePath + " could not be read: " + ex.Message);
				return null;
			}
		}

		private static Mesh ParseObj(string name, string[] lines)
		{
			//IL_0290: Unknown result type (might be due to invalid IL or missing references)
			//IL_0295: Unknown result type (might be due to invalid IL or missing references)
			//IL_029e: Expected O, but got Unknown
			//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0120: Unknown result type (might be due to invalid IL or missing references)
			//IL_0182: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a1: 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)
			//IL_01d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f0: Unknown result type (might be due to invalid IL or missing references)
			List<Vector3> list = new List<Vector3>();
			List<Vector2> list2 = new List<Vector2>();
			List<Vector3> list3 = new List<Vector3>();
			List<Vector3> list4 = new List<Vector3>();
			List<Vector2> list5 = new List<Vector2>();
			List<Vector3> list6 = new List<Vector3>();
			List<int> list7 = new List<int>();
			Dictionary<string, int> dictionary = new Dictionary<string, int>();
			List<int> list8 = new List<int>();
			char[] separator = new char[2] { ' ', '\t' };
			for (int i = 0; i < lines.Length; i++)
			{
				string[] array = lines[i].Trim().Split(separator, StringSplitOptions.RemoveEmptyEntries);
				if (array.Length == 0)
				{
					continue;
				}
				switch (array[0])
				{
				case "v":
					list.Add(new Vector3(Number(array[1]), Number(array[2]), Number(array[3])));
					break;
				case "vt":
					list2.Add(new Vector2(Number(array[1]), Number(array[2])));
					break;
				case "vn":
					list3.Add(new Vector3(Number(array[1]), Number(array[2]), Number(array[3])));
					break;
				case "f":
				{
					list8.Clear();
					for (int j = 1; j < array.Length; j++)
					{
						if (!dictionary.TryGetValue(array[j], out var value))
						{
							string[] array2 = array[j].Split(new char[1] { '/' });
							value = list4.Count;
							list4.Add(list[int.Parse(array2[0], CultureInfo.InvariantCulture) - 1]);
							list5.Add((array2.Length > 1 && array2[1].Length > 0) ? list2[int.Parse(array2[1], CultureInfo.InvariantCulture) - 1] : Vector2.zero);
							list6.Add((array2.Length > 2 && array2[2].Length > 0) ? list3[int.Parse(array2[2], CultureInfo.InvariantCulture) - 1] : Vector3.up);
							dictionary[array[j]] = value;
						}
						list8.Add(value);
					}
					for (int k = 1; k + 1 < list8.Count; k++)
					{
						list7.Add(list8[0]);
						list7.Add(list8[k]);
						list7.Add(list8[k + 1]);
					}
					break;
				}
				}
			}
			if (list7.Count == 0)
			{
				throw new InvalidDataException("no faces");
			}
			Mesh val = new Mesh
			{
				name = name
			};
			if (list4.Count > 65000)
			{
				val.indexFormat = (IndexFormat)1;
			}
			val.vertices = list4.ToArray();
			val.uv = list5.ToArray();
			val.normals = list6.ToArray();
			val.triangles = list7.ToArray();
			val.RecalculateBounds();
			val.RecalculateTangents();
			return val;
		}

		private static float Number(string text)
		{
			return float.Parse(text, NumberStyles.Float, CultureInfo.InvariantCulture);
		}

		private static byte[] ReadFile(string relativePath)
		{
			string text = Path.Combine(s_root, relativePath);
			if (!File.Exists(text))
			{
				Log.Error("Assets", "missing file " + text);
				return null;
			}
			return File.ReadAllBytes(text);
		}

		private static void PackNormals(Color32[] pixels)
		{
			//IL_0006: 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)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			for (int i = 0; i < pixels.Length; i++)
			{
				Color32 val = pixels[i];
				pixels[i] = new Color32(byte.MaxValue, val.g, val.g, val.r);
			}
		}

		private static AudioClip ParseWav(string name, byte[] data)
		{
			if (data.Length < 12 || Encoding.ASCII.GetString(data, 0, 4) != "RIFF" || Encoding.ASCII.GetString(data, 8, 4) != "WAVE")
			{
				throw new InvalidDataException("not a RIFF/WAVE file");
			}
			int num = 0;
			int num2 = 0;
			int num3 = 0;
			int num4 = -1;
			int num5 = 0;
			int num6 = 12;
			while (num6 + 8 <= data.Length)
			{
				string text = Encoding.ASCII.GetString(data, num6, 4);
				int num7 = BitConverter.ToInt32(data, num6 + 4);
				int num8 = num6 + 8;
				if (text == "fmt ")
				{
					if (BitConverter.ToInt16(data, num8) != 1)
					{
						throw new InvalidDataException("only uncompressed PCM is supported");
					}
					num = BitConverter.ToInt16(data, num8 + 2);
					num2 = BitConverter.ToInt32(data, num8 + 4);
					num3 = BitConverter.ToInt16(data, num8 + 14);
				}
				else if (text == "data")
				{
					num4 = num8;
					num5 = Math.Min(num7, data.Length - num8);
				}
				num6 = num8 + num7 + (num7 & 1);
			}
			if (num3 != 16 || num < 1 || num2 <= 0 || num4 < 0)
			{
				throw new InvalidDataException($"expected 16-bit PCM, got {num3} bit, {num} channels");
			}
			int num9 = num5 / 2;
			float[] array = new float[num9];
			for (int i = 0; i < num9; i++)
			{
				array[i] = (float)BitConverter.ToInt16(data, num4 + i * 2) / 32768f;
			}
			AudioClip val = AudioClip.Create(name, num9 / num, num, num2, false);
			s_setAudioData.Invoke(val, new object[2] { array, 0 });
			return val;
		}
	}
	internal static class BreathSystem
	{
		private const float EnterDepth = 0.35f;

		private const float ExitDepth = 0.2f;

		private const float WaterLevelSmoothing = 1.5f;

		private const float WaterLevelJump = 3f;

		private static readonly OxygenTank s_oxygen = new OxygenTank();

		private static readonly DrownTimer s_drown = new DrownTimer();

		private static readonly DiveReward s_reward = new DiveReward();

		private static Player s_player;

		private static bool s_levelValid;

		private static bool s_regenHeld;

		private static float s_heldRegenTimer;

		internal static bool InWater { get; private set; }

		internal static bool Submerged { get; private set; }

		internal static bool HelmetWorn { get; private set; }

		internal static bool OutOfAir { get; private set; }

		internal static bool Exhausted { get; private set; }

		internal static bool Still { get; private set; }

		internal static bool Sprinting { get; private set; }

		internal static float WaterLevel { get; private set; }

		internal static float DepthAboveEyes { get; private set; }

		internal static float WarningIntensity { get; private set; }

		internal static float Oxygen => s_oxygen.Value;

		internal static void BeforeStats(Player player)
		{
			if (Submerged && player == s_player)
			{
				s_heldRegenTimer = player.m_staminaRegenTimer;
				player.m_staminaRegenTimer = float.MaxValue;
				s_regenHeld = true;
			}
		}

		internal static void AfterStats(Player player, float dt)
		{
			if (player != s_player)
			{
				Reset("new local player");
				s_player = player;
			}
			RestoreRegenTimer(player, dt);
			if (!CanDive(player))
			{
				LeaveWater(player, dt);
				return;
			}
			InWater = true;
			Sample(player, dt);
			UpdateOxygen(player, dt);
			OutOfAir = Submerged && !HelmetWorn && s_oxygen.Empty;
			Exhausted = Submerged && !((Character)player).HaveStamina(0f);
			DiveController.Tick(player, dt);
			HelmetRegen(player, dt);
			Drown(player, dt);
			WarningIntensity = ((Submerged && !HelmetWorn) ? BreathMath.WarningIntensity(s_oxygen.Fraction, ConfigManager.WarningThreshold.Value) : 0f);
		}

		internal static void Reset(string reason)
		{
			if (Submerged || InWater)
			{
				Log.Debug("Breath", "reset (" + reason + ")");
			}
			InWater = false;
			Submerged = false;
			HelmetWorn = false;
			OutOfAir = false;
			Exhausted = false;
			WarningIntensity = 0f;
			DepthAboveEyes = 0f;
			s_levelValid = false;
			s_regenHeld = false;
			s_oxygen.Fill();
			s_drown.Reset();
			s_reward.Reset();
			DiveController.Reset();
			EffectsManager.Instance?.StopAll();
		}

		private static void RestoreRegenTimer(Player player, float dt)
		{
			if (s_regenHeld)
			{
				s_regenHeld = false;
				if (player.m_staminaRegenTimer > 1E+30f)
				{
					player.m_staminaRegenTimer = s_heldRegenTimer - dt;
				}
			}
		}

		private static bool CanDive(Player player)
		{
			if (((Character)player).InWater() && !((Character)player).IsDead() && !((Character)player).InIntro() && !((Character)player).IsTeleporting() && !((Character)player).IsAttached() && !((Character)player).IsDebugFlying())
			{
				return (Object)(object)((Character)player).GetStandingOnShip() == (Object)null;
			}
			return false;
		}

		private static void LeaveWater(Player player, float dt)
		{
			if (Submerged)
			{
				EndDive(player);
			}
			if (InWater)
			{
				InWater = false;
				OutOfAir = false;
				Exhausted = false;
				WarningIntensity = 0f;
				DepthAboveEyes = 0f;
				s_levelValid = false;
				s_drown.Reset();
				DiveController.Release();
			}
			s_oxygen.Step(dt, submerged: false, helmet: false, 0f, ConfigManager.RefillPerSecond.Value);
			s_reward.Tick(dt);
		}

		private static void Sample(Player player, float dt)
		{
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			float waterLevel = ((Character)player).m_waterLevel;
			if (!s_levelValid || Mathf.Abs(waterLevel - WaterLevel) > 3f)
			{
				WaterLevel = waterLevel;
				s_levelValid = true;
			}
			else
			{
				WaterLevel = BreathMath.Smooth(WaterLevel, waterLevel, dt, 1.5f);
			}
			float extraDepth = WaterLevel - ((Component)player).transform.position.y - ((Character)player).m_swimDepth;
			bool submerged = Submerged;
			bool helmetWorn = HelmetWorn;
			Submerged = BreathMath.UpdateSubmerged(submerged, extraDepth, 0.35f, 0.2f);
			HelmetWorn = EternalHelmet.IsWorn((Humanoid)(object)player);
			DepthAboveEyes = Mathf.Max(0f, WaterLevel - ((Character)player).GetEyePoint().y);
			bool num = ((Vector3)(ref ((Character)player).m_moveDir)).sqrMagnitude > 0.01f || DiveController.VerticalInput;
			Still = !num;
			Sprinting = num && ((Character)player).m_run;
			s_reward.Tick(dt);
			if (Submerged)
			{
				s_reward.Submerged(dt, DepthAboveEyes);
			}
			if (Submerged && !submerged)
			{
				Log.Info("Breath", HelmetWorn ? "head underwater, breathing through the Eternal helmet" : "head underwater, holding breath");
			}
			else if (!Submerged && submerged)
			{
				EndDive(player);
			}
			else if (Submerged && HelmetWorn != helmetWorn)
			{
				Log.Info("Helmet", HelmetWorn ? "Eternal helmet put on underwater: free breathing" : "Eternal helmet removed underwater: holding breath");
			}
		}

		private static void EndDive(Player player)
		{
			float diveSeconds = s_reward.DiveSeconds;
			float maxDepth = s_reward.MaxDepth;
			float num = s_reward.Surface(ConfigManager.Reward);
			Submerged = false;
			s_drown.Reset();
			if (num > 0f)
			{
				((Character)player).RaiseSkill((SkillType)103, num);
			}
			Log.Info("Breath", $"surfaced after {diveSeconds:0.0} s, deepest {maxDepth:0.0} m" + ((num > 0f) ? $", Swim experience x{num:0.0}" : string.Empty));
		}

		private static void UpdateOxygen(Player player, float dt)
		{
			float drainPerSecond = 0f;
			if (Submerged && !HelmetWorn)
			{
				SwimEffort effort = ((!Still) ? ((!Sprinting) ? SwimEffort.Normal : SwimEffort.Sprint) : SwimEffort.Idle);
				drainPerSecond = BreathMath.DrainPerSecond(ConfigManager.Breath, DepthAboveEyes, effort, ((Character)player).GetSkillFactor((SkillType)103));
			}
			s_oxygen.Step(dt, Submerged, HelmetWorn, drainPerSecond, ConfigManager.RefillPerSecond.Value);
		}

		private static void HelmetRegen(Player player, float dt)
		{
			if (!Submerged || !HelmetWorn || !Still || player.m_staminaRegenTimer > 0f)
			{
				return;
			}
			float maxStamina = ((Character)player).GetMaxStamina();
			if (!(player.m_stamina >= maxStamina))
			{
				float statusEffectMultiplier = 1f;
				((Character)player).m_seman.ModifyStaminaRegen(ref statusEffectMultiplier);
				float num = BreathMath.HelmetRegenAmount(player.m_staminaRegen, player.m_stamina, maxStamina, player.m_staminaRegenTimeMultiplier, statusEffectMultiplier, ConfigManager.StillRegenFactor.Value, Game.m_staminaRegenRate, dt);
				if (num > 0f)
				{
					((Character)player).AddStamina(num);
				}
			}
		}

		private static void Drown(Player player, float dt)
		{
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Expected O, but got Unknown
			//IL_008b: 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)
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
			bool flag = s_drown.OutOfAirSeconds > 0f;
			float num = s_drown.Step(dt, OutOfAir, ConfigManager.GraceSeconds.Value, ConfigManager.TickSeconds.Value, ConfigManager.DamagePerSecond.Value);
			if (OutOfAir && !flag)
			{
				Log.Info("Breath", $"out of oxygen: floating up, drowning in {ConfigManager.GraceSeconds.Value:0.#} s");
			}
			if (!(num <= 0f))
			{
				HitData val = new HitData();
				val.m_damage.m_damage = num;
				val.m_point = ((Character)player).GetCenterPoint();
				val.m_dir = Vector3.up;
				val.m_hitType = (HitType)4;
				((Character)player).Damage(val);
				Vector3 position = ((Component)player).transform.position;
				position.y = WaterLevel;
				EffectList drownEffects = player.m_drownEffects;
				if (drownEffects != null)
				{
					drownEffects.Create(position, ((Component)player).transform.rotation, (Transform)null, 1f, -1, default(ZDOID));
				}
				if (Log.Verbose)
				{
					Log.Debug("Breath", $"drowning tick: {num:0.#} damage");
				}
			}
		}
	}
	internal static class ConfigManager
	{
		private const string OxygenSection = "1 - Oxygen";

		private const string DrowningSection = "2 - Drowning";

		private const string DivingSection = "3 - Diving";

		private const string SkillSection = "4 - Swim skill";

		private const string HelmetSection = "5 - Helmet";

		private const string RecipeSection = "6 - Helmet recipe";

		private const string EffectsSection = "7 - Effects";

		private const string ControlsSection = "8 - Controls";

		private const string DebugSection = "9 - Debug";

		internal static ConfigEntry<float> DrainPerSecond;

		internal static ConfigEntry<float> RefillPerSecond;

		internal static ConfigEntry<float> DepthStepMeters;

		internal static ConfigEntry<float> DepthCostPerStep;

		internal static ConfigEntry<float> DepthMultiplierCap;

		internal static ConfigEntry<float> SprintMultiplier;

		internal static ConfigEntry<float> IdleMultiplier;

		internal static ConfigEntry<float> SwimSkillMaxReduction;

		internal static ConfigEntry<float> WarningThreshold;

		internal static ConfigEntry<float> GraceSeconds;

		internal static ConfigEntry<float> DamagePerSecond;

		internal static ConfigEntry<float> TickSeconds;

		internal static ConfigEntry<float> DescendSpeed;

		internal static ConfigEntry<float> AscendSpeed;

		internal static ConfigEntry<float> OutOfAirAscentSpeed;

		internal static ConfigEntry<float> MaxDiveDepth;

		internal static ConfigEntry<float> FastSwimSpeedMultiplier;

		internal static ConfigEntry<float> SkillPerSecond;

		internal static ConfigEntry<float> MinDiveSeconds;

		internal static ConfigEntry<float> MinDiveDepth;

		internal static ConfigEntry<float> MaxSkillPerDive;

		internal static ConfigEntry<float> RewardCooldownSeconds;

		internal static ConfigEntry<float> SwimEffortMultiplier;

		internal static ConfigEntry<float> StillRegenFactor;

		internal static ConfigEntry<float> Armor;

		internal static ConfigEntry<float> ArmorPerLevel;

		internal static ConfigEntry<int> MaxQuality;

		internal static ConfigEntry<float> Weight;

		internal static ConfigEntry<float> Durability;

		internal static ConfigEntry<float> DurabilityPerLevel;

		internal static ConfigEntry<float> MovementSpeedPenalty;

		internal static ConfigEntry<int> MinStationLevel;

		internal static ConfigEntry<int> IronAmount;

		internal static ConfigEntry<int> IronPerLevel;

		internal static ConfigEntry<int> BronzeAmount;

		internal static ConfigEntry<int> BronzePerLevel;

		internal static ConfigEntry<int> ChainAmount;

		internal static ConfigEntry<int> ChainPerLevel;

		internal static ConfigEntry<int> ObsidianAmount;

		internal static ConfigEntry<int> ObsidianPerLevel;

		internal static ConfigEntry<bool> OxygenBar;

		internal static ConfigEntry<bool> Vignette;

		internal static ConfigEntry<float> VignetteMaxAlpha;

		internal static ConfigEntry<bool> WarningBubbles;

		internal static ConfigEntry<bool> Heartbeat;

		internal static ConfigEntry<float> HeartbeatVolume;

		internal static ConfigEntry<bool> RuneGlow;

		internal static ConfigEntry<float> RuneGlowIdle;

		internal static ConfigEntry<float> RuneGlowSubmerged;

		internal static ConfigEntry<bool> ExhaleBubbles;

		internal static ConfigEntry<bool> RegulatedBreath;

		internal static ConfigEntry<float> BreathVolume;

		internal static ConfigEntry<bool> UnderwaterCamera;

		internal static ConfigEntry<bool> UnderwaterFog;

		internal static ConfigEntry<bool> UnderwaterTint;

		internal static ConfigEntry<string> DescendButton;

		internal static ConfigEntry<string> AscendButton;

		internal static ConfigEntry<bool> VerboseLogging;

		private static bool s_itemSettingsDirty;

		internal static BreathSettings Breath => new BreathSettings(DrainPerSecond.Value, DepthStepMeters.Value, DepthCostPerStep.Value, DepthMultiplierCap.Value, SprintMultiplier.Value, IdleMultiplier.Value, SwimSkillMaxReduction.Value);

		internal static DiveSettings Dive => new DiveSettings(DescendSpeed.Value, AscendSpeed.Value, OutOfAirAscentSpeed.Value, MaxDiveDepth.Value);

		internal static DiveRewardSettings Reward => new DiveRewardSettings(SkillPerSecond.Value, MinDiveSeconds.Value, MinDiveDepth.Value, MaxSkillPerDive.Value, RewardCooldownSeconds.Value);

		internal static void Bind(ConfigFile config)
		{
			DrainPerSecond = Admin(config, "1 - Oxygen", "DrainPerSecond", 2f, 0f, 20f, "Oxygen per second (out of 100) spent while your head is underwater without the Eternal helmet, moving or not. Depth, effort and Swim skill scale it. The default gives about 50 s swimming and 100 s standing still.");
			RefillPerSecond = Admin(config, "1 - Oxygen", "RefillPerSecond", 25f, 1f, 100f, "Oxygen per second recovered once your head is out of the water.");
			DepthStepMeters = Admin(config, "1 - Oxygen", "DepthStepMeters", 10f, 1f, 100f, "Every this many meters of water above your eyes raise the oxygen drain by DepthCostPerStep.");
			DepthCostPerStep = Admin(config, "1 - Oxygen", "DepthCostPerStep", 0.15f, 0f, 2f, "Extra drain per depth step. Multiplier = 1 + (depth / DepthStepMeters) * DepthCostPerStep.");
			DepthMultiplierCap = Admin(config, "1 - Oxygen", "DepthMultiplierCap", 2f, 1f, 10f, "Highest value the depth multiplier can reach.");
			SprintMultiplier = Admin(config, "1 - Oxygen", "SprintMultiplier", 1.5f, 1f, 5f, "Oxygen drain multiplier while fast swimming underwater (holding Run).");
			IdleMultiplier = Admin(config, "1 - Oxygen", "IdleMultiplier", 0.5f, 0f, 1f, "Oxygen drain multiplier while not moving underwater.");
			SwimSkillMaxReduction = Admin(config, "1 - Oxygen", "SwimSkillMaxReduction", 0.4f, 0f, 1f, "Oxygen drain reduction at Swim skill 100, scaling linearly from skill 0. 0.4 means 40 % slower.");
			WarningThreshold = Admin(config, "1 - Oxygen", "WarningThreshold", 0.25f, 0f, 1f, "Oxygen fraction under which the low-air warning starts (vignette, bubbles, heartbeat).");
			GraceSeconds = Admin(config, "2 - Drowning", "GraceSeconds", 3f, 0f, 30f, "Seconds without oxygen underwater before drowning damage starts.");
			DamagePerSecond = Admin(config, "2 - Drowning", "DamagePerSecond", 2f, 0f, 100f, "Drowning damage per second once the grace is over. Like vanilla drowning, armor does not reduce it.");
			TickSeconds = Admin(config, "2 - Drowning", "TickSeconds", 1f, 0.25f, 5f, "Seconds between drowning damage ticks. Each tick deals DamagePerSecond * TickSeconds.");
			DescendSpeed = Admin(config, "3 - Diving", "DescendSpeed", 2f, 0.5f, 10f, "Meters per second you sink while holding the descend button.");
			AscendSpeed = Admin(config, "3 - Diving", "AscendSpeed", 3f, 0.5f, 10f, "Meters per second you rise while holding the ascend button.");
			OutOfAirAscentSpeed = Admin(config, "3 - Diving", "OutOfAirAscentSpeed", 1.5f, 0.1f, 10f, "Meters per second you float up when your oxygen runs out. Descending is blocked meanwhile, and also while your stamina is empty (then you keep your depth).");
			MaxDiveDepth = Admin(config, "3 - Diving", "MaxDiveDepth", 45f, 1f, 200f, "Deepest dive below the floating line, in meters. The ocean water volume ends about 50 m below the surface, so dives are also kept 1 m above its bottom whatever this says.");
			FastSwimSpeedMultiplier = Admin(config, "3 - Diving", "FastSwimSpeedMultiplier", 1.4f, 1f, 3f, "Swim speed multiplier while holding Run with your head underwater. Valheim has no swim sprint of its own.");
			SkillPerSecond = Admin(config, "4 - Swim skill", "SkillPerSecond", 0.3f, 0f, 5f, "Swim skill experience per second of a completed dive. Vanilla gives 1 per second of moving while swimming.");
			MinDiveSeconds = Admin(config, "4 - Swim skill", "MinDiveSeconds", 4f, 0f, 60f, "A dive must last at least this long to give Swim experience.");
			MinDiveDepth = Admin(config, "4 - Swim skill", "MinDiveDepth", 1.5f, 0f, 50f, "A dive must reach at least this many meters of water above your eyes to give Swim experience.");
			MaxSkillPerDive = Admin(config, "4 - Swim skill", "MaxSkillPerDive", 6f, 0f, 50f, "Most Swim experience a single dive can give.");
			RewardCooldownSeconds = Admin(config, "4 - Swim skill", "RewardCooldownSeconds", 30f, 0f, 600f, "Seconds after a rewarded dive before the next dive can give experience.");
			SwimEffortMultiplier = Admin(config, "5 - Helmet", "SwimEffortMultiplier", 0.8f, 0f, 1f, "Swimming stamina cost multiplier while wearing the helmet. Uses the game's own swim stamina modifier, so it shows in the tooltip and applies at the surface too.");
			StillRegenFactor = Admin(config, "5 - Helmet", "StillRegenFactor", 0.5f, 0f, 1f, "With the helmet on and not moving underwater, stamina regenerates at this fraction of the normal rate.");
			Armor = Admin(config, "5 - Helmet", "Armor", 24f, 0f, 200f, "Base armor of the Eternal helmet.");
			ArmorPerLevel = Admin(config, "5 - Helmet", "ArmorPerLevel", 3f, 0f, 50f, "Armor added per quality level.");
			MaxQuality = Admin(config, "5 - Helmet", "MaxQuality", 4, 1, 10, "Highest quality level. Crafting gives level 1; each upgrade needs one more forge level than the last.");
			Weight = Admin(config, "5 - Helmet", "Weight", 5f, 0f, 50f, "Weight of the helmet.");
			Durability = Admin(config, "5 - Helmet", "Durability", 300f, 1f, 10000f, "Base durability. For reference, the vanilla iron helmet has 1000.");
			DurabilityPerLevel = Admin(config, "5 - Helmet", "DurabilityPerLevel", 200f, 0f, 5000f, "Durability added per quality level (vanilla iron helmet: 200).");
			MovementSpeedPenalty = Admin(config, "5 - Helmet", "MovementSpeedPenalty", 0f, 0f, 0.5f, "Movement speed penalty while worn. 0.05 means -5 %.");
			MinStationLevel = Admin(config, "6 - Helmet recipe", "MinStationLevel", 3, 1, 7, "Forge level needed to craft the helmet. Upgrading to quality N needs MinStationLevel + N - 1.");
			IronAmount = Admin(config, "6 - Helmet recipe", "IronAmount", 20, 0, 999, "Iron to craft.");
			IronPerLevel = Admin(config, "6 - Helmet recipe", "IronPerLevel", 10, 0, 999, "Iron per upgrade level. The game scales it: quality 2, 3 and 4 cost 1x, 2x and 4x this amount.");
			BronzeAmount = Admin(config, "6 - Helmet recipe", "BronzeAmount", 6, 0, 999, "Bronze to craft.");
			BronzePerLevel = Admin(config, "6 - Helmet recipe", "BronzePerLevel", 0, 0, 999, "Bronze per upgrade level.");
			ChainAmount = Admin(config, "6 - Helmet recipe", "ChainAmount", 2, 0, 999, "Chain to craft.");
			ChainPerLevel = Admin(config, "6 - Helmet recipe", "ChainPerLevel", 2, 0, 999, "Chain per upgrade level, scaled by the game like IronPerLevel.");
			ObsidianAmount = Admin(config, "6 - Helmet recipe", "ObsidianAmount", 4, 0, 999, "Obsidian to craft.");
			ObsidianPerLevel = Admin(config, "6 - Helmet recipe", "ObsidianPerLevel", 0, 0, 999, "Obsidian per upgrade level.");
			OxygenBar = config.Bind<bool>("7 - Effects", "OxygenBar", true, "Oxygen bar above the stamina bar, shown while your head is underwater or your oxygen is not full.");
			Vignette = config.Bind<bool>("7 - Effects", "Vignette", true, "Blue vignette on screen when you are running out of air.");
			VignetteMaxAlpha = Local(config, "7 - Effects", "VignetteMaxAlpha", 0.55f, 0f, 1f, "Opacity of the vignette at zero oxygen.");
			WarningBubbles = config.Bind<bool>("7 - Effects", "WarningBubbles", true, "Bubbles escape from your mouth when you are running out of air.");
			Heartbeat = config.Bind<bool>("7 - Effects", "Heartbeat", true, "Muffled heartbeat when you are running out of air.");
			HeartbeatVolume = Local(config, "7 - Effects", "HeartbeatVolume", 0.7f, 0f, 1f, "Heartbeat volume at zero oxygen.");
			RuneGlow = config.Bind<bool>("7 - Effects", "RuneGlow", true, "Helmet runes glow aqua, brighter underwater.");
			RuneGlowIdle = Local(config, "7 - Effects", "RuneGlowIdle", 1.2f, 0f, 10f, "Rune glow intensity out of the water.");
			RuneGlowSubmerged = Local(config, "7 - Effects", "RuneGlowSubmerged", 4f, 0f, 20f, "Rune glow intensity underwater.");
			ExhaleBubbles = config.Bind<bool>("7 - Effects", "ExhaleBubbles", true, "With the helmet on, a puff of bubbles on every exhale.");
			RegulatedBreath = config.Bind<bool>("7 - Effects", "RegulatedBreath", true, "With the helmet on, a soft regulated breathing sound underwater.");
			BreathVolume = Local(config, "7 - Effects", "BreathVolume", 0.25f, 0f, 1f, "Volume of the regulated breathing and exhale bubbles.");
			UnderwaterCamera = config.Bind<bool>("7 - Effects", "UnderwaterCamera", true, "Let the camera follow you below the surface while your head is underwater. Off keeps the vanilla camera above the water.");
			UnderwaterFog = config.Bind<bool>("7 - Effects", "UnderwaterFog", true, "Thicker blue fog while the camera is underwater.");
			UnderwaterTint = config.Bind<bool>("7 - Effects", "UnderwaterTint", true, "Blue-green screen tint while the camera is underwater.");
			DescendButton = config.Bind<string>("8 - Controls", "DescendButton", "Crouch", "Valheim input name held to dive deeper. The gamepad uses the matching Joy button (JoyCrouch). Follows your in-game key binding.");
			AscendButton = config.Bind<string>("8 - Controls", "AscendButton", "Jump", "Valheim input name held to rise. The gamepad uses the matching Joy button (JoyJump).");
			VerboseLogging = config.Bind<bool>("9 - Debug", "VerboseLogging", false, "Log extra detail, such as each drowning tick.");
			config.SettingChanged += OnSettingChanged;
			SynchronizationManager.OnConfigurationSynchronized += delegate(object sender, ConfigurationSynchronizationEventArgs args)
			{
				s_itemSettingsDirty = true;
				Log.Info("Config", args.InitialSynchronization ? "received the server's settings" : "server settings updated");
			};
		}

		internal static bool ConsumeItemSettingsDirty()
		{
			bool result = s_itemSettingsDirty;
			s_itemSettingsDirty = false;
			return result;
		}

		private static void OnSettingChanged(object sender, SettingChangedEventArgs args)
		{
			string section = args.ChangedSetting.Definition.Section;
			if (section == "5 - Helmet" || section == "6 - Helmet recipe")
			{
				s_itemSettingsDirty = true;
			}
		}

		private static ConfigEntry<float> Admin(ConfigFile config, string section, string key, float value, float min, float max, string description)
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Expected O, but got Unknown
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Expected O, but got Unknown
			return config.Bind<float>(section, key, value, new ConfigDescription(description, (AcceptableValueBase)(object)new AcceptableValueRange<float>(min, max), new object[1] { (object)new ConfigurationManagerAttributes
			{
				IsAdminOnly = true
			} }));
		}

		private static ConfigEntry<int> Admin(ConfigFile config, string section, string key, int value, int min, int max, string description)
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Expected O, but got Unknown
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Expected O, but got Unknown
			return config.Bind<int>(section, key, value, new ConfigDescription(description, (AcceptableValueBase)(object)new AcceptableValueRange<int>(min, max), new object[1] { (object)new ConfigurationManagerAttributes
			{
				IsAdminOnly = true
			} }));
		}

		private static ConfigEntry<float> Local(ConfigFile config, string section, string key, float value, float min, float max, string description)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Expected O, but got Unknown
			return config.Bind<float>(section, key, value, new ConfigDescription(description, (AcceptableValueBase)(object)new AcceptableValueRange<float>(min, max), Array.Empty<object>()));
		}
	}
	internal static class DiveController
	{
		private const float VerticalGain = 4f;

		private const float VerticalMaxSpeed = 6f;

		private const float TriggerMargin = 1f;

		private const float TriggerRefreshSeconds = 0.5f;

		private const float UnderwaterCameraDistance = -10000f;

		private static float s_restoreSwimSpeed = float.NaN;

		private static WaterVolume s_volume;

		private static float s_triggerBottom = float.NegativeInfinity;

		private static float s_triggerRefresh;

		private static GameCamera s_camera;

		private static float s_cameraDefault;

		private static string s_descendSource;

		private static string s_descendJoy;

		private static string s_ascendSource;

		private static string s_ascendJoy;

		internal static float DiveDepth { get; private set; }

		internal static bool VerticalInput { get; private set; }

		internal static void Tick(Player player, float dt)
		{
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			if (!((Character)player).IsSwimming())
			{
				Release();
				return;
			}
			bool num = ((Character)player).TakeInput();
			bool flag = num && Held(ConfigManager.DescendButton.Value, ref s_descendSource, ref s_descendJoy);
			bool flag2 = num && Held(ConfigManager.AscendButton.Value, ref s_ascendSource, ref s_ascendJoy);
			VerticalInput = flag || flag2;
			RefreshTrigger(player, dt);
			float swimDepth = ((Character)player).m_swimDepth;
			float actualDiveDepth = BreathSystem.WaterLevel - ((Component)player).transform.position.y - swimDepth;
			float maxDepth = DiveMath.MaxDepth(ConfigManager.MaxDiveDepth.Value, BreathSystem.WaterLevel, s_triggerBottom, swimDepth, 1f);
			float num2 = DiveMath.NextDiveDepth(DiveDepth, flag, flag2, BreathSystem.OutOfAir, BreathSystem.Exhausted, dt, ConfigManager.Dive, maxDepth);
			if (flag)
			{
				num2 = DiveMath.ClampToFloor(num2, actualDiveDepth, ((Character)player).IsOnGround());
			}
			else if (!BreathSystem.Submerged)
			{
				num2 = Mathf.MoveTowards(num2, 0f, ConfigManager.AscendSpeed.Value * dt);
			}
			if (DiveDepth <= 0f && num2 > 0f)
			{
				Log.Debug("Dive", "diving");
			}
			DiveDepth = num2;
			ApplyCamera(BreathSystem.Submerged && ConfigManager.UnderwaterCamera.Value);
		}

		internal static void BeforeSwimming(Player player)
		{
			s_restoreSwimSpeed = float.NaN;
			if (BreathSystem.Submerged && BreathSystem.Sprinting)
			{
				s_restoreSwimSpeed = ((Character)player).m_swimSpeed;
				((Character)player).m_swimSpeed = s_restoreSwimSpeed * ConfigManager.FastSwimSpeedMultiplier.Value;
			}
		}

		internal static void RestoreSwimSpeed(Player player)
		{
			if (!float.IsNaN(s_restoreSwimSpeed))
			{
				((Character)player).m_swimSpeed = s_restoreSwimSpeed;
				s_restoreSwimSpeed = float.NaN;
			}
		}

		internal static void AfterSwimming(Player player)
		{
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			if (!(DiveDepth <= 0f))
			{
				float targetY = BreathSystem.WaterLevel - (((Character)player).m_swimDepth + DiveDepth);
				Rigidbody body = ((Character)player).m_body;
				Vector3 linearVelocity = body.linearVelocity;
				linearVelocity.y = DiveMath.VerticalVelocity(((Component)player).transform.position.y, targetY, 4f, 6f);
				body.linearVelocity = linearVelocity;
				body.useGravity = false;
			}
		}

		internal static void Release()
		{
			DiveDepth = 0f;
			VerticalInput = false;
			ApplyCamera(underwater: false);
		}

		internal static void Reset()
		{
			Release();
			s_volume = null;
			s_triggerBottom = float.NegativeInfinity;
			s_triggerRefresh = 0f;
		}

		private static bool Held(string button, ref string cachedSource, ref string cachedJoy)
		{
			if ((object)button != cachedSource)
			{
				cachedSource = button;
				cachedJoy = "Joy" + button;
			}
			if (!ZInput.GetButton(button))
			{
				return ZInput.GetButton(cachedJoy);
			}
			return true;
		}

		private static void RefreshTrigger(Player player, float dt)
		{
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			s_triggerRefresh -= dt;
			if (s_triggerRefresh > 0f)
			{
				return;
			}
			s_triggerRefresh = 0.5f;
			WaterVolume obj = s_volume;
			Floating.GetWaterLevel(((Component)player).transform.position, ref s_volume);
			if (obj != s_volume)
			{
				Collider val = (((Object)(object)s_volume != (Object)null) ? s_volume.m_collider : null);
				float num;
				if (!((Object)(object)val != (Object)null))
				{
					num = float.NegativeInfinity;
				}
				else
				{
					Bounds bounds = val.bounds;
					num = ((Bounds)(ref bounds)).min.y;
				}
				s_triggerBottom = num;
				if ((Object)(object)s_volume != (Object)null)
				{
					Log.Debug("Dive", $"water volume bottom at y={s_triggerBottom:0.0}");
				}
			}
		}

		private static void ApplyCamera(bool underwater)
		{
			GameCamera instance = GameCamera.instance;
			if (!((Object)(object)instance == (Object)null))
			{
				if (instance != s_camera)
				{
					s_camera = instance;
					s_cameraDefault = instance.m_minWaterDistance;
				}
				float num = (underwater ? (-10000f) : s_cameraDefault);
				if (instance.m_minWaterDistance != num)
				{
					instance.m_minWaterDistance = num;
				}
			}
		}
	}
	internal sealed class EffectsManager : MonoBehaviour
	{
		private const float BreathCycleSeconds = 6f;

		private const float ExhaleSeconds = 2.9f;

		private const float BubbleRiseSpeed = 0.8f;

		private const float ColourDepth = 30f;

		private const float Silent = 0.002f;

		private static readonly Color VignetteColour = new Color(0.08f, 0.38f, 0.58f);

		private static readonly Color TintShallow = new Color(0.1f, 0.34f, 0.4f);

		private static readonly Color TintDeep = new Color(0.02f, 0.07f, 0.14f);

		private static readonly Color FogShallow = new Color(0.1f, 0.3f, 0.36f);

		private static readonly Color FogDeep = new Color(0.01f, 0.04f, 0.08f);

		private static Material s_bubbleMaterial;

		private Hud _hud;

		private GameObject _overlay;

		private RawImage _tint;

		private RawImage _vignette;

		private float _warning;

		private float _tintAlpha;

		private GameObject _audioRoot;

		private AudioSource _heartbeat;

		private AudioSource _breath;

		private AudioSource _oneShot;

		private AudioClip _exhaleClip;

		private Player _audioPlayer;

		private bool _audioPaused;

		private float _lastCycleTime;

		private float _cycleTimer;

		private Player _bubblePlayer;

		private ParticleSystem _bubbles;

		internal static EffectsManager Instance { get; private set; }

		internal static bool CameraUnderwater { get; private set; }

		internal static float CameraDepth { get; private set; }

		internal static void ApplyUnderwaterFog()
		{
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)Instance == (Object)null) && CameraUnderwater && ConfigManager.UnderwaterFog.Value)
			{
				float num = Mathf.Clamp01(CameraDepth / 30f);
				RenderSettings.fogColor = Color.Lerp(FogShallow, FogDeep, num);
				RenderSettings.fogDensity = Mathf.Lerp(0.03f, 0.08f, num);
			}
		}

		internal void StopAll()
		{
			//IL_0042: 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_009b: 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_00a8: Unknown result type (might be due to invalid IL or missing references)
			_warning = 0f;
			_tintAlpha = 0f;
			_cycleTimer = 0f;
			_lastCycleTime = 0f;
			CameraUnderwater = false;
			CameraDepth = 0f;
			SetImage(_vignette, VignetteColour, 0f);
			SetImage(_tint, TintShallow, 0f);
			StopSource(_heartbeat);
			StopSource(_breath);
			StopSource(_oneShot);
			if ((Object)(object)_bubbles != (Object)null)
			{
				EmissionModule emission = _bubbles.emission;
				((EmissionModule)(ref emission)).rateOverTime = MinMaxCurve.op_Implicit(0f);
				_bubbles.Clear();
			}
		}

		private void Awake()
		{
			Instance = this;
		}

		private void OnDestroy()
		{
			if ((Object)(object)_overlay != (Object)null)
			{
				Object.Destroy((Object)(object)_overlay);
			}
			if ((Object)(object)_audioRoot != (Object)null)
			{
				Object.Destroy((Object)(object)_audioRoot);
			}
			if ((Object)(object)_bubbles != (Object)null)
			{
				Object.Destroy((Object)(object)((Component)_bubbles).gameObject);
			}
			if (Instance == this)
			{
				Instance = null;
			}
		}

		private void Update()
		{
			Player localPlayer = Player.m_localPlayer;
			float deltaTime = Time.deltaTime;
			UpdateCamera();
			_warning = Mathf.MoveTowards(_warning, ((Object)(object)localPlayer != (Object)null) ? BreathSystem.WarningIntensity : 0f, deltaTime * 1.5f);
			UpdateOverlay(deltaTime);
			UpdateAudio(localPlayer, deltaTime);
			UpdateBubbles(localPlayer);
		}

		private static void UpdateCamera()
		{
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: 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)
			CameraUnderwater = false;
			CameraDepth = 0f;
			if (BreathSystem.Submerged && !((Object)(object)GameCamera.instance == (Object)null))
			{
				Vector3 position = ((Component)GameCamera.instance).transform.position;
				float liquidLevel = Floating.GetLiquidLevel(position, 1f, (LiquidType)0);
				if (liquidLevel > position.y)
				{
					CameraUnderwater = true;
					CameraDepth = liquidLevel - position.y;
				}
			}
		}

		private void UpdateOverlay(float dt)
		{
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
			EnsureOverlay();
			if (!((Object)(object)_overlay == (Object)null))
			{
				float num = 1f + 0.4f * _warning;
				float num2 = 0.85f + 0.15f * Mathf.Sin(Time.time * (float)Math.PI * 2f * num);
				float alpha = (ConfigManager.Vignette.Value ? (_warning * ConfigManager.VignetteMaxAlpha.Value * num2) : 0f);
				SetImage(_vignette, VignetteColour, alpha);
				float num3 = Mathf.Clamp01(CameraDepth / 30f);
				float num4 = ((ConfigManager.UnderwaterTint.Value && CameraUnderwater) ? Mathf.Lerp(0.22f, 0.5f, num3) : 0f);
				_tintAlpha = Mathf.MoveTowards(_tintAlpha, num4, dt * 2f);
				SetImage(_tint, Color.Lerp(TintShallow, TintDeep, num3), _tintAlpha);
			}
		}

		private void EnsureOverlay()
		{
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Expected O, but got Unknown
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Expected O, but got Unknown
			Hud instance = Hud.instance;
			if ((Object)(object)instance == (Object)null)
			{
				_hud = null;
			}
			else if (instance != _hud || !((Object)(object)_overlay != (Object)null))
			{
				_hud = instance;
				_overlay = new GameObject("EternalOverlay", new Type[1] { typeof(RectTransform) });
				RectTransform val = (RectTransform)_overlay.transform;
				((Transform)val).SetParent(((Component)instance).transform, false);
				Stretch(val);
				((Transform)val).SetAsFirstSibling();
				_tint = CreateImage("UnderwaterTint", val, (Texture)(object)Texture2D.whiteTexture);
				Sprite val2 = AssetLoader.Sprite("Effects/vignette.png");
				_vignette = CreateImage("LowAirVignette", val, (Texture)(object)(((Object)(object)val2 != (Object)null) ? val2.texture : null));
			}
		}

		private static RawImage CreateImage(string name, RectTransform parent, Texture texture)
		{
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Expected O, but got Unknown
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject(name, new Type[2]
			{
				typeof(RectTransform),
				typeof(RawImage)
			});
			RectTransform val2 = (RectTransform)val.transform;
			((Transform)val2).SetParent((Transform)(object)parent, false);
			Stretch(val2);
			RawImage component = val.GetComponent<RawImage>();
			component.texture = texture;
			((Graphic)component).raycastTarget = false;
			((Graphic)component).color = Color.clear;
			((Behaviour)component).enabled = false;
			return component;
		}

		private static void Stretch(RectTransform rect)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			rect.anchorMin = Vector2.zero;
			rect.anchorMax = Vector2.one;
			rect.offsetMin = Vector2.zero;
			rect.offsetMax = Vector2.zero;
		}

		private static void SetImage(RawImage image, Color colour, float alpha)
		{
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: 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_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: 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_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: 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_006f: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)image == (Object)null || (Object)(object)image.texture == (Object)null)
			{
				return;
			}
			bool flag = alpha > 0.002f;
			if (((Behaviour)image).enabled != flag)
			{
				((Behaviour)image).enabled = flag;
			}
			if (flag)
			{
				Color color = ((Graphic)image).color;
				if (Mathf.Abs(color.a - alpha) > 0.003f || Mathf.Abs(color.r - colour.r) > 0.003f || Mathf.Abs(color.b - colour.b) > 0.003f)
				{
					((Graphic)image).color = new Color(colour.r, colour.g, colour.b, alpha);
				}
			}
		}

		private void UpdateAudio(Player player, float dt)
		{
			bool flag = (Object)(object)ZNet.instance != (Object)null && Game.IsPaused();
			if (flag != _audioPaused)
			{
				_audioPaused = flag;
				SetPaused(_heartbeat, flag);
				SetPaused(_breath, flag);
			}
			if (flag)
			{
				return;
			}
			if ((Object)(object)player == (Object)null)
			{
				Fade(_heartbeat, 0f, dt, 2f);
				Fade(_breath, 0f, dt, 2f);
				return;
			}
			EnsureAudio(player);
			bool flag2 = BreathSystem.Submerged && BreathSystem.HelmetWorn;
			Fade(_heartbeat, ConfigManager.Heartbeat.Value ? (_warning * ConfigManager.HeartbeatVolume.Value) : 0f, dt, 2f);
			if ((Object)(object)_heartbeat != (Object)null)
			{
				_heartbeat.pitch = 1f + 0.4f * _warning;
			}
			Fade(_breath, (ConfigManager.RegulatedBreath.Value && flag2) ? ConfigManager.BreathVolume.Value : 0f, dt, 1f);
			if (flag2)
			{
				TrackExhale(dt);
				return;
			}
			_cycleTimer = 0f;
			_lastCycleTime = 0f;
		}

		private void EnsureAudio(Player player)
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Expected O, but got Unknown
			if ((Object)(object)_audioRoot == (Object)null)
			{
				_audioRoot = new GameObject("EternalAudio");
				_audioRoot.transform.SetParent(((Component)this).transform, false);
				_heartbeat = CreateSource(AssetLoader.Clip("Audio/heartbeat_muffled.wav"), loop: true);
				_breath = CreateSource(AssetLoader.Clip("Audio/breath_regulated.wav"), loop: true);
				_oneShot = CreateSource(null, loop: false);
				_oneShot.volume = 1f;
				_exhaleClip = AssetLoader.Clip("Audio/exhale_bubbles.wav");
			}
			if (player != _audioPlayer)
			{
				_audioPlayer = player;
				AudioMixerGroup outputAudioMixerGroup = FindSfxGroup(player);
				_heartbeat.outputAudioMixerGroup = outputAudioMixerGroup;
				_breath.outputAudioMixerGroup = outputAudioMixerGroup;
				_oneShot.outputAudioMixerGroup = outputAudioMixerGroup;
			}
		}

		private AudioSource CreateSource(AudioClip clip, bool loop)
		{
			AudioSource obj = _audioRoot.AddComponent<AudioSource>();
			obj.clip = clip;
			obj.loop = loop;
			obj.playOnAwake = false;
			obj.spatialBlend = 0f;
			obj.volume = 0f;
			obj.priority = 64;
			return obj;
		}

		private static AudioMixerGroup FindSfxGroup(Player player)
		{
			EffectList[] array = (EffectList[])(object)new EffectList[2]
			{
				((Character)player).m_waterEffects,
				player.m_drownEffects
			};
			foreach (EffectList val in array)
			{
				if (val?.m_effectPrefabs == null)
				{
					continue;
				}
				EffectData[] effectPrefabs = val.m_effectPrefabs;
				foreach (EffectData val2 in effectPrefabs)
				{
					AudioSource val3 = (((Object)(object)val2.m_prefab != (Object)null) ? val2.m_prefab.GetComponentInChildren<AudioSource>(true) : null);
					if ((Object)(object)val3 != (Object)null && (Object)(object)val3.outputAudioMixerGroup != (Object)null)
					{
						Log.Info("Effects", "sounds routed to mixer group " + ((Object)val3.outputAudioMixerGroup).name);
						return val3.outputAudioMixerGroup;
					}
				}
			}
			Log.Warning("Effects", "no effects mixer group found on the player, using the ambient group");
			if (!((Object)(object)AudioMan.instance != (Object)null))
			{
				return null;
			}
			return AudioMan.instance.m_ambientMixer;
		}

		private static void Fade(AudioSource source, float target, float dt, float speed)
		{
			if (!((Object)(object)source == (Object)null) && !((Object)(object)source.clip == (Object)null))
			{
				source.volume = Mathf.MoveTowards(source.volume, target, dt * speed);
				if (target > 0.002f && !source.isPlaying)
				{
					source.time = 0f;
					source.Play();
				}
				else if (target <= 0.002f && source.volume <= 0.002f && source.isPlaying)
				{
					source.Stop();
				}
			}
		}

		private static void SetPaused(AudioSource source, bool paused)
		{
			if (!((Object)(object)source == (Object)null))
			{
				if (paused)
				{
					source.Pause();
				}
				else
				{
					source.UnPause();
				}
			}
		}

		private static void StopSource(AudioSource source)
		{
			if (!((Object)(object)source == (Object)null))
			{
				source.Stop();
				if (source.loop)
				{
					source.volume = 0f;
				}
			}
		}

		private void TrackExhale(float dt)
		{
			float num;
			if ((Object)(object)_breath != (Object)null && _breath.isPlaying)
			{
				num = _breath.time;
			}
			else
			{
				_cycleTimer = (_cycleTimer + dt) % 6f;
				num = _cycleTimer;
			}
			if (num < _lastCycleTime)
			{
				_lastCycleTime = -1f;
			}
			bool num2 = _lastCycleTime < 2.9f && num >= 2.9f;
			_lastCycleTime = num;
			if (num2 && ConfigManager.ExhaleBubbles.Value)
			{
				if ((Object)(object)_bubbles != (Object)null)
				{
					_bubbles.Emit(12);
				}
				if ((Object)(object)_oneShot != (Object)null && (Object)(object)_exhaleClip != (Object)null)
				{
					_oneShot.PlayOneShot(_exhaleClip, 0.8f * ConfigManager.BreathVolume.Value);
				}
			}
		}

		private void UpdateBubbles(Player player)
		{
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)player == (Object)null || !BreathSystem.Submerged)
			{
				SetBubbleRate(0f);
				return;
			}
			EnsureBubbles(player);
			if (!((Object)(object)_bubbles == (Object)null))
			{
				MainModule main = _bubbles.main;
				((MainModule)(ref main)).startLifetime = MinMaxCurve.op_Implicit(Mathf.Clamp(BreathSystem.DepthAboveEyes / 0.8f, 0.25f, 2.5f));
				SetBubbleRate(ConfigManager.WarningBubbles.Value ? (14f * _warning) : 0f);
			}
		}

		private void SetBubbleRate(float rate)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)_bubbles == (Object)null))
			{
				EmissionModule emission = _bubbles.emission;
				((EmissionModule)(ref emission)).rateOverTime = MinMaxCurve.op_Implicit(rate);
			}
		}

		private void EnsureBubbles(Player player)
		{
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Expected O, but got Unknown
			//IL_006b: 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_0088: 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_00af: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f5: 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_0102: Unknown result type (might be due to invalid IL or missing references)
			//IL_0112: Unknown result type (might be due to invalid IL or missing references)
			//IL_0117: Unknown result type (might be due to invalid IL or missing references)
			//IL_0132: Unknown result type (might be due to invalid IL or missing references)
			//IL_0137: Unknown result type (might be due to invalid IL or missing references)
			//IL_0155: 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_0181: Unknown result type (might be due to invalid IL or missing references)
			//IL_0191: Unknown result type (might be due to invalid IL or missing references)
			//IL_0196: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a7: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)_bubbles != (Object)null) || player != _bubblePlayer)
			{
				_bubblePlayer = player;
				GameObject val = new GameObject("EternalBubbles");
				val.transform.SetParent(((Object)(object)((Character)player).m_head != (Object)null) ? ((Character)player).m_head : ((Component)player).transform, false);
				val.SetActive(false);
				_bubbles = val.AddComponent<ParticleSystem>();
				MainModule main = _bubbles.main;
				((MainModule)(ref main)).loop = true;
				((MainModule)(ref main)).playOnAwake = false;
				((MainModule)(ref main)).startLifetime = MinMaxCurve.op_Implicit(1.5f);
				((MainModule)(ref main)).startSpeed = MinMaxCurve.op_Implicit(0.05f);
				((MainModule)(ref main)).startSize = new MinMaxCurve(0.02f, 0.06f);
				((MainModule)(ref main)).startColor = MinMaxGradient.op_Implicit(new Color(0.85f, 0.95f, 1f, 0.8f));
				((MainModule)(ref main)).simulationSpace = (ParticleSystemSimulationSpace)1;
				((MainModule)(ref main)).maxParticles = 96;
				EmissionModule emission = _bubbles.emission;
				((EmissionModule)(ref emission)).rateOverTime = MinMaxCurve.op_Implicit(0f);
				ShapeModule shape = _bubbles.shape;
				((ShapeModule)(ref shape)).shapeType = (ParticleSystemShapeType)0;
				((ShapeModule)(ref shape)).radius = 0.04f;
				VelocityOverLifetimeModule velocityOverLifetime = _bubbles.velocityOverLifetime;
				((VelocityOverLifetimeModule)(ref velocityOverLifetime)).enabled = true;
				((VelocityOverLifetimeModule)(ref velocityOverLifetime)).space = (ParticleSystemSimulationSpace)1;
				((VelocityOverLifetimeModule)(ref velocityOverLifetime)).x = new MinMaxCurve(-0.06f, 0.06f);
				((VelocityOverLifetimeModule)(ref velocityOverLifetime)).y = new MinMaxCurve(0.6f, 1f);
				((VelocityOverLifetimeModule)(ref velocityOverLifetime)).z = new MinMaxCurve(-0.06f, 0.06f);
				NoiseModule noise = _bubbles.noise;
				((NoiseModule)(ref noise)).enabled = true;
				((NoiseModule)(ref noise)).strength = MinMaxCurve.op_Implicit(0.12f);
				((NoiseModule)(ref noise)).frequency = 1.2f;
				ParticleSystemRenderer component = val.GetComponent<ParticleSystemRenderer>();
				component.renderMode = (ParticleSystemRenderMode)0;
				((Renderer)component).sharedMaterial = BubbleMaterial(player);
				val.SetActive(true);
				_bubbles.Play();
			}
		}

		private static Material BubbleMaterial(Player player)
		{
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Expected O, but got Unknown
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Expected O, but got Unknown
			if ((Object)(object)s_bubbleMaterial != (Object)null)
			{
				return s_bubbleMaterial;
			}
			ParticleSystemRenderer val = (((Object)(object)player.m_breathParticles != (Object)null) ? ((Component)player.m_breathParticles).GetComponent<ParticleSystemRenderer>() : null);
			Material val2 = (((Object)(object)val != (Object)null) ? ((Renderer)val).sharedMaterial : null);
			if ((Object)(object)val2 != (Object)null)
			{
				s_bubbleMaterial = new Material(val2);
			}
			else
			{
				s_bubbleMaterial = new Material(Shader.Find("Particles/Standard Unlit") ?? Shader.Find("Legacy Shaders/Particles/Alpha Blended") ?? Shader.Find("Sprites/Default"));
			}
			((Object)s_bubbleMaterial).name = "eternal_bubble_mat";
			Sprite val3 = AssetLoader.Sprite("Effects/bubble.png");
			if ((Object)(object)val3 != (Object)null)
			{
				s_bubbleMaterial.mainTexture = (Texture)(object)val3.texture;
			}
			Log.Info("Effects", "bubble material uses shader " + ((Object)s_bubbleMaterial.shader).name);
			return s_bubbleMaterial;
		}
	}
	internal static class EternalHelmet
	{
		internal const string PrefabName = "HelmetEternal";

		internal const string NameToken = "$item_helmeteternal";

		internal static readonly Color RuneColor = new Color(0.22f, 0.95f, 0.85f);

		private const string BasePrefab = "HelmetIron";

		private const string DescriptionToken = "$item_helmeteternal_description";

		private const int MaxForgeLevel = 7;

		private static CustomItem s_item;

		private static Material s_helmetMaterial;

		internal static bool HasRuneGlow { get; private set; }

		internal static void Register()
		{
			PrefabManager.OnVanillaPrefabsAvailable += CreateItem;
			ItemManager.OnItemsRegistered += ApplySettings;
		}

		internal static bool IsWorn(Humanoid character)
		{
			ItemData helmetItem = character.m_helmetItem;
			if (helmetItem != null)
			{
				return helmetItem.m_shared.m_name == "$item_helmeteternal";
			}
			return false;
		}

		internal static void ApplySettings()
		{
			if (s_item != null)
			{
				ApplyStats(s_item.ItemDrop.m_itemData.m_shared);
				ApplyRecipe();
				if ((Object)(object)Player.m_localPlayer != (Object)null)
				{
					SyncInventory(Player.m_localPlayer);
				}
				int num = ConfigManager.MinStationLevel.Value + ConfigManager.MaxQuality.Value - 1;
				if (num > 7)
				{
					Log.Warning("Helmet", $"quality {ConfigManager.MaxQuality.Value} needs forge level {num}, but the forge " + $"stops at {7}: the last upgrades cannot be made. Lower MinStationLevel or MaxQuality.");
				}
			}
		}

		internal static void SyncInventory(Player player)
		{
			Inventory inventory = ((Humanoid)player).GetInventory();
			if (inventory == null)
			{
				return;
			}
			foreach (ItemData allItem in inventory.GetAllItems())
			{
				if (!(allItem.m_shared.m_name != "$item_helmeteternal"))
				{
					ApplyStats(allItem.m_shared);
					float maxDurability = allItem.GetMaxDurability();
					if (allItem.m_durability > maxDurability)
					{
						allItem.m_durability = maxDurability;
					}
				}
			}
		}

		internal static void ApplyStats(SharedData shared)
		{
			shared.m_armor = ConfigManager.Armor.Value;
			shared.m_armorPerLevel = ConfigManager.ArmorPerLevel.Value;
			shared.m_maxQuality = ConfigManager.MaxQuality.Value;
			shared.m_weight = ConfigManager.Weight.Value;
			shared.m_maxDurability = ConfigManager.Durability.Value;
			shared.m_durabilityPerLevel = ConfigManager.DurabilityPerLevel.Value;
			shared.m_movementModifier = 0f - ConfigManager.MovementSpeedPenalty.Value;
			shared.m_swimStaminaModifier = ConfigManager.SwimEffortMultiplier.Value - 1f;
		}

		private static void CreateItem()
		{
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Expected O, but got Unknown
			//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d4: Expected O, but got Unknown
			PrefabManager.OnVanillaPrefabsAvailable -= CreateItem;
			try
			{
				ItemConfig val = new ItemConfig
				{
					Name = "$item_helmeteternal",
					Description = "$item_helmeteternal_description",
					CraftingStation = CraftingStations.Forge,
					MinStationLevel = ConfigManager.MinStationLevel.Value
				};
				foreach (RequirementConfig item in RecipeRequirements())
				{
					val.AddRequirement(item);
				}
				if (!EternalPlugin.IsHeadless)
				{
					Sprite val2 = AssetLoader.Sprite("Icons/helmet_eternal_64.png") ?? AssetLoader.Sprite("Icons/helmet_eternal_128.png");
					if ((Object)(object)val2 != (Object)null)
					{
						val.Icons = (Sprite[])(object)new Sprite[1] { val2 };
					}
				}
				s_item = new CustomItem("HelmetEternal", "HelmetIron", val);
				ApplyStats(s_item.ItemDrop.m_itemData.m_shared);
				if (!EternalPlugin.IsHeadless)
				{
					ApplyMaterials(s_item.ItemPrefab);
					AddExtraParts(s_item.ItemPrefab);
					AddVisual(s_item.ItemPrefab);
				}
				ItemManager.Instance.AddItem(s_item);
				Log.Info("Helmet", "HelmetEternal created from HelmetIron");
			}
			catch (Exception arg)
			{
				Log.Error("Helmet", string.Format("could not create {0}: {1}", "HelmetEternal", arg));
			}
		}

		private static IEnumerable<RequirementConfig> RecipeRequirements()
		{
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Expected O, but got Unknown
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Expected O, but got Unknown
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Expected O, but got Unknown
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Expected O, but got Unknown
			return ((IEnumerable<RequirementConfig>)(object)new RequirementConfig[4]
			{
				new RequirementConfig("Iron", ConfigManager.IronAmount.Value, ConfigManager.IronPerLevel.Value, true),
				new RequirementConfig("Bronze", ConfigManager.BronzeAmount.Value, ConfigManager.BronzePerLevel.Value, true),
				new RequirementConfig("Chain", ConfigManager.ChainAmount.Value, ConfigManager.ChainPerLevel.Value, true),
				new RequirementConfig("Obsidian", ConfigManager.ObsidianAmount.Value, ConfigManager.ObsidianPerLevel.Value, true)
			}).Where((RequirementConfig r) => r.Amount > 0 || r.AmountPerLevel > 0);
		}

		private static void ApplyRecipe()
		{
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: 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_00d0: Expected O, but got Unknown
			CustomRecipe recipe = s_item.Recipe;
			Recipe val = ((recipe != null) ? recipe.Recipe : null);
			if ((Object)(object)val == (Object)null || (Object)(object)ObjectDB.instance == (Object)null)
			{
				return;
			}
			List<Requirement> list = new List<Requirement>();
			foreach (RequirementConfig item in RecipeRequirements())
			{
				GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(item.Item);
				ItemDrop val2 = (((Object)(object)itemPrefab != (Object)null) ? itemPrefab.GetComponent<ItemDrop>() : null);
				if ((Object)(object)val2 == (Object)null)
				{
					Log.Warning("Helmet", "recipe material " + item.Item + " not found in ObjectDB, skipped");
					continue;
				}
				list.Add(new Requirement
				{
					m_resItem = val2,
					m_amount = item.Amount,
					m_amountPerLevel = item.AmountPerLevel,
					m_recover = true
				});
			}
			val.m_minStationLevel = ConfigManager.MinStationLevel.Value;
			val.m_resources = list.ToArray();
		}

		private static void ApplyMaterials(GameObject prefab)
		{
			Texture2D albedo = AssetLoader.Texture("Textures/helmet_eternal_albedo.png", linear: false, normalMap: false, (FilterMode)0);
			Texture2D normal = AssetLoader.Texture("Textures/helmet_eternal_normal.png", linear: true, normalMap: true, (FilterMode)0);
			Texture2D metallic = AssetLoader.Texture("Textures/helmet_eternal_metallic.png", linear: true, normalMap: false, (FilterMode)0);
			Texture2D emission = AssetLoader.Texture("Textures/helmet_eternal_emission.png", linear: false, normalMap: false, (FilterMode)0);
			Dictionary<Material, Material> dictionary = new Dictionary<Material, Material>();
			Renderer[] componentsInChildren = prefab.GetComponentsInChildren<Renderer>(true);
			foreach (Renderer val in componentsInChildren)
			{
				if (val is ParticleSystemRenderer)
				{
					continue;
				}
				Material[] sharedMaterials = val.sharedMaterials;
				for (int j = 0; j < sharedMaterials.Length; j++)
				{
					Material val2 = sharedMaterials[j];
					if (!((Object)(object)val2 == (Object)null))
					{
						if (!dictionary.TryGetValue(val2, out var value))
						{
							value = (dictionary[val2] = BuildMaterial(val2, albedo, normal, metallic, emission));
							s_helmetMaterial = s_helmetMaterial ?? value;
						}
						sharedMaterials[j] = value;
					}
				}
				val.sharedMaterials = sharedMaterials;
			}
		}

		private static Material BuildMaterial(Material original, Texture2D albedo, Texture2D normal, Texture2D metallic, Texture2D emission)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Expected O, but got Unknown
			//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
			Material val = new Material(original)
			{
				name = "helmet_eternal_mat"
			};
			List<string> list = new List<string>();
			List<string> list2 = new List<string>();
			SetTexture(val, "_MainTex", albedo, list, list2);
			SetTexture(val, "_BumpMap", normal, list, list2);
			SetTexture(val, "_MetallicGlossMap", metallic, list, list2);
			SetTexture(val, "_EmissionMap", emission, list, list2);
			SetFloat(val, "_UseGlossmap", 1f, list, list2);
			SetFloat(val, "_SmoothnessTextureChannel", 0f, list, list2);
			SetFloat(val, "_GlossMapScale", 1f, list, list2);
			SetFloat(val, "_Metallic", 1f, list, list2);
			HasRuneGlow = (Object)(object)emission != (Object)null && val.HasProperty("_EmissionMap") && val.HasProperty("_EmissionColor");
			if (HasRuneGlow)
			{
				val.SetColor("_EmissionColor", RuneColor * ConfigManager.RuneGlowIdle.Value);
				list.Add("_EmissionColor");
			}
			else
			{
				list2.Add("_EmissionColor (rune glow off)");
			}
			string text = ((list2.Count > 0) ? ("; the shader lacks " + string.Join(", ", list2)) : string.Empty);
			Log.Info("Helmet", "material from " + ((Object)original).name + " (" + ((Object)original.shader).name + "): set " + string.Join(", ", list) + text);
			return val;
		}

		private static void SetTexture(Material material, string property, Texture2D texture, List<string> applied, List<string> missing)
		{
			if (!((Object)(object)texture == (Object)null))
			{
				if (material.HasProperty(property))
				{
					material.SetTexture(property, (Texture)(object)texture);
					applied.Add(property);
				}
				else
				{
					missing.Add(property);
				}
			}
		}

		private static void SetFloat(Material material, string property, float value, List<string> applied, List<string> missing)
		{
			if (material.HasProperty(property))
			{
				material.SetFloat(property, value);
				applied.Add(property);
			}
			else
			{
				missing.Add(property);
			}
		}

		private static void AddExtraParts(GameObject prefab)
		{
			//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e2: Expected O, but got Unknown
			//IL_0141: Unknown result type (might be due to invalid IL or missing references)
			//IL_0146: Unknown result type (might be due to invalid IL or missing references)
			//IL_0153: Expected O, but got Unknown
			Transform val = prefab.transform.Find("attach") ?? prefab.transform.Find("attach_skin");
			MeshFilter val2 = (((Object)(object)val != (Object)null) ? ((Component)val).GetComponentInChildren<MeshFilter>(true) : null);
			if ((Object)(object)val2 == (Object)null || (Object)(object)s_helmetMaterial == (Object)null)
			{
				Log.Warning("Helmet", "the vanilla helmet mesh was not found: the horns and fins are not added");
				return;
			}
			Mesh mesh = AssetLoader.Mesh("Meshes/helmet_eternal_horns.obj");
			Mesh mesh2 = AssetLoader.Mesh("Meshes/helmet_eternal_fins.obj");
			Texture2D val3 = AssetLoader.Texture("Textures/helmet_eternal_addons_albedo.png", linear: false, normalMap: false, (FilterMode)0);
			Texture2D val4 = AssetLoader.Texture("Textures/helmet_eternal_addons_normal.png", linear: true, normalMap: true, (FilterMode)0);
			Texture2D val5 = AssetLoader.Texture("Textures/helmet_eternal_addons_metallic.png", linear: true, normalMap: false, (FilterMode)0);
			Texture2D val6 = AssetLoader.Texture("Textures/helmet_eternal_addons_emission.png", linear: false, normalMap: false, (FilterMode)0);
			if ((Object)(object)val3 == (Object)null)
			{
				Log.Warning("Helmet", "the horn and fin textures are missing: the horns and fins are not added");
				return;
			}
			Material val7 = new Material(s_helmetMaterial)
			{
				name = "helmet_eternal_horns_mat"
			};
			val7.SetTexture("_MainTex", (Texture)(object)val3);
			if ((Object)(object)val4 != (Object)null)
			{
				val7.SetTexture("_BumpMap", (Texture)(object)val4);
			}
			if ((Object)(object)val5 != (Object)null)
			{
				val7.SetTexture("_MetallicGlossMap", (Texture)(object)val5);
			}
			val7.SetTexture("_EmissionMap", (Texture)(object)(((Object)(object)val6 != (Object)null) ? val6 : Texture2D.blackTexture));
			Material val8 = new Material(val7)
			{
				name = "helmet_eternal_fins_mat"
			};
			val8.SetFloat("_Cull", 0f);
			val8.SetFloat("_TwoSidedNormals", 1f);
			Renderer component = ((Component)val2).GetComponent<Renderer>();
			AddPart(((Component)val2).transform, component, "EternalHorns", mesh, val7);
			AddPart(((Component)val2).transform, component, "EternalFins", mesh2, val8);
		}

		private static void AddPart(Transform parent, Renderer template, string name, Mesh mesh, Material material)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)mesh == (Object)null))
			{
				GameObject val = new GameObject(name)
				{
					layer = ((Component)parent).gameObject.layer
				};
				val.transform.SetParent(parent, false);
				val.AddComponent<MeshFilter>().sharedMesh = mesh;
				MeshRenderer val2 = val.AddComponent<MeshRenderer>();
				((Renderer)val2).sharedMaterial = material;
				if ((Object)(object)template != (Object)null)
				{
					((Renderer)val2).shadowCastingMode = template.shadowCastingMode;
					((Renderer)val2).receiveShadows = template.receiveShadows;
				}
				Log.Info("Helmet", $"{name} added ({mesh.triangles.Length / 3} triangles)");
			}
		}

		private static void AddVisual(GameObject prefab)
		{
			Transform val = prefab.transform.Find("attach") ?? prefab.transform.Find("attach_skin");
			if ((Object)(object)val == (Object)null)
			{
				Log.Warning("Helmet", "HelmetIron has no attach child any more: the rune glow is disabled");
			}
			else
			{
				((Component)val).gameObject.AddComponent<EternalHelmetVisual>();
			}
		}
	}
	internal sealed class EternalHelmetVisual : MonoBehaviour
	{
		private const float CheckInterval = 0.2f;

		private const float FadeSpeed = 6f;

		private static readonly int EmissionColorId = Shader.PropertyToID("_EmissionColor");

		private Renderer[] _renderers;

		private MaterialPropertyBlock _block;

		private bool _submerged;

		private float _intensity = -1f;

		private float _shown = -1f;

		private float _nextCheck;

		private void Awake()
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Expected O, but got Unknown
			_renderers = ((Component)this).GetComponentsInChildren<Renderer>(true);
			_block = new MaterialPropertyBlock();
		}

		private void OnEnable()
		{
			_intensity = -1f;
			_shown = -1f;
			_nextCheck = 0f;
		}

		private void Update()
		{
			float time = Time.time;
			if (time >= _nextCheck)
			{
				_nextCheck = time + 0.2f;
				_submerged = IsUnderwater();
				_shown = -1f;
			}
			if (EternalHelmet.HasRuneGlow)
			{
				float num = 0f;
				if (ConfigManager.RuneGlow.Value)
				{
					num = (_submerged ? ConfigManager.RuneGlowSubmerged.Value : ConfigManager.RuneGlowIdle.Value);
				}
				_intensity = ((_intensity < 0f) ? num : Mathf.MoveTowards(_intensity, num, 6f * Time.deltaTime));
				float num2 = ((_submerged && num > 0f) ? (1f + 0.15f * Mathf.Sin(time * 2.2f)) : 1f);
				float num3 = _intensity * num2;
				if (Mathf.Abs(num3 - _shown) > 0.01f)
				{
					Apply(num3);
				}
			}
		}

		private bool IsUnderwater()
		{
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			Renderer val = ((_renderers.Length != 0) ? _renderers[0] : null);
			Vector3 val2;
			if (!((Object)(object)val != (Object)null))
			{
				val2 = ((Component)this).transform.position;
			}
			else
			{
				Bounds bounds = val.bounds;
				val2 = ((Bounds)(ref bounds)).center;
			}
			Vector3 val3 = val2;
			return Floating.GetLiquidLevel(val3, 1f, (LiquidType)0) > val3.y + 0.05f;
		}

		private void Apply(float intensity)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			_shown = intensity;
			Color val = EternalHelmet.RuneColor * intensity;
			Renderer[] renderers = _renderers;
			foreach (Renderer val2 in renderers)
			{
				if (!((Object)(object)val2 == (Object)null))
				{
					val2.GetPropertyBlock(_block);
					_block.SetColor(EmissionColorId, val);
					val2.SetPropertyBlock(_block);
				}
			}
		}
	}
	[BepInPlugin("com.srcodex.eternal", "Eternal", "1.1.2")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[NetworkCompatibility(/*Could not decode attribute arguments.*/)]
	public sealed class EternalPlugin : BaseUnityPlugin
	{
		public const string Guid = "com.srcodex.eternal";

		public const string ModName = "Eternal";

		public const string Version = "1.1.2";

		private Harmony _harmony;

		internal static bool IsHeadless { get; private set; }

		private void Awake()
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Invalid comparison between Unknown and I4
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Expected O, but got Unknown
			Log.Init(((BaseUnityPlugin)this).Logger);
			IsHeadless = (int)SystemInfo.graphicsDeviceType == 4;
			ConfigManager.Bind(((BaseUnityPlugin)this).Config);
			AssetLoader.Init(Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location));
			EternalHelmet.Register();
			CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new GiveHelmetCommand());
			if (!IsHeadless)
			{
				((Component)this).gameObject.AddComponent<EffectsManager>();
				((Component)this).gameObject.AddComponent<OxygenBar>();
			}
			_harmony = new Harmony("com.srcodex.eternal");
			_harmony.PatchAll(typeof(EternalPlugin).Assembly);
			Log.Info("Patch", $"{_harmony.GetPatchedMethods().Count()} game methods patched");
			Log.Info("Config", "Eternal 1.1.2 loaded" + (IsHeadless ? " on a dedicated server, visuals off" : string.Empty));
		}

		private void Update()
		{
			if (ConfigManager.ConsumeItemSettingsDirty())
			{
				EternalHelmet.ApplySettings();
			}
		}

		private void OnDestroy()
		{
			BreathSystem.Reset("plugin unloaded");
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}
	}
	internal static class Log
	{
		internal const string Breath = "Breath";

		internal const string Dive = "Dive";

		internal const string Helmet = "Helmet";

		internal const string Assets = "Assets";

		internal const string Effects = "Effects";

		internal const string Config = "Config";

		internal const string Patch = "Patch";

		private static readonly HashSet<string> s_reportedFailures = new HashSet<string>();

		private static ManualLogSource s_source;

		internal static bool Verbose
		{
			get
			{
				if (ConfigManager.VerboseLogging != null)
				{
					return ConfigManager.VerboseLogging.Value;
				}
				return false;
			}
		}

		internal static void Init(ManualLogSource source)
		{
			s_source = source;
		}

		internal static void Info(string tag, string message)
		{
			ManualLogSource obj = s_source;
			if (obj != null)
			{
				obj.LogInfo((object)("[" + tag + "] " + message));
			}
		}

		internal static void Warning(string tag, string message)
		{
			ManualLogSource obj = s_source;
			if (obj != null)
			{
				obj.LogWarning((object)("[" + tag + "] " + message));
			}
		}

		internal static void Error(string tag, string message)
		{
			ManualLogSource obj = s_source;
			if (obj != null)
			{
				obj.LogError((object)("[" + tag + "] " + message));
			}
		}

		internal static void Debug(string tag, string message)
		{
			if (Verbose)
			{
				ManualLogSource obj = s_source;
				if (obj != null)
				{
					obj.LogInfo((object)("[" + tag + "] " + message));
				}
			}
		}

		internal static void ErrorOnce(string tag, string site, Exception exception)
		{
			if (s_reportedFailures.Add(site))
			{
				ManualLogSource obj = s_source;
				if (obj != null)
				{
					obj.LogError((object)$"[{tag}] {site} failed (later failures here are not logged): {exception}");
				}
			}
		}
	}
	internal sealed class OxygenBar : MonoBehaviour
	{
		private const float BarWidth = 128f;

		private const float StackStep = 23f;

		private const float HideDelay = 1f;

		private static readonly int VisibleId = Animator.StringToHash("Visible");

		private static readonly Color OxygenColour = new Color(0.25f, 0.78f, 0.95f);

		private static readonly Color HelmetColour = new Color(0.5f, 1f, 0.9f);

		private static readonly string[] s_numbers = BuildNumbers();

		private Hud _hud;

		private RectTransform _root;

		private Animator _animator;

		private GuiBar _fast;

		private GuiBar _slow;

		private TMP_Text _text;

		private Color _colour;

		private float _hideTimer = 1f;

		private float _offset = 23f;

		private int _shownValue = -1;

		private bool _buildFailed;

		private void Update()
		{
			//IL_0117: Unknown result type (might be due to invalid IL or missing references)
			//IL_0123: Unknown result type (might be due to invalid IL or missing references)
			//IL_0125: Unknown result type (might be due to invalid IL or missing references)
			//IL_0128: Unknown result type (might be due to invalid IL or missing references)
			//IL_011e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0135: Unknown result type (might be due to invalid IL or missing references)
			//IL_0137: 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_01a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b5: Unknown result type (might be due to invalid IL or missing references)
			Hud instance = Hud.instance;
			if ((Object)(object)instance == (Object)null)
			{
				return;
			}
			if (instance != _hud)
			{
				Build(instance);
			}
			if ((Object)(object)_root == (Object)null)
			{
				return;
			}
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null)
			{
				_animator.SetBool(VisibleId, false);
				return;
			}
			float oxygen = BreathSystem.Oxygen;
			bool flag = ConfigManager.OxygenBar.Value && (BreathSystem.Submerged || oxygen < 99.99f);
			_hideTimer = (flag ? 0f : (_hideTimer + Time.deltaTime));
			_animator.SetBool(VisibleId, _hideTimer < 1f);
			float value = oxygen / 100f;
			_fast.SetValue(value);
			_slow.SetValue(value);
			int num = Mathf.Clamp(Mathf.CeilToInt(oxygen), 0, 100);
			if (num != _shownValue)
			{
				_shownValue = num;
				_text.text = s_numbers[num];
			}
			Color val = ((BreathSystem.Submerged && BreathSystem.HelmetWorn) ? HelmetColour : OxygenColour);
			if (val != _colour)
			{
				_colour = val;
				_fast.SetColor(val);
			}
			bool flag2 = ((Character)localPlayer).GetMaxEitr() > 0f && instance.m_eitrHideTimer < 1f;
			_offset = Mathf.MoveTowards(_offset, flag2 ? 46f : 23f, 120f * Time.deltaTime);
			_root.anchoredPosition = instance.m_staminaBar2Root.anchoredPosition + new Vector2(0f, _offset);
		}

		private void OnDestroy()
		{
			if ((Object)(object)_root != (Object)null)
			{
				Object.Destroy((Object)(object)((Component)_root).gameObject);
			}
		}

		private void Build(Hud hud)
		{
			//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fb: Expected O, but got Unknown
			//IL_0134: Unknown result type (might be due to invalid IL or missing references)
			//IL_0139: Unknown result type (might be due to invalid IL or missing references)
			//IL_0144: Unknown result type (might be due to invalid IL or missing references)
			_hud = hud;
			_root = null;
			if (_buildFailed)
			{
				return;
			}
			GameObject val = null;
			try
			{
				RectTransform staminaBar2Root = hud.m_staminaBar2Root;
				val = Object.Instantiate<GameObject>(((Component)staminaBar2Root).gameObject, ((Transform)staminaBar2Root).parent, false);
				((Object)val).name = "EternalOxygenPanel";
				_animator = val.GetComponent<Animator>();
				_text = val.GetComponentInChildren<TMP_Text>(true);
				_fast = null;
				_slow = null;
				GuiBar[] componentsInChildren = val.GetComponentsInChildren<GuiBar>(true);
				foreach (GuiBar val2 in componentsInChildren)
				{
					if (((Object)val2).name.IndexOf("fast", StringComparison.OrdinalIgnoreCase) >= 0)
					{
						_fast = val2;
					}
					else
					{
						_slow = val2;
					}
				}
				if ((Object)(object)_animator == (Object)null || (Object)(object)_text == (Object)null || (Object)(object)_fast == (Object)null || (Object)(object)_slow == (Object)null)
				{
					throw new InvalidOperationException("the vanilla stamina panel no longer has the expected parts");
				}
				_root = (RectTransform)val.transform;
				_root.SetSizeWithCurrentAnchors((Axis)0, 128f + hud.m_staminaBarBorderBuffer);
				_fast.SetWidth(128f);
				_slow.SetWidth(128f);
				_colour = OxygenColour;
				_fast.SetColor(OxygenColour);
				_shownValue = -1;
				_hideTimer = 1f;
				AddIcon(_root);
				Log.Info("Effects", "oxygen bar added to the HUD");
			}
			catch (Exception ex)
			{
				_buildFailed = true;
				_root = null;
				if ((Object)(object)val != (Object)null)
				{
					Object.Destroy((Object)(object)val);
				}
				Log.Warning("Effects", "could not build the oxygen bar, the mod keeps working without it: " + ex.Message);
			}
		}

		private static void AddIcon(RectTransform root)
		{
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: 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_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
			Sprite val = AssetLoader.Sprite("Effects/bubble.png");
			if (!((Object)(object)val == (Object)null))
			{
				GameObject val2 = new GameObject("OxygenIcon", new Type[2]
				{
					typeof(RectTransform),
					typeof(Image)
				});
				RectTransform val3 = (RectTransform)val2.transform;
				((Transform)val3).SetParent((Transform)(object)root, false);
				val3.anchorMin = new Vector2(0f, 0.5f);
				val3.anchorMax = new Vector2(0f, 0.5f);
				val3.pivot = new Vector2(1f, 0.5f);
				val3.anchoredPosition = new Vector2(4f, 0f);
				val3.sizeDelta = new Vector2(15f, 15f);
				Image component = val2.GetComponent<Image>();
				component.sprite = val;
				((Graphic)component).raycastTarget = false;
				((Graphic)component).color = new Color(0.75f, 0.95f, 1f, 0.95f);
			}
		}

		private static string[] BuildNumbers()
		{
			string[] array = new string[101];
			for (int i = 0; i < array.Length; i++)
			{
				array[i] = i.ToString();
			}
			return array;
		}
	}
}
namespace Eternal.Patches
{
	[HarmonyPatch(typeof(Character), "UpdateSwimming")]
	internal static class CharacterUpdateSwimmingPatch
	{
		private static void Prefix(Character __instance)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Expected O, but got Unknown
			if ((object)__instance != Player.m_localPlayer)
			{
				return;
			}
			try
			{
				DiveController.BeforeSwimming((Player)__instance);
			}
			catch (Exception exception)
			{
				Log.ErrorOnce("Patch", "Character.UpdateSwimming prefix", exception);
			}
		}

		private static void Postfix(Character __instance)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Expected O, but got Unknown
			if ((object)__instance != Player.m_localPlayer)
			{
				return;
			}
			try
			{
				DiveController.AfterSwimming((Player)__instance);
			}
			catch (Exception exception)
			{
				Log.ErrorOnce("Patch", "Character.UpdateSwimming postfix", exception);
			}
		}

		private static void Finalizer(Character __instance)
		{
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Expected O, but got Unknown
			if ((object)__instance == Player.m_localPlayer)
			{
				DiveController.RestoreSwimSpeed((Player)__instance);
			}
		}
	}
	[HarmonyPatch(typeof(Character), "Jump")]
	internal static class CharacterJumpPatch
	{
		private static bool Prefix(Character __instance)
		{
			if (BreathSystem.Submerged)
			{
				return (object)__instance != Player.m_localPlayer;
			}
			return true;
		}
	}
	[HarmonyPatch(typeof(EnvMan), "SetEnv")]
	internal static class EnvManSetEnvPatch
	{
		private static void Postfix()
		{
			EffectsManager.ApplyUnderwaterFog();
		}
	}
	[HarmonyPatch(typeof(Player), "UpdateStats", new Type[] { typeof(float) })]
	internal static class PlayerUpdateStatsPatch
	{
		private static void Prefix(Player __instance)
		{
			if (__instance != Player.m_localPlayer)
			{
				return;
			}
			try
			{
				BreathSystem.BeforeStats(__instance);
			}
			catch (Exception exception)
			{
				Log.ErrorOnce("Patch", "Player.UpdateStats prefix", exception);
			}
		}

		private static void Postfix(Player __instance, float dt)
		{
			if (__instance != Player.m_localPlayer)
			{
				return;
			}
			try
			{
				BreathSystem.AfterStats(__instance, dt);
			}
			catch (Exception exception)
			{
				Log.ErrorOnce("Patch", "Player.UpdateStats postfix", exception);
			}
		}
	}
	[HarmonyPatch(typeof(Player), "OnSwimming")]
	internal static class PlayerOnSwimmingPatch
	{
		private static void Prefix(Player __instance)
		{
			if (BreathSystem.Submerged && __instance == Player.m_localPlayer)
			{
				__instance.m_drownDamageTimer = 0f;
			}
		}
	}
	[HarmonyPatch(typeof(Player), "Dodge")]
	internal static class PlayerDodgePatch
	{
		private static bool Prefix(Player __instance)
		{
			if (BreathSystem.Submerged)
			{
				return __instance != Player.m_localPlayer;
			}
			return true;
		}
	}
	[HarmonyPatch(typeof(Player), "OnInventoryChanged")]
	internal static class PlayerOnInventoryChangedPatch
	{
		private static void Postfix(Player __instance)
		{
			if (__instance != Player.m_localPlayer)
			{
				return;
			}
			try
			{
				EternalHelmet.SyncInventory(__instance);
			}
			catch (Exception exception)
			{
				Log.ErrorOnce("Patch", "Player.OnInventoryChanged postfix", exception);
			}
		}
	}
	[HarmonyPatch(typeof(Player), "OnDeath")]
	internal static class PlayerOnDeathPatch
	{
		private static void Postfix(Player __instance)
		{
			if (__instance == Player.m_localPlayer)
			{
				BreathSystem.Reset("death");
			}
		}
	}
	[HarmonyPatch(typeof(Player), "OnSpawned")]
	internal static class PlayerOnSpawnedPatch
	{
		private static void Postfix(Player __instance)
		{
			if (__instance == Player.m_localPlayer)
			{
				BreathSystem.Reset("spawn");
				EternalHelmet.SyncInventory(__instance);
			}
		}
	}
}
namespace Eternal.Logic
{
	public enum SwimEffort
	{
		Idle,
		Normal,
		Sprint
	}
	public readonly struct BreathSettings
	{
		public readonly float BaseDrainPerSecond;

		public readonly float DepthStepMeters;

		public readonly float DepthCostPerStep;

		public readonly float DepthMultiplierCap;

		public readonly float SprintMultiplier;

		public readonly float IdleMultiplier;

		public readonly float SwimSkillMaxReduction;

		public BreathSettings(float baseDrainPerSecond, float depthStepMeters, float depthCostPerStep, float depthMultiplierCap, float sprintMultiplier, float idleMultiplier, float swimSkillMaxReduction)
		{
			BaseDrainPerSecond = baseDrainPerSecond;
			DepthStepMeters = depthStepMeters;
			DepthCostPerStep = depthCostPerStep;
			DepthMultiplierCap = depthMultiplierCap;
			SprintMultiplier = sprintMultiplier;
			IdleMultiplier = idleMultiplier;
			SwimSkillMaxReduction = swimSkillMaxReduction;
		}
	}
	public static class BreathMath
	{
		public static float DepthMultiplier(float depthMeters, float depthStepMeters, float depthCostPerStep, float cap)
		{
			if (depthMeters <= 0f || depthStepMeters <= 0f)
			{
				return 1f;
			}
			return Clamp(1f + depthMeters / depthStepMeters * depthCostPerStep, 1f, Math.Max(1f, cap));
		}

		public static float EffortMultiplier(SwimEffort effort, float sprintMultiplier, float idleMultiplier)
		{
			return effort switch
			{
				SwimEffort.Sprint => Math.Max(0f, sprintMultiplier), 
				SwimEffort.Idle => Math.Max(0f, idleMultiplier), 
				_ => 1f, 
			};
		}

		public static float SkillMultiplier(float swimSkillFactor, float maxReduction)
		{
			return 1f - Clamp01(swimSkillFactor) * Clamp01(maxReduction);
		}

		public static float DrainPerSecond(in BreathSettings settings, float depthMeters, SwimEffort effort, float swimSkillFactor)
		{
			return Math.Max(0f, settings.BaseDrainPerSecond) * DepthMultiplier(depthMeters, settings.DepthStepMeters, settings.DepthCostPerStep, settings.DepthMultiplierCap) * EffortMultiplier(effort, settings.SprintMultiplier, settings.IdleMultiplier) * SkillMultiplier(swimSkillFactor, settings.SwimSkillMaxReduction);
		}

		public static float WarningIntensity(float fraction, float threshold)
		{
			if (threshold <= 0f)
			{
				return 0f;
			}
			return Clamp01(1f - fraction / threshold);
		}

		public static float HelmetRegenAmount(float staminaRegen, float stamina, float maxStamina, float regenTimeMultiplier, float statusEffectMultiplier, float factor, float worldRegenRate, float dt)
		{
			if (maxStamina <= 0f || dt <= 0f)
			{
				return 0f;
			}
			float num = (staminaRegen + (1f - stamina / maxStamina) * staminaRegen * regenTimeMultiplier) * Math.Max(0f, factor) * Math.Max(0f, statusEffectMultiplier);
			return Math.Max(0f, num * dt * worldRegenRate);
		}

		public static bool UpdateSubmerged(bool wasSubmerged, float extraDepth, float enterDepth, float exitDepth)
		{
			if (!wasSubmerged)
			{
				return extraDepth > enterDepth;
			}
			return extraDepth > exitDepth;
		}