Decompiled source of SurvivalNeeds v1.0.1

plugins/SurvivalNeeds.dll

Decompiled 3 days ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using Jotunn.Configs;
using Jotunn.Entities;
using Jotunn.Managers;
using Jotunn.Utils;
using Microsoft.CodeAnalysis;
using TMPro;
using UnityEngine;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("SurvivalNeeds")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.1.0")]
[assembly: AssemblyInformationalVersion("1.0.1+e71ae06cc59fc4048ec21a02357166f94a697f06")]
[assembly: AssemblyProduct("SurvivalNeeds")]
[assembly: AssemblyTitle("SurvivalNeeds")]
[assembly: AssemblyVersion("1.0.1.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace SurvivalNeeds
{
	public static class Cfg
	{
		public enum HudPlacement
		{
			Auto,
			RightOfHealth,
			AboveHealth
		}

		public static ConfigEntry<bool> HudEnabled;

		public static ConfigEntry<float> HudOffsetX;

		public static ConfigEntry<float> HudOffsetY;

		public static ConfigEntry<float> HudScale;

		public static ConfigEntry<bool> ShowPercentages;

		public static ConfigEntry<float> LowWarningPercent;

		public static ConfigEntry<HudPlacement> Placement;

		public static ConfigEntry<float> HungerMinutes;

		public static ConfigEntry<float> ThirstMinutes;

		public static ConfigEntry<float> EnergyMinutes;

		public static ConfigEntry<float> SprintEnergyMultiplier;

		public static ConfigEntry<bool> ResetOnDeath;

		public static ConfigEntry<float> Tier2MinHealth;

		public static ConfigEntry<float> Tier3MinHealth;

		public static ConfigEntry<float> Tier4MinHealth;

		public static ConfigEntry<float> Tier5MinHealth;

		public static ConfigEntry<float> Tier1Fill;

		public static ConfigEntry<float> Tier2Fill;

		public static ConfigEntry<float> Tier3Fill;

		public static ConfigEntry<float> Tier4Fill;

		public static ConfigEntry<float> Tier5Fill;

		public static ConfigEntry<float> StaminaToEnergy;

		public static ConfigEntry<float> MaxEnergyPerFood;

		public static ConfigEntry<bool> DrinksFillThirst;

		public static ConfigEntry<float> DrinkThirstFill;

		public static ConfigEntry<string> DrinkItems;

		public static ConfigEntry<int> WaterSkinCharges;

		public static ConfigEntry<float> WaterSkinThirstPerCharge;

		public static ConfigEntry<string> WaterSkinRecipe;

		public static ConfigEntry<string> WaterSkinStation;

		public static ConfigEntry<int> WaterSkinStationLevel;

		public static ConfigEntry<bool> AutoRefillInWater;

		public static ConfigEntry<bool> RefillOnUseNearWater;

		public static ConfigEntry<float> RefillDistance;

		public static ConfigEntry<bool> DrinkFromWaterEnabled;

		public static ConfigEntry<KeyboardShortcut> DrinkFromWaterKey;

		public static ConfigEntry<float> DrinkFromWaterAmount;

		public static ConfigEntry<float> DrinkFromWaterCooldown;

		public static ConfigEntry<float> RestEnergyPerTick;

		public static ConfigEntry<bool> SleepFullRestore;

		public static ConfigEntry<float> StarvationDamage;

		public static ConfigEntry<float> WarningInterval;

		public static ConfigEntry<bool> ExhaustionEnabled;

		private static ConfigDescription Admin(string text, AcceptableValueBase range = null)
		{
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Expected O, but got Unknown
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Expected O, but got Unknown
			return new ConfigDescription(text, range, new object[1] { (object)new ConfigurationManagerAttributes
			{
				IsAdminOnly = true
			} });
		}

		private static ConfigDescription Local(string text, AcceptableValueBase range = null)
		{
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Expected O, but got Unknown
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Expected O, but got Unknown
			return new ConfigDescription(text, range, new object[1] { (object)new ConfigurationManagerAttributes
			{
				IsAdminOnly = false
			} });
		}

		public static void Bind(ConfigFile c)
		{
			//IL_0603: Unknown result type (might be due to invalid IL or missing references)
			HudEnabled = c.Bind<bool>("1 - HUD", "Enabled", true, Local("Show the Hunger, Thirst and Energy bars."));
			Placement = c.Bind<HudPlacement>("1 - HUD", "Placement", HudPlacement.Auto, Local("Auto places the bars right of the health bar, or above it if that space is taken. RightOfHealth and AboveHealth force one side."));
			HudOffsetX = c.Bind<float>("1 - HUD", "OffsetX", 0f, Local("Horizontal offset in HUD pixels.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(-2000f, 2000f)));
			HudOffsetY = c.Bind<float>("1 - HUD", "OffsetY", 0f, Local("Vertical offset in HUD pixels.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(-2000f, 2000f)));
			HudScale = c.Bind<float>("1 - HUD", "Scale", 1f, Local("Size multiplier for the bars.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 3f)));
			ShowPercentages = c.Bind<bool>("1 - HUD", "ShowPercentages", true, Local("Show the percentage next to each bar."));
			LowWarningPercent = c.Bind<float>("1 - HUD", "LowWarningPercent", 15f, Local("Bars pulse when below this percentage.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f)));
			HungerMinutes = c.Bind<float>("2 - Decay", "HungerMinutes", 60f, Admin("Real minutes for hunger to drain from 100 to 0.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 10000f)));
			ThirstMinutes = c.Bind<float>("2 - Decay", "ThirstMinutes", 45f, Admin("Real minutes for thirst to drain from 100 to 0.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 10000f)));
			EnergyMinutes = c.Bind<float>("2 - Decay", "EnergyMinutes", 90f, Admin("Real minutes awake for energy to drain from 100 to 0.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 10000f)));
			SprintEnergyMultiplier = c.Bind<float>("2 - Decay", "SprintEnergyMultiplier", 3f, Admin("Energy drain multiplier while sprinting.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 20f)));
			ResetOnDeath = c.Bind<bool>("2 - Decay", "ResetOnDeath", true, Admin("Refill all needs when you die and respawn."));
			Tier2MinHealth = c.Bind<float>("3 - Food", "Tier2MinHealth", 20f, Admin("Minimum food health for tier 2. Anything below is tier 1."));
			Tier3MinHealth = c.Bind<float>("3 - Food", "Tier3MinHealth", 40f, Admin("Minimum food health for tier 3."));
			Tier4MinHealth = c.Bind<float>("3 - Food", "Tier4MinHealth", 60f, Admin("Minimum food health for tier 4."));
			Tier5MinHealth = c.Bind<float>("3 - Food", "Tier5MinHealth", 80f, Admin("Minimum food health for tier 5."));
			Tier1Fill = c.Bind<float>("3 - Food", "Tier1Fill", 10f, Admin("Hunger percent filled by a tier 1 food.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f)));
			Tier2Fill = c.Bind<float>("3 - Food", "Tier2Fill", 20f, Admin("Hunger percent filled by a tier 2 food.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f)));
			Tier3Fill = c.Bind<float>("3 - Food", "Tier3Fill", 30f, Admin("Hunger percent filled by a tier 3 food.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f)));
			Tier4Fill = c.Bind<float>("3 - Food", "Tier4Fill", 40f, Admin("Hunger percent filled by a tier 4 food.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f)));
			Tier5Fill = c.Bind<float>("3 - Food", "Tier5Fill", 50f, Admin("Hunger percent filled by a tier 5 food.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f)));
			StaminaToEnergy = c.Bind<float>("3 - Food", "StaminaToEnergy", 0.25f, Admin("Energy percent restored per point of the food's stamina value.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 10f)));
			MaxEnergyPerFood = c.Bind<float>("3 - Food", "MaxEnergyPerFood", 25f, Admin("Most energy percent a single food item can restore.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f)));
			DrinksFillThirst = c.Bind<bool>("3 - Food", "DrinksFillThirst", true, Admin("Meads and other drinks fill thirst."));
			DrinkThirstFill = c.Bind<float>("3 - Food", "DrinkThirstFill", 25f, Admin("Thirst percent filled by a mead or drink.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f)));
			DrinkItems = c.Bind<string>("3 - Food", "DrinkItems", "Mead,BarleyWine", Admin("Comma separated prefab name prefixes counted as drinks. Items flagged as drinks by the game always count."));
			WaterSkinCharges = c.Bind<int>("4 - WaterSkin", "Charges", 3, Admin("Charges in a full waterskin.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 20)));
			WaterSkinThirstPerCharge = c.Bind<float>("4 - WaterSkin", "ThirstPerCharge", 25f, Admin("Thirst percent restored per charge.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f)));
			WaterSkinRecipe = c.Bind<string>("4 - WaterSkin", "Recipe", "LeatherScraps:4,DeerHide:2", Admin("Crafting cost as Prefab:Amount pairs separated by commas."));
			WaterSkinStation = c.Bind<string>("4 - WaterSkin", "CraftingStation", "piece_workbench", Admin("Crafting station prefab name. Leave empty to craft by hand."));
			WaterSkinStationLevel = c.Bind<int>("4 - WaterSkin", "CraftingStationLevel", 1, Admin("Required crafting station level.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 10)));
			AutoRefillInWater = c.Bind<bool>("4 - WaterSkin", "AutoRefillInWater", true, Admin("Refill waterskins while standing, wading or swimming in water."));
			RefillOnUseNearWater = c.Bind<bool>("4 - WaterSkin", "RefillOnUseNearWater", true, Admin("Using a waterskin that is not full near water refills it."));
			RefillDistance = c.Bind<float>("4 - WaterSkin", "RefillDistance", 3f, Admin("How close to water, in meters, counts as near water.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 20f)));
			DrinkFromWaterEnabled = c.Bind<bool>("5 - Drink From Water", "Enabled", true, Admin("Allow drinking straight from water you stand in."));
			DrinkFromWaterKey = c.Bind<KeyboardShortcut>("5 - Drink From Water", "Key", new KeyboardShortcut((KeyCode)121, Array.Empty<KeyCode>()), Local("Key to drink from water you are standing in."));
			DrinkFromWaterAmount = c.Bind<float>("5 - Drink From Water", "ThirstPerSip", 20f, Admin("Thirst percent restored per sip.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f)));
			DrinkFromWaterCooldown = c.Bind<float>("5 - Drink From Water", "CooldownSeconds", 1.5f, Admin("Seconds between sips.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 60f)));
			RestEnergyPerTick = c.Bind<float>("6 - Rest", "EnergyPerTick", 1f, Admin("Energy points per tick while resting. One tick per second per comfort level.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f)));
			SleepFullRestore = c.Bind<bool>("6 - Rest", "SleepFullRestore", true, Admin("Sleeping through the night fully restores energy."));
			StarvationDamage = c.Bind<float>("7 - Starvation", "DamagePerTick", 1f, Admin("Damage per second when hunger or thirst is empty. Does not stack.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1000f)));
			WarningInterval = c.Bind<float>("7 - Starvation", "WarningIntervalSeconds", 30f, Admin("Seconds between repeated warning messages.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(5f, 600f)));
			ExhaustionEnabled = c.Bind<bool>("8 - Exhaustion", "Enabled", true, Admin("At 0 energy, stamina is locked at 0 and sprinting is disabled."));
		}

		public static int TierFor(float health)
		{
			if (health >= Tier5MinHealth.Value)
			{
				return 5;
			}
			if (health >= Tier4MinHealth.Value)
			{
				return 4;
			}
			if (health >= Tier3MinHealth.Value)
			{
				return 3;
			}
			if (health >= Tier2MinHealth.Value)
			{
				return 2;
			}
			return 1;
		}

		public static float FillFor(int tier)
		{
			return tier switch
			{
				5 => Tier5Fill.Value, 
				4 => Tier4Fill.Value, 
				3 => Tier3Fill.Value, 
				2 => Tier2Fill.Value, 
				_ => Tier1Fill.Value, 
			};
		}

		public static float EnergyFor(float stamina)
		{
			return Mathf.Clamp(stamina * StaminaToEnergy.Value, 0f, MaxEnergyPerFood.Value);
		}
	}
	public static class Commands
	{
		public static void Register()
		{
			CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new SetNeedCommand("sn_hunger", "Hunger"));
			CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new SetNeedCommand("sn_thirst", "Thirst"));
			CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new SetNeedCommand("sn_energy", "Energy"));
			CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new SetNeedCommand("sn_all", "All"));
			CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new RefillCommand());
			CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new StatusCommand());
		}

		public static bool Allowed()
		{
			if (Terminal.m_cheat)
			{
				return true;
			}
			SynchronizationManager instance = SynchronizationManager.Instance;
			if (instance != null)
			{
				return instance.PlayerIsAdmin;
			}
			return false;
		}

		public static void Print(string text)
		{
			if ((Object)(object)Console.instance != (Object)null)
			{
				((Terminal)Console.instance).AddString(text);
			}
		}

		public static bool Guard()
		{
			if (!Allowed())
			{
				Print("SurvivalNeeds: admin or devcommands required.");
				return false;
			}
			if ((Object)(object)Player.m_localPlayer == (Object)null)
			{
				Print("SurvivalNeeds: no local player.");
				return false;
			}
			return true;
		}
	}
	public class SetNeedCommand : ConsoleCommand
	{
		private readonly string _name;

		private readonly string _need;

		public override string Name => _name;

		public override string Help => "[0-100] Set " + _need.ToLowerInvariant() + " (SurvivalNeeds, admin or devcommands)";

		public SetNeedCommand(string name, string need)
		{
			_name = name;
			_need = need;
		}

		public override void Run(string[] args)
		{
			if (!Commands.Guard())
			{
				return;
			}
			if (args.Length < 1 || !float.TryParse(args[0], NumberStyles.Float, CultureInfo.InvariantCulture, out var result))
			{
				Commands.Print("Usage: " + _name + " <0-100>");
				return;
			}
			switch (_need)
			{
			case "Hunger":
				Needs.Hunger = result;
				break;
			case "Thirst":
				Needs.Thirst = result;
				break;
			case "Energy":
				Needs.Energy = result;
				break;
			default:
				Needs.SetAll(result);
				break;
			}
			Needs.Clamp();
			Needs.Save(Player.m_localPlayer);
			Commands.Print(StatusCommand.Describe());
		}
	}
	public class RefillCommand : ConsoleCommand
	{
		public override string Name => "sn_refill";

		public override string Help => "Refill all waterskins in your inventory (SurvivalNeeds, admin or devcommands)";

		public override void Run(string[] args)
		{
			if (Commands.Guard())
			{
				WaterSkin.RefillAll(Player.m_localPlayer, message: true);
				Commands.Print("Waterskins refilled.");
			}
		}
	}
	public class StatusCommand : ConsoleCommand
	{
		public override string Name => "sn_status";

		public override string Help => "Show hunger, thirst, energy and resting state (SurvivalNeeds)";

		public static string Describe()
		{
			return string.Format(CultureInfo.InvariantCulture, "Hunger {0:0.#}  Thirst {1:0.#}  Energy {2:0.#}  Resting {3} (comfort {4})  Exhausted {5}", Needs.Hunger, Needs.Thirst, Needs.Energy, NeedsController.Resting, NeedsController.Comfort, Needs.Exhausted);
		}

		public override void Run(string[] args)
		{
			Commands.Print(Describe());
		}
	}
	public static class Icons
	{
		public static Sprite WaterSkin;

		public static Sprite Hunger;

		public static Sprite Thirst;

		public static Sprite Energy;

		private static Sprite _rounded;

		public static Sprite Rounded
		{
			get
			{
				//IL_0019: Unknown result type (might be due to invalid IL or missing references)
				//IL_001f: Expected O, but got Unknown
				//IL_0115: Unknown result type (might be due to invalid IL or missing references)
				//IL_0124: 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)
				//IL_0088: 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_00d1: Unknown result type (might be due to invalid IL or missing references)
				//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
				if ((Object)(object)_rounded != (Object)null)
				{
					return _rounded;
				}
				Texture2D val = new Texture2D(32, 32, (TextureFormat)4, false);
				((Texture)val).wrapMode = (TextureWrapMode)1;
				((Texture)val).filterMode = (FilterMode)1;
				Color32[] array = (Color32[])(object)new Color32[1024];
				for (int i = 0; i < 32; i++)
				{
					for (int j = 0; j < 32; j++)
					{
						float num = Mathf.Clamp((float)j + 0.5f, 15.5f, 16.5f);
						float num2 = Mathf.Clamp((float)i + 0.5f, 15.5f, 16.5f);
						float num3 = Vector2.Distance(new Vector2((float)j + 0.5f, (float)i + 0.5f), new Vector2(num, num2));
						float num4 = Mathf.Clamp01(15.5f - num3 + 0.5f);
						array[i * 32 + j] = new Color32(byte.MaxValue, byte.MaxValue, byte.MaxValue, (byte)(num4 * 255f));
					}
				}
				val.SetPixels32(array);
				val.Apply();
				_rounded = Sprite.Create(val, new Rect(0f, 0f, 32f, 32f), new Vector2(0.5f, 0.5f), 100f, 0u, (SpriteMeshType)0, new Vector4(15f, 15f, 15f, 15f));
				((Object)_rounded).name = "SurvivalNeeds_Rounded";
				return _rounded;
			}
		}

		public static void Load()
		{
			WaterSkin = FromResource("waterskin.png");
			Hunger = FromResource("hunger.png");
			Thirst = FromResource("thirst.png");
			Energy = FromResource("energy.png");
		}

		private static Sprite FromResource(string file)
		{
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Expected O, but got Unknown
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			using Stream stream = typeof(Icons).Assembly.GetManifestResourceStream("SurvivalNeeds.Assets." + file);
			if (stream == null)
			{
				Plugin.Warn("Missing embedded icon " + file);
				return null;
			}
			MemoryStream memoryStream = new MemoryStream();
			stream.CopyTo(memoryStream);
			Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false);
			ImageConversion.LoadImage(val, memoryStream.ToArray());
			((Texture)val).wrapMode = (TextureWrapMode)1;
			((Texture)val).filterMode = (FilterMode)1;
			((Object)val).name = "SurvivalNeeds_" + file;
			Sprite obj = Sprite.Create(val, new Rect(0f, 0f, (float)((Texture)val).width, (float)((Texture)val).height), new Vector2(0.5f, 0.5f), 100f);
			((Object)obj).name = ((Object)val).name;
			return obj;
		}
	}
	public static class NeedEffects
	{
		public const string StarvingName = "SE_SurvivalNeeds_Starving";

		public const string DehydratedName = "SE_SurvivalNeeds_Dehydrated";

		public const string ExhaustedName = "SE_SurvivalNeeds_Exhausted";

		public static int StarvingHash;

		public static int DehydratedHash;

		public static int ExhaustedHash;

		public static void Register()
		{
			StarvingHash = Add("SE_SurvivalNeeds_Starving", "Starving", Icons.Hunger, "Your hunger is empty. You lose health every second until you eat.", "You are starving! Eat something.");
			DehydratedHash = Add("SE_SurvivalNeeds_Dehydrated", "Dehydrated", Icons.Thirst, "Your thirst is empty. You lose health every second until you drink.", "You are dehydrated! Find water.");
			ExhaustedHash = Add("SE_SurvivalNeeds_Exhausted", "Exhausted", Icons.Energy, "Your energy is empty. No stamina and no sprinting until you rest, sleep or eat.", "You are exhausted! Rest or sleep.");
		}

		private static int Add(string id, string display, Sprite icon, string tooltip, string start)
		{
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Expected O, but got Unknown
			StatusEffect val = ScriptableObject.CreateInstance<StatusEffect>();
			((Object)val).name = id;
			val.m_name = display;
			val.m_tooltip = tooltip;
			val.m_icon = icon;
			val.m_ttl = 0f;
			val.m_startMessageType = (MessageType)2;
			val.m_startMessage = start;
			val.m_stopMessage = "";
			ItemManager.Instance.AddStatusEffect(new CustomStatusEffect(val, false));
			return val.NameHash();
		}

		public static void Sync(Player p, int hash, bool active)
		{
			SEMan sEMan = ((Character)p).GetSEMan();
			if (sEMan != null)
			{
				bool flag = sEMan.HaveStatusEffect(hash);
				if (active && !flag)
				{
					sEMan.AddStatusEffect(hash, false, 0, 0f, (short)0);
				}
				else if (!active && flag)
				{
					sEMan.RemoveStatusEffect(hash, true);
				}
			}
		}
	}
	public static class Needs
	{
		public const float Max = 100f;

		private const string KeyHunger = "SurvivalNeeds.Hunger";

		private const string KeyThirst = "SurvivalNeeds.Thirst";

		private const string KeyEnergy = "SurvivalNeeds.Energy";

		public static float Hunger = 100f;

		public static float Thirst = 100f;

		public static float Energy = 100f;

		public static bool ResetPending;

		public static bool Exhausted
		{
			get
			{
				if (Cfg.ExhaustionEnabled.Value && Energy <= 0f)
				{
					return (Object)(object)Player.m_localPlayer != (Object)null;
				}
				return false;
			}
		}

		public static void Clamp()
		{
			Hunger = Mathf.Clamp(Hunger, 0f, 100f);
			Thirst = Mathf.Clamp(Thirst, 0f, 100f);
			Energy = Mathf.Clamp(Energy, 0f, 100f);
		}

		public static void SetAll(float v)
		{
			Hunger = v;
			Thirst = v;
			Energy = v;
			Clamp();
		}

		public static void LoadFrom(Player p)
		{
			if (ResetPending)
			{
				ResetPending = false;
				SetAll(100f);
				Save(p);
			}
			else
			{
				Hunger = Read(p, "SurvivalNeeds.Hunger");
				Thirst = Read(p, "SurvivalNeeds.Thirst");
				Energy = Read(p, "SurvivalNeeds.Energy");
				Clamp();
			}
		}

		private static float Read(Player p, string key)
		{
			if ((Object)(object)p != (Object)null && p.m_customData != null && p.m_customData.TryGetValue(key, out var value) && float.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out var result))
			{
				return result;
			}
			return 100f;
		}

		public static void Save(Player p)
		{
			if (!((Object)(object)p == (Object)null) && p.m_customData != null)
			{
				p.m_customData["SurvivalNeeds.Hunger"] = Hunger.ToString("0.###", CultureInfo.InvariantCulture);
				p.m_customData["SurvivalNeeds.Thirst"] = Thirst.ToString("0.###", CultureInfo.InvariantCulture);
				p.m_customData["SurvivalNeeds.Energy"] = Energy.ToString("0.###", CultureInfo.InvariantCulture);
			}
		}

		public static bool IsDrink(ItemData item)
		{
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Invalid comparison between Unknown and I4
			if (item == null || item.m_shared == null)
			{
				return false;
			}
			if (WaterSkin.Is(item))
			{
				return false;
			}
			if ((int)item.m_shared.m_itemType != 2)
			{
				return false;
			}
			if (item.m_shared.m_isDrink)
			{
				return true;
			}
			string text = (((Object)(object)item.m_dropPrefab != (Object)null) ? ((Object)item.m_dropPrefab).name : "");
			string text2 = item.m_shared.m_name ?? "";
			string[] array = (Cfg.DrinkItems.Value ?? "").Split(',');
			for (int i = 0; i < array.Length; i++)
			{
				string text3 = array[i].Trim();
				if (text3.Length != 0)
				{
					if (text.StartsWith(text3, StringComparison.OrdinalIgnoreCase))
					{
						return true;
					}
					if (text2.IndexOf(text3, StringComparison.OrdinalIgnoreCase) >= 0)
					{
						return true;
					}
				}
			}
			return false;
		}

		public static bool IsFood(ItemData item)
		{
			if (item == null || item.m_shared == null)
			{
				return false;
			}
			SharedData shared = item.m_shared;
			if (!(shared.m_food > 0f) && !(shared.m_foodStamina > 0f))
			{
				return shared.m_foodEitr > 0f;
			}
			return true;
		}

		public static void OnAte(Player p, ItemData item)
		{
			if (IsFood(item))
			{
				float num = Cfg.FillFor(Cfg.TierFor(item.m_shared.m_food));
				float num2 = Cfg.EnergyFor(item.m_shared.m_foodStamina);
				Hunger += num;
				Energy += num2;
				Clamp();
				Save(p);
			}
		}

		public static void OnDrank(Player p, ItemData item)
		{
			if (Cfg.DrinksFillThirst.Value && IsDrink(item))
			{
				Thirst += Cfg.DrinkThirstFill.Value;
				Clamp();
				Save(p);
			}
		}
	}
	public class NeedsController : MonoBehaviour
	{
		public static NeedsController Instance;

		private Player _tracked;

		private float _tick;

		private bool _resting;

		private int _comfort;

		private float _lastWarning = -9999f;

		private float _lastSip = -9999f;

		private NeedsHud _hud;

		private bool _hudFailed;

		public static bool Resting
		{
			get
			{
				if ((Object)(object)Instance != (Object)null)
				{
					return Instance._resting;
				}
				return false;
			}
		}

		public static int Comfort
		{
			get
			{
				if (!((Object)(object)Instance != (Object)null))
				{
					return 0;
				}
				return Instance._comfort;
			}
		}

		private void Awake()
		{
			Instance = this;
		}

		private void Update()
		{
			Player localPlayer = Player.m_localPlayer;
			EnsureHud();
			if ((Object)(object)localPlayer == (Object)null)
			{
				_tracked = null;
				return;
			}
			if ((Object)(object)localPlayer != (Object)(object)_tracked)
			{
				_tracked = localPlayer;
				Needs.LoadFrom(localPlayer);
				_tick = 0f;
				_resting = false;
			}
			if (((Character)localPlayer).IsDead() || ((Character)localPlayer).InIntro())
			{
				return;
			}
			float deltaTime = Time.deltaTime;
			if (deltaTime <= 0f)
			{
				return;
			}
			Needs.Hunger -= deltaTime * 100f / (Cfg.HungerMinutes.Value * 60f);
			Needs.Thirst -= deltaTime * 100f / (Cfg.ThirstMinutes.Value * 60f);
			if (!localPlayer.IsSleeping() && !_resting)
			{
				float num = deltaTime * 100f / (Cfg.EnergyMinutes.Value * 60f);
				if (((Character)localPlayer).IsRunning())
				{
					num *= Cfg.SprintEnergyMultiplier.Value;
				}
				Needs.Energy -= num;
			}
			Needs.Clamp();
			_tick += deltaTime;
			int num2 = 0;
			while (_tick >= 1f && num2++ < 5)
			{
				_tick -= 1f;
				Second(localPlayer);
			}
			if (_tick > 1f)
			{
				_tick = 0f;
			}
			HandleDrinkKey(localPlayer);
		}

		private void EnsureHud()
		{
			if ((Object)(object)_hud != (Object)null || _hudFailed || (Object)(object)Hud.instance == (Object)null || (Object)(object)Hud.instance.m_rootObject == (Object)null)
			{
				return;
			}
			try
			{
				_hud = NeedsHud.Create(Hud.instance);
			}
			catch (Exception ex)
			{
				Plugin.Warn("HUD creation failed: " + ex);
				_hudFailed = true;
			}
		}

		private void Second(Player p)
		{
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Expected O, but got Unknown
			//IL_009d: 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_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			_resting = CheckResting(p, out _comfort);
			if (_resting)
			{
				Needs.Energy += (float)Mathf.Max(1, _comfort) * Cfg.RestEnergyPerTick.Value;
			}
			bool flag = Needs.Hunger <= 0f;
			bool flag2 = Needs.Thirst <= 0f;
			if ((flag || flag2) && Cfg.StarvationDamage.Value > 0f && !((Character)p).InGodMode())
			{
				HitData val = new HitData();
				val.m_damage.m_damage = Cfg.StarvationDamage.Value;
				val.m_point = ((Component)p).transform.position;
				val.m_dir = Vector3.zero;
				val.m_hitType = (HitType)0;
				((Character)p).ApplyDamage(val, true, false, (DamageModifier)0);
			}
			NeedEffects.Sync(p, NeedEffects.StarvingHash, flag);
			NeedEffects.Sync(p, NeedEffects.DehydratedHash, flag2);
			NeedEffects.Sync(p, NeedEffects.ExhaustedHash, Needs.Exhausted);
			if (flag || flag2 || Needs.Exhausted)
			{
				if (Time.time - _lastWarning >= Cfg.WarningInterval.Value)
				{
					_lastWarning = Time.time;
					string text = ((flag && flag2) ? "You are starving and dehydrated!" : (flag ? "You are starving! Eat something." : ((!flag2) ? "You are exhausted! Rest or sleep." : "You are dehydrated! Find water.")));
					((Character)p).Message((MessageType)1, text, 0, (Sprite)null, false);
				}
			}
			else
			{
				_lastWarning = -9999f;
			}
			if (Cfg.AutoRefillInWater.Value && WaterSkin.InWater(p))
			{
				WaterSkin.RefillAll(p, message: true);
			}
			Needs.Clamp();
			Needs.Save(p);
		}

		private static bool CheckResting(Player p, out int comfort)
		{
			comfort = 0;
			if (!((Character)p).IsSitting() && !((Character)p).InBed() && (!((Character)p).IsAttached() || ((Character)p).IsAttachedToShip()))
			{
				return false;
			}
			SEMan sEMan = ((Character)p).GetSEMan();
			if (sEMan == null)
			{
				return false;
			}
			if (!sEMan.HaveStatusEffect(SEMan.s_statusEffectRested) && !sEMan.HaveStatusEffect(SEMan.s_statusEffectResting))
			{
				return false;
			}
			comfort = p.GetComfortLevel();
			if (comfort <= 0)
			{
				comfort = SE_Rested.CalculateComfortLevel(p);
			}
			if (comfort <= 0)
			{
				comfort = 1;
			}
			return true;
		}

		private void HandleDrinkKey(Player p)
		{
			//IL_0012: 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)
			if (!Cfg.DrinkFromWaterEnabled.Value)
			{
				return;
			}
			KeyboardShortcut value = Cfg.DrinkFromWaterKey.Value;
			if (((KeyboardShortcut)(ref value)).IsDown() && CanUseKeys(p) && !(Time.time - _lastSip < Cfg.DrinkFromWaterCooldown.Value))
			{
				if (!WaterSkin.InWater(p))
				{
					((Character)p).Message((MessageType)2, "Stand in water to drink", 0, (Sprite)null, false);
					return;
				}
				_lastSip = Time.time;
				Needs.Thirst += Cfg.DrinkFromWaterAmount.Value;
				Needs.Clamp();
				Needs.Save(p);
				WaterSkin.PlayDrinkEffects(p);
				((Character)p).Message((MessageType)1, "You drink from the water", 0, Icons.Thirst, false);
			}
		}

		private static bool CanUseKeys(Player p)
		{
			if (((Character)p).IsDead() || ((Character)p).InPlaceMode())
			{
				return false;
			}
			if ((Object)(object)Chat.instance != (Object)null && Chat.instance.HasFocus())
			{
				return false;
			}
			if (Console.IsVisible() || TextInput.IsVisible() || Menu.IsVisible() || InventoryGui.IsVisible() || Minimap.IsOpen() || StoreGui.IsVisible())
			{
				return false;
			}
			IInputSystem current = UnityInput.Current;
			if (current.GetKey((KeyCode)306) || current.GetKey((KeyCode)305) || current.GetKey((KeyCode)308) || current.GetKey((KeyCode)307))
			{
				return false;
			}
			return true;
		}

		public void OnPlayerDied(Player p)
		{
			if (Cfg.ResetOnDeath.Value)
			{
				Needs.SetAll(100f);
				Needs.Save(p);
				Needs.ResetPending = true;
			}
		}
	}
	public class NeedsHud : MonoBehaviour
	{
		private class Row
		{
			public RectTransform Fill;

			public Image FillImage;

			public Image Glow;

			public Image Track;

			public RectTransform Icon;

			public TMP_Text Text;

			public Color Color;

			public float Shown = -1f;
		}

		private const float Pad = 8f;

		private const float RowH = 18f;

		private const float Gap = 5f;

		private const float IconSize = 18f;

		private const float BarW = 118f;

		private const float BarH = 8f;

		private const float TextW = 36f;

		private const float Margin = 14f;

		private static readonly Color HungerColor = new Color(1f, 0.58f, 0.16f, 1f);

		private static readonly Color ThirstColor = new Color(0.25f, 0.64f, 1f, 1f);

		private static readonly Color EnergyColor = new Color(1f, 0.84f, 0.2f, 1f);

		private static readonly Color LowColor = new Color(1f, 0.22f, 0.18f, 1f);

		private static readonly Color BackColor = new Color(0.04f, 0.045f, 0.055f, 0.62f);

		private static readonly Color TrackColor = new Color(0f, 0f, 0f, 0.55f);

		private Hud _hud;

		private RectTransform _root;

		private RectTransform _panel;

		private Image _panelImage;

		private readonly List<Row> _rows = new List<Row>();

		private float _layoutTimer;

		private bool _lastPercent = true;

		public static NeedsHud Create(Hud hud)
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: 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)
			GameObject val = new GameObject("SurvivalNeedsHud", new Type[1] { typeof(RectTransform) });
			val.transform.SetParent(hud.m_rootObject.transform, false);
			val.layer = hud.m_rootObject.layer;
			NeedsHud needsHud = val.AddComponent<NeedsHud>();
			needsHud.Build(hud);
			return needsHud;
		}

		private void Build(Hud hud)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Expected O, but got Unknown
			//IL_0028: 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)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: 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_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_010a: Unknown result type (might be due to invalid IL or missing references)
			//IL_014e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0170: Unknown result type (might be due to invalid IL or missing references)
			//IL_0192: Unknown result type (might be due to invalid IL or missing references)
			_hud = hud;
			_root = (RectTransform)((Component)this).transform;
			_root.anchorMin = new Vector2(0.5f, 0.5f);
			_root.anchorMax = new Vector2(0.5f, 0.5f);
			_root.pivot = Vector2.zero;
			_root.sizeDelta = Vector2.zero;
			_panel = NewRect("Panel", (Transform)(object)_root);
			_panel.anchorMin = Vector2.zero;
			_panel.anchorMax = Vector2.zero;
			_panel.pivot = Vector2.zero;
			_panel.anchoredPosition = Vector2.zero;
			_panelImage = ((Component)_panel).gameObject.AddComponent<Image>();
			_panelImage.sprite = Icons.Rounded;
			_panelImage.type = (Type)1;
			_panelImage.pixelsPerUnitMultiplier = 1.6f;
			((Graphic)_panelImage).color = BackColor;
			((Graphic)_panelImage).raycastTarget = false;
			TMP_FontAsset font = (((Object)(object)hud.m_healthText != (Object)null) ? hud.m_healthText.font : null);
			_rows.Add(MakeRow("Energy", Icons.Energy, EnergyColor, 0, font));
			_rows.Add(MakeRow("Thirst", Icons.Thirst, ThirstColor, 1, font));
			_rows.Add(MakeRow("Hunger", Icons.Hunger, HungerColor, 2, font));
			ApplySize();
			Layout();
		}

		private static RectTransform NewRect(string name, Transform parent)
		{
			//IL_0014: 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_0026: 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_0042: Expected O, but got Unknown
			GameObject val = new GameObject(name, new Type[1] { typeof(RectTransform) });
			val.transform.SetParent(parent, false);
			val.layer = ((Component)parent).gameObject.layer;
			return (RectTransform)val.transform;
		}

		private Row MakeRow(string name, Sprite icon, Color color, int index, TMP_FontAsset font)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: 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_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_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_011f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0126: Unknown result type (might be due to invalid IL or missing references)
			//IL_0138: Unknown result type (might be due to invalid IL or missing references)
			//IL_014d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0162: Unknown result type (might be due to invalid IL or missing references)
			//IL_01af: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_0207: Unknown result type (might be due to invalid IL or missing references)
			//IL_0277: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0314: Unknown result type (might be due to invalid IL or missing references)
			//IL_0345: Unknown result type (might be due to invalid IL or missing references)
			//IL_035a: Unknown result type (might be due to invalid IL or missing references)
			//IL_036f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0384: Unknown result type (might be due to invalid IL or missing references)
			//IL_03ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_03f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_03ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_0411: Unknown result type (might be due to invalid IL or missing references)
			//IL_0426: Unknown result type (might be due to invalid IL or missing references)
			//IL_043b: Unknown result type (might be due to invalid IL or missing references)
			//IL_04a2: Unknown result type (might be due to invalid IL or missing references)
			Row obj = new Row
			{
				Color = color
			};
			RectTransform val = NewRect(name, (Transform)(object)_panel);
			val.anchorMin = Vector2.zero;
			val.anchorMax = Vector2.zero;
			val.pivot = Vector2.zero;
			val.anchoredPosition = new Vector2(8f, 8f + (float)index * 23f);
			val.sizeDelta = new Vector2(184f, 18f);
			RectTransform val2 = NewRect("Icon", (Transform)(object)val);
			Vector2 val3 = default(Vector2);
			((Vector2)(ref val3))..ctor(0f, 0.5f);
			val2.anchorMax = val3;
			val2.anchorMin = val3;
			val2.pivot = new Vector2(0.5f, 0.5f);
			val2.anchoredPosition = new Vector2(9f, 0f);
			val2.sizeDelta = new Vector2(18f, 18f);
			Image obj2 = ((Component)val2).gameObject.AddComponent<Image>();
			obj2.sprite = icon;
			obj2.preserveAspect = true;
			((Graphic)obj2).raycastTarget = false;
			obj.Icon = val2;
			RectTransform val4 = NewRect("Track", (Transform)(object)val);
			((Vector2)(ref val3))..ctor(0f, 0.5f);
			val4.anchorMax = val3;
			val4.anchorMin = val3;
			val4.pivot = new Vector2(0f, 0.5f);
			val4.anchoredPosition = new Vector2(24f, 0f);
			val4.sizeDelta = new Vector2(118f, 8f);
			obj.Track = ((Component)val4).gameObject.AddComponent<Image>();
			obj.Track.sprite = Icons.Rounded;
			obj.Track.type = (Type)1;
			obj.Track.pixelsPerUnitMultiplier = 3.75f;
			((Graphic)obj.Track).color = TrackColor;
			((Graphic)obj.Track).raycastTarget = false;
			RectTransform val5 = NewRect("Glow", (Transform)(object)val4);
			val5.anchorMin = Vector2.zero;
			val5.anchorMax = Vector2.one;
			val5.offsetMin = new Vector2(-3f, -3f);
			val5.offsetMax = new Vector2(3f, 3f);
			obj.Glow = ((Component)val5).gameObject.AddComponent<Image>();
			obj.Glow.sprite = Icons.Rounded;
			obj.Glow.type = (Type)1;
			obj.Glow.pixelsPerUnitMultiplier = 2.142857f;
			((Graphic)obj.Glow).color = new Color(LowColor.r, LowColor.g, LowColor.b, 0f);
			((Graphic)obj.Glow).raycastTarget = false;
			((Transform)val5).SetAsFirstSibling();
			RectTransform val6 = NewRect("Fill", (Transform)(object)val4);
			val6.anchorMin = Vector2.zero;
			val6.anchorMax = Vector2.one;
			val6.offsetMin = Vector2.zero;
			val6.offsetMax = Vector2.zero;
			obj.FillImage = ((Component)val6).gameObject.AddComponent<Image>();
			obj.FillImage.sprite = Icons.Rounded;
			obj.FillImage.type = (Type)1;
			obj.FillImage.pixelsPerUnitMultiplier = 3.75f;
			((Graphic)obj.FillImage).color = color;
			((Graphic)obj.FillImage).raycastTarget = false;
			obj.Fill = val6;
			RectTransform obj3 = NewRect("Shine", (Transform)(object)val6);
			obj3.anchorMin = new Vector2(0f, 0.55f);
			obj3.anchorMax = new Vector2(1f, 1f);
			obj3.offsetMin = new Vector2(2f, 0f);
			obj3.offsetMax = new Vector2(-2f, -1f);
			Image obj4 = ((Component)obj3).gameObject.AddComponent<Image>();
			obj4.sprite = Icons.Rounded;
			obj4.type = (Type)1;
			obj4.pixelsPerUnitMultiplier = 10f;
			((Graphic)obj4).color = new Color(1f, 1f, 1f, 0.22f);
			((Graphic)obj4).raycastTarget = false;
			RectTransform obj5 = NewRect("Text", (Transform)(object)val);
			((Vector2)(ref val3))..ctor(0f, 0.5f);
			obj5.anchorMax = val3;
			obj5.anchorMin = val3;
			obj5.pivot = new Vector2(0f, 0.5f);
			obj5.anchoredPosition = new Vector2(148f, 0f);
			obj5.sizeDelta = new Vector2(36f, 18f);
			TextMeshProUGUI val7 = ((Component)obj5).gameObject.AddComponent<TextMeshProUGUI>();
			if ((Object)(object)font != (Object)null)
			{
				((TMP_Text)val7).font = font;
			}
			((TMP_Text)val7).fontSize = 13f;
			((TMP_Text)val7).alignment = (TextAlignmentOptions)4097;
			((TMP_Text)val7).textWrappingMode = (TextWrappingModes)0;
			((TMP_Text)val7).overflowMode = (TextOverflowModes)0;
			((Graphic)val7).color = new Color(1f, 1f, 1f, 0.9f);
			((Graphic)val7).raycastTarget = false;
			((TMP_Text)val7).text = "100%";
			obj.Text = (TMP_Text)(object)val7;
			return obj;
		}

		private void ApplySize()
		{
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			bool flag = (_lastPercent = Cfg.ShowPercentages.Value);
			float num = 150f + (flag ? 42f : 0f) + 8f;
			float num2 = 80f;
			_panel.sizeDelta = new Vector2(num, num2);
			foreach (Row row in _rows)
			{
				((Component)row.Text).gameObject.SetActive(flag);
			}
		}

		private void Update()
		{
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			Player localPlayer = Player.m_localPlayer;
			bool flag = Cfg.HudEnabled.Value && (Object)(object)localPlayer != (Object)null && !((Character)localPlayer).IsDead();
			if (((Component)_panel).gameObject.activeSelf != flag)
			{
				((Component)_panel).gameObject.SetActive(flag);
			}
			if (flag)
			{
				if (_lastPercent != Cfg.ShowPercentages.Value)
				{
					ApplySize();
				}
				float value = Cfg.HudScale.Value;
				((Transform)_root).localScale = new Vector3(value, value, 1f);
				_layoutTimer -= Time.unscaledDeltaTime;
				if (_layoutTimer <= 0f)
				{
					_layoutTimer = 0.5f;
					Layout();
				}
				float unscaledDeltaTime = Time.unscaledDeltaTime;
				UpdateRow(_rows[0], Needs.Energy, unscaledDeltaTime);
				UpdateRow(_rows[1], Needs.Thirst, unscaledDeltaTime);
				UpdateRow(_rows[2], Needs.Hunger, unscaledDeltaTime);
			}
		}

		private void UpdateRow(Row row, float value, float dt)
		{
			//IL_0093: 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)
			//IL_01e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0208: 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_010d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0149: Unknown result type (might be due to invalid IL or missing references)
			//IL_0172: 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_019b: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_0284: Unknown result type (might be due to invalid IL or missing references)
			//IL_0289: Unknown result type (might be due to invalid IL or missing references)
			//IL_0293: Unknown result type (might be due to invalid IL or missing references)
			//IL_027d: Unknown result type (might be due to invalid IL or missing references)
			float num = Mathf.Clamp01(value / 100f);
			if (row.Shown < 0f)
			{
				row.Shown = num;
			}
			row.Shown = Mathf.Lerp(row.Shown, num, 1f - Mathf.Exp(-6f * dt));
			if (Mathf.Abs(row.Shown - num) < 0.0005f)
			{
				row.Shown = num;
			}
			float num2 = 0.06779661f;
			float num3 = ((row.Shown <= 0.001f) ? 0f : Mathf.Max(row.Shown, num2));
			row.Fill.anchorMax = new Vector2(num3, 1f);
			((Behaviour)row.FillImage).enabled = num3 > 0f;
			bool flag = value / 100f * 100f < Cfg.LowWarningPercent.Value;
			if (flag)
			{
				float num4 = 0.5f + 0.5f * Mathf.Sin(Time.unscaledTime * 6f);
				((Graphic)row.FillImage).color = Color.Lerp(row.Color, LowColor, 0.25f + 0.5f * num4);
				((Graphic)row.Glow).color = new Color(LowColor.r, LowColor.g, LowColor.b, 0.15f + 0.4f * num4);
				float num5 = 1f + 0.14f * num4;
				((Transform)row.Icon).localScale = new Vector3(num5, num5, 1f);
				((Graphic)row.Track).color = Color.Lerp(TrackColor, new Color(0.35f, 0.02f, 0.02f, 0.7f), num4);
			}
			else
			{
				((Graphic)row.FillImage).color = row.Color;
				((Graphic)row.Glow).color = new Color(LowColor.r, LowColor.g, LowColor.b, 0f);
				((Transform)row.Icon).localScale = Vector3.one;
				((Graphic)row.Track).color = TrackColor;
			}
			if (((Component)row.Text).gameObject.activeSelf)
			{
				string text = Mathf.RoundToInt(value) + "%";
				if (row.Text.text != text)
				{
					row.Text.text = text;
				}
				((Graphic)row.Text).color = (Color)(flag ? Color.Lerp(Color.white, LowColor, 0.6f) : new Color(1f, 1f, 1f, 0.9f));
			}
		}

		private bool LocalBounds(RectTransform t, out Rect rect)
		{
			//IL_0001: 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_006c: 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)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: 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_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: 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_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
			rect = default(Rect);
			if ((Object)(object)t == (Object)null || !((Component)t).gameObject.activeInHierarchy)
			{
				return false;
			}
			Vector3[] array = (Vector3[])(object)new Vector3[4];
			t.GetWorldCorners(array);
			Transform parent = ((Transform)_root).parent;
			Vector2 val = default(Vector2);
			((Vector2)(ref val))..ctor(float.MaxValue, float.MaxValue);
			Vector2 val2 = default(Vector2);
			((Vector2)(ref val2))..ctor(float.MinValue, float.MinValue);
			Vector3[] array2 = array;
			foreach (Vector3 val3 in array2)
			{
				Vector3 val4 = parent.InverseTransformPoint(val3);
				val = Vector2.Min(val, Vector2.op_Implicit(val4));
				val2 = Vector2.Max(val2, Vector2.op_Implicit(val4));
			}
			rect = Rect.MinMaxRect(val.x, val.y, val2.x, val2.y);
			if (((Rect)(ref rect)).width > 1f)
			{
				return ((Rect)(ref rect)).height > 1f;
			}
			return false;
		}

		private void Layout()
		{
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_012e: 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_013a: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_0164: Unknown result type (might be due to invalid IL or missing references)
			//IL_015c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0103: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_0215: Unknown result type (might be due to invalid IL or missing references)
			//IL_021a: Unknown result type (might be due to invalid IL or missing references)
			//IL_021f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0227: Unknown result type (might be due to invalid IL or missing references)
			//IL_022e: Unknown result type (might be due to invalid IL or missing references)
			//IL_023a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0169: Unknown result type (might be due to invalid IL or missing references)
			//IL_0108: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fd: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_hud == (Object)null)
			{
				return;
			}
			Transform parent = ((Transform)_root).parent;
			RectTransform val = (RectTransform)(object)((parent is RectTransform) ? parent : null);
			float num;
			if (!((Object)(object)val != (Object)null))
			{
				num = 1920f;
			}
			else
			{
				Rect rect = val.rect;
				num = ((Rect)(ref rect)).width;
			}
			float num2 = num;
			bool flag = false;
			Rect val2 = default(Rect);
			RectTransform[] array = (RectTransform[])(object)new RectTransform[3] { _hud.m_healthPanel, _hud.m_foodBarRoot, _hud.m_gpRoot };
			foreach (RectTransform t in array)
			{
				if (LocalBounds(t, out var rect2) && (!((Object)(object)val != (Object)null) || !(((Rect)(ref rect2)).width > num2 * 0.45f)))
				{
					val2 = (flag ? Rect.MinMaxRect(Mathf.Min(((Rect)(ref val2)).xMin, ((Rect)(ref rect2)).xMin), Mathf.Min(((Rect)(ref val2)).yMin, ((Rect)(ref rect2)).yMin), Mathf.Max(((Rect)(ref val2)).xMax, ((Rect)(ref rect2)).xMax), Mathf.Max(((Rect)(ref val2)).yMax, ((Rect)(ref rect2)).yMax)) : rect2);
					flag = true;
				}
			}
			float value = Cfg.HudScale.Value;
			Vector2 val3 = _panel.sizeDelta * value;
			Vector2 val5 = default(Vector2);
			if (!flag)
			{
				Rect val4 = (Rect)(((Object)(object)val != (Object)null) ? val.rect : new Rect(-960f, -540f, 1920f, 1080f));
				((Vector2)(ref val5))..ctor(((Rect)(ref val4)).xMin + 320f, ((Rect)(ref val4)).yMin + 30f);
			}
			else
			{
				Vector2 val6 = default(Vector2);
				((Vector2)(ref val6))..ctor(((Rect)(ref val2)).xMax + 14f, ((Rect)(ref val2)).yMin);
				Vector2 val7 = default(Vector2);
				((Vector2)(ref val7))..ctor(((Rect)(ref val2)).xMin, ((Rect)(ref val2)).yMax + 14f);
				val5 = (Vector2)(Cfg.Placement.Value switch
				{
					Cfg.HudPlacement.AboveHealth => val7, 
					Cfg.HudPlacement.RightOfHealth => val6, 
					_ => Blocked(new Rect(val6, val3)) ? val7 : val6, 
				});
			}
			val5 += new Vector2(Cfg.HudOffsetX.Value, Cfg.HudOffsetY.Value);
			((Transform)_root).localPosition = new Vector3(val5.x, val5.y, 0f);
		}

		private bool Blocked(Rect candidate)
		{
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			RectTransform[] array = (RectTransform[])(object)new RectTransform[2]
			{
				_hud.m_staminaBar2Root,
				(RectTransform)(((Object)(object)_hud.m_actionBarRoot != (Object)null) ? /*isinst with value type is only supported in some contexts*/: null)
			};
			foreach (RectTransform t in array)
			{
				if (LocalBounds(t, out var rect) && ((Rect)(ref rect)).Overlaps(candidate))
				{
					return true;
				}
			}
			Transform parent = ((Transform)_root).parent;
			RectTransform val = (RectTransform)(object)((parent is RectTransform) ? parent : null);
			if ((Object)(object)val != (Object)null)
			{
				float xMax = ((Rect)(ref candidate)).xMax;
				Rect rect2 = val.rect;
				if (xMax > ((Rect)(ref rect2)).center.x - 160f)
				{
					return true;
				}
			}
			return false;
		}
	}
	[HarmonyPatch(typeof(Player), "EatFood")]
	internal static class EatFoodPatch
	{
		private static void Postfix(Player __instance, ItemData item, bool __result)
		{
			if (__result && !((Object)(object)__instance != (Object)(object)Player.m_localPlayer))
			{
				Needs.OnAte(__instance, item);
			}
		}
	}
	[HarmonyPatch(typeof(Player), "ConsumeItem")]
	internal static class ConsumeItemPatch
	{
		private static void Postfix(Player __instance, ItemData item, bool __result)
		{
			if (__result && !((Object)(object)__instance != (Object)(object)Player.m_localPlayer))
			{
				Needs.OnDrank(__instance, item);
			}
		}
	}
	[HarmonyPatch(typeof(Humanoid), "UseItem")]
	internal static class UseItemPatch
	{
		private static bool Prefix(Humanoid __instance, ItemData item)
		{
			if (item == null || !WaterSkin.Is(item))
			{
				return true;
			}
			Player val = (Player)(object)((__instance is Player) ? __instance : null);
			if ((Object)(object)val == (Object)null || (Object)(object)val != (Object)(object)Player.m_localPlayer)
			{
				return true;
			}
			WaterSkin.Use(val, item);
			return false;
		}
	}
	[HarmonyPatch(typeof(Player), "UpdateStats", new Type[] { typeof(float) })]
	internal static class UpdateStatsPatch
	{
		private static readonly FieldRef<Player, float> Stamina = AccessTools.FieldRefAccess<Player, float>("m_stamina");

		private static void Postfix(Player __instance)
		{
			if (!((Object)(object)__instance != (Object)(object)Player.m_localPlayer) && Needs.Exhausted)
			{
				Stamina.Invoke(__instance) = 0f;
			}
		}
	}
	[HarmonyPatch(typeof(Player), "CheckRun")]
	internal static class CheckRunPatch
	{
		private static void Postfix(Player __instance, ref bool __result)
		{
			if (__result && (Object)(object)__instance == (Object)(object)Player.m_localPlayer && Needs.Exhausted)
			{
				__result = false;
			}
		}
	}
	[HarmonyPatch(typeof(Player), "SetSleeping")]
	internal static class SetSleepingPatch
	{
		private static void Prefix(Player __instance, bool sleep)
		{
			if (!sleep && !((Object)(object)__instance != (Object)(object)Player.m_localPlayer) && __instance.IsSleeping() && Cfg.SleepFullRestore.Value)
			{
				Needs.Energy = 100f;
				Needs.Save(__instance);
				((Character)__instance).Message((MessageType)1, "You wake up fully rested", 0, Icons.Energy, false);
			}
		}
	}
	[HarmonyPatch(typeof(Player), "OnDeath")]
	internal static class OnDeathPatch
	{
		private static void Postfix(Player __instance)
		{
			if (!((Object)(object)__instance != (Object)(object)Player.m_localPlayer) && !((Object)(object)NeedsController.Instance == (Object)null))
			{
				NeedsController.Instance.OnPlayerDied(__instance);
			}
		}
	}
	[HarmonyPatch(typeof(Player), "Save")]
	internal static class SavePatch
	{
		private static void Prefix(Player __instance)
		{
			if ((Object)(object)__instance == (Object)(object)Player.m_localPlayer)
			{
				Needs.Save(__instance);
			}
		}
	}
	[HarmonyPatch(typeof(ItemData), "GetTooltip", new Type[]
	{
		typeof(ItemData),
		typeof(int),
		typeof(bool),
		typeof(float),
		typeof(int),
		typeof(bool)
	})]
	internal static class TooltipPatch
	{
		private static void Postfix(ItemData item, ref string __result)
		{
			if (item == null || item.m_shared == null)
			{
				return;
			}
			try
			{
				StringBuilder stringBuilder = new StringBuilder(__result ?? "");
				if (WaterSkin.Is(item))
				{
					int charges = WaterSkin.GetCharges(item);
					stringBuilder.Append("\n\n<color=#6fbaff>Charges: <b>").Append(charges).Append("/")
						.Append(WaterSkin.MaxCharges)
						.Append("</b></color>");
					stringBuilder.Append("\n<color=#6fbaff>+").Append(Cfg.WaterSkinThirstPerCharge.Value.ToString("0")).Append("% thirst per drink</color>");
					stringBuilder.Append("\n<color=#a0a0a0>Refills in water, or use it near water.</color>");
				}
				else
				{
					if (Needs.IsFood(item))
					{
						int num = Cfg.TierFor(item.m_shared.m_food);
						stringBuilder.Append("\n\n<color=#ff9c3a>Hunger tier <b>T").Append(num).Append("</b>: +")
							.Append(Cfg.FillFor(num).ToString("0"))
							.Append("% hunger</color>");
						float num2 = Cfg.EnergyFor(item.m_shared.m_foodStamina);
						if (num2 > 0f)
						{
							stringBuilder.Append("\n<color=#ffd84a>+").Append(num2.ToString("0.#")).Append("% energy</color>");
						}
					}
					if (Cfg.DrinksFillThirst.Value && Needs.IsDrink(item))
					{
						stringBuilder.Append(Needs.IsFood(item) ? "\n" : "\n\n").Append("<color=#6fbaff>+").Append(Cfg.DrinkThirstFill.Value.ToString("0"))
							.Append("% thirst</color>");
					}
				}
				__result = stringBuilder.ToString();
			}
			catch (Exception ex)
			{
				Plugin.Warn("Tooltip patch: " + ex.Message);
			}
		}
	}
	[HarmonyPatch(typeof(InventoryGrid), "UpdateGui")]
	internal static class InventoryGridPatch
	{
		private static readonly FieldInfo InvField = AccessTools.Field(typeof(InventoryGrid), "m_inventory");

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

		private static void Postfix(InventoryGrid __instance)
		{
			try
			{
				Inventory val = (Inventory)((InvField != null) ? /*isinst with value type is only supported in some contexts*/: null);
				if (val == null || GetElement == null)
				{
					return;
				}
				int width = val.GetWidth();
				foreach (ItemData allItem in val.GetAllItems())
				{
					if (WaterSkin.Is(allItem))
					{
						object? obj = GetElement.Invoke(__instance, new object[3]
						{
							allItem.m_gridPos.x,
							allItem.m_gridPos.y,
							width
						});
						InventoryElement val2 = (InventoryElement)((obj is InventoryElement) ? obj : null);
						if (!((Object)(object)val2 == (Object)null) && !((Object)(object)val2.m_amount == (Object)null))
						{
							ChargeText.Apply(val2.m_amount, allItem);
						}
					}
				}
			}
			catch (Exception ex)
			{
				Plugin.Warn("Inventory overlay: " + ex.Message);
			}
		}
	}
	[HarmonyPatch(typeof(HotkeyBar), "UpdateIcons")]
	internal static class HotkeyBarPatch
	{
		private static readonly FieldInfo Elements = AccessTools.Field(typeof(HotkeyBar), "m_elements");

		private static readonly FieldInfo Items = AccessTools.Field(typeof(HotkeyBar), "m_items");

		private static FieldInfo _amount;

		private static void Postfix(HotkeyBar __instance)
		{
			try
			{
				IList list = ((Elements != null) ? (Elements.GetValue(__instance) as IList) : null);
				IList list2 = ((Items != null) ? (Items.GetValue(__instance) as IList) : null);
				if (list == null || list2 == null)
				{
					return;
				}
				foreach (object item in list2)
				{
					ItemData val = (ItemData)((item is ItemData) ? item : null);
					if (val == null || !WaterSkin.Is(val))
					{
						continue;
					}
					int x = val.m_gridPos.x;
					if (x < 0 || x >= list.Count)
					{
						continue;
					}
					object obj = list[x];
					if (obj != null)
					{
						if (_amount == null)
						{
							_amount = AccessTools.Field(obj.GetType(), "m_amount");
						}
						TMP_Text val2 = (TMP_Text)((_amount != null) ? /*isinst with value type is only supported in some contexts*/: null);
						if ((Object)(object)val2 != (Object)null)
						{
							ChargeText.Apply(val2, val);
						}
					}
				}
			}
			catch (Exception ex)
			{
				Plugin.Warn("Hotbar overlay: " + ex.Message);
			}
		}
	}
	internal static class ChargeText
	{
		public static void Apply(TMP_Text txt, ItemData item)
		{
			int charges = WaterSkin.GetCharges(item);
			if (!((Component)txt).gameObject.activeSelf)
			{
				((Component)txt).gameObject.SetActive(true);
			}
			txt.text = ((charges > 0) ? ("<color=#8fd0ff>" + charges + "/" + WaterSkin.MaxCharges + "</color>") : ("<color=#ff7a6a>0/" + WaterSkin.MaxCharges + "</color>"));
		}
	}
	[BepInPlugin("com.survivalneeds.valheim", "SurvivalNeeds", "1.0.1")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[NetworkCompatibility(/*Could not decode attribute arguments.*/)]
	[SynchronizationMode(/*Could not decode attribute arguments.*/)]
	public class Plugin : BaseUnityPlugin
	{
		public const string Guid = "com.survivalneeds.valheim";

		public const string ModName = "SurvivalNeeds";

		public const string Version = "1.0.1";

		public static Plugin Instance;

		private Harmony _harmony;

		private void Awake()
		{
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Expected O, but got Unknown
			Instance = this;
			Cfg.Bind(((BaseUnityPlugin)this).Config);
			Icons.Load();
			NeedEffects.Register();
			WaterSkin.Register();
			Commands.Register();
			_harmony = new Harmony("com.survivalneeds.valheim");
			_harmony.PatchAll(typeof(Plugin).Assembly);
			((Component)this).gameObject.AddComponent<NeedsController>();
			((BaseUnityPlugin)this).Logger.LogInfo((object)"SurvivalNeeds 1.0.1 loaded");
		}

		private void OnDestroy()
		{
			if (_harmony != null)
			{
				_harmony.UnpatchSelf();
			}
		}

		public static void Log(string msg)
		{
			if ((Object)(object)Instance != (Object)null)
			{
				((BaseUnityPlugin)Instance).Logger.LogInfo((object)msg);
			}
		}

		public static void Warn(string msg)
		{
			if ((Object)(object)Instance != (Object)null)
			{
				((BaseUnityPlugin)Instance).Logger.LogWarning((object)msg);
			}
		}
	}
	public static class WaterSkin
	{
		public const string PrefabName = "SurvivalNeeds_WaterSkin";

		public const string DisplayName = "Waterskin";

		public const string BasePrefab = "MeadTasty";

		private const string ChargesKey = "SurvivalNeeds.Charges";

		private static CustomItem _item;

		private static readonly FieldRef<Character, ZSyncAnimation> ZAnim = AccessTools.FieldRefAccess<Character, ZSyncAnimation>("m_zanim");

		public static int MaxCharges => Mathf.Max(1, Cfg.WaterSkinCharges.Value);

		public static void Register()
		{
			PrefabManager.OnVanillaPrefabsAvailable += AddItem;
			ItemManager.OnItemsRegistered += ApplyRecipe;
			SynchronizationManager.OnConfigurationSynchronized += delegate
			{
				ApplyRecipe();
			};
			Cfg.WaterSkinRecipe.SettingChanged += delegate
			{
				ApplyRecipe();
			};
			Cfg.WaterSkinStation.SettingChanged += delegate
			{
				ApplyRecipe();
			};
			Cfg.WaterSkinStationLevel.SettingChanged += delegate
			{
				ApplyRecipe();
			};
		}

		private static void AddItem()
		{
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Expected O, but got Unknown
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Expected O, but got Unknown
			//IL_00f2: Unknown result type (might be due to invalid IL or missing references)
			PrefabManager.OnVanillaPrefabsAvailable -= AddItem;
			ItemConfig val = new ItemConfig();
			val.Name = "Waterskin";
			val.Description = "A stitched leather pouch with a cork stopper. Holds a few good swallows of water. Refill it by wading into water or using it near water.";
			val.CraftingStation = Cfg.WaterSkinStation.Value ?? "";
			val.MinStationLevel = Cfg.WaterSkinStationLevel.Value;
			val.Requirements = ParseRequirements(Cfg.WaterSkinRecipe.Value);
			val.Icons = (Sprite[])(object)((!((Object)(object)Icons.WaterSkin != (Object)null)) ? null : new Sprite[1] { Icons.WaterSkin });
			ItemConfig val2 = val;
			_item = new CustomItem("SurvivalNeeds_WaterSkin", "MeadTasty", val2);
			ItemDrop itemDrop = _item.ItemDrop;
			if ((Object)(object)itemDrop != (Object)null)
			{
				SharedData shared = itemDrop.m_itemData.m_shared;
				shared.m_name = "Waterskin";
				shared.m_description = val2.Description;
				shared.m_itemType = (ItemType)16;
				shared.m_maxStackSize = 1;
				shared.m_weight = 1f;
				shared.m_value = 0;
				shared.m_teleportable = true;
				shared.m_consumeStatusEffect = null;
				shared.m_food = 0f;
				shared.m_foodStamina = 0f;
				shared.m_foodEitr = 0f;
				shared.m_foodRegen = 0f;
				shared.m_foodBurnTime = 0f;
				shared.m_isDrink = false;
				if ((Object)(object)Icons.WaterSkin != (Object)null)
				{
					shared.m_icons = (Sprite[])(object)new Sprite[1] { Icons.WaterSkin };
				}
			}
			ItemManager.Instance.AddItem(_item);
		}

		private static RequirementConfig[] ParseRequirements(string text)
		{
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Expected O, but got Unknown
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Expected O, but got Unknown
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Expected O, but got Unknown
			List<RequirementConfig> list = new List<RequirementConfig>();
			string[] array = (text ?? "").Split(',');
			for (int i = 0; i < array.Length; i++)
			{
				string[] array2 = array[i].Split(':');
				if (array2.Length == 2 && int.TryParse(array2[1].Trim(), out var result) && result > 0)
				{
					list.Add(new RequirementConfig(array2[0].Trim(), result, 0, true));
				}
			}
			if (list.Count == 0)
			{
				list.Add(new RequirementConfig("LeatherScraps", 4, 0, true));
				list.Add(new RequirementConfig("DeerHide", 2, 0, true));
			}
			return list.ToArray();
		}

		public static void ApplyRecipe()
		{
			//IL_00f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_0106: Unknown result type (might be due to invalid IL or missing references)
			//IL_0113: Unknown result type (might be due to invalid IL or missing references)
			//IL_011a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0126: Expected O, but got Unknown
			ObjectDB instance = ObjectDB.instance;
			if ((Object)(object)instance == (Object)null || instance.m_recipes == null)
			{
				return;
			}
			Recipe val = null;
			foreach (Recipe recipe in instance.m_recipes)
			{
				if ((Object)(object)recipe != (Object)null && (Object)(object)recipe.m_item != (Object)null && ((Object)recipe.m_item).name == "SurvivalNeeds_WaterSkin")
				{
					val = recipe;
					break;
				}
			}
			if ((Object)(object)val == (Object)null)
			{
				return;
			}
			List<Requirement> list = new List<Requirement>();
			RequirementConfig[] array = ParseRequirements(Cfg.WaterSkinRecipe.Value);
			foreach (RequirementConfig val2 in array)
			{
				GameObject itemPrefab = instance.GetItemPrefab(val2.Item);
				ItemDrop val3 = (((Object)(object)itemPrefab != (Object)null) ? itemPrefab.GetComponent<ItemDrop>() : null);
				if ((Object)(object)val3 == (Object)null)
				{
					Plugin.Warn("Unknown waterskin recipe item " + val2.Item);
					continue;
				}
				list.Add(new Requirement
				{
					m_resItem = val3,
					m_amount = val2.Amount,
					m_amountPerLevel = 0,
					m_recover = true
				});
			}
			if (list.Count > 0)
			{
				val.m_resources = list.ToArray();
			}
			val.m_minStationLevel = Cfg.WaterSkinStationLevel.Value;
			string value = Cfg.WaterSkinStation.Value;
			if (string.IsNullOrEmpty(value))
			{
				val.m_craftingStation = null;
				return;
			}
			GameObject val4 = (((Object)(object)ZNetScene.instance != (Object)null) ? ZNetScene.instance.GetPrefab(value) : null);
			CraftingStation val5 = (((Object)(object)val4 != (Object)null) ? val4.GetComponent<CraftingStation>() : null);
			if ((Object)(object)val5 != (Object)null)
			{
				val.m_craftingStation = val5;
			}
		}

		public static bool Is(ItemData item)
		{
			if (item == null || item.m_shared == null)
			{
				return false;
			}
			if ((Object)(object)item.m_dropPrefab != (Object)null)
			{
				return ((Object)item.m_dropPrefab).name == "SurvivalNeeds_WaterSkin";
			}
			if (item.m_shared.m_name == "Waterskin" && item.m_shared.m_maxStackSize == 1)
			{
				return item.m_shared.m_food <= 0f;
			}
			return false;
		}

		public static int GetCharges(ItemData item)
		{
			if (item.m_customData != null && item.m_customData.TryGetValue("SurvivalNeeds.Charges", out var value) && int.TryParse(value, out var result))
			{
				return Mathf.Clamp(result, 0, MaxCharges);
			}
			return MaxCharges;
		}

		public static void SetCharges(ItemData item, int v)
		{
			if (item.m_customData == null)
			{
				item.m_customData = new Dictionary<string, string>();
			}
			item.m_customData["SurvivalNeeds.Charges"] = Mathf.Clamp(v, 0, MaxCharges).ToString();
		}

		public static void Use(Player p, ItemData item)
		{
			int charges = GetCharges(item);
			if (charges < MaxCharges && Cfg.RefillOnUseNearWater.Value && NearWater(p, Cfg.RefillDistance.Value))
			{
				SetCharges(item, MaxCharges);
				((Character)p).Message((MessageType)2, "Waterskin refilled", 0, Icons.WaterSkin, false);
				return;
			}
			if (charges <= 0)
			{
				((Character)p).Message((MessageType)2, "Empty, refill at water", 0, Icons.WaterSkin, false);
				return;
			}
			if (Needs.Thirst >= 99.5f)
			{
				((Character)p).Message((MessageType)2, "You are not thirsty", 0, (Sprite)null, false);
				return;
			}
			SetCharges(item, charges - 1);
			Needs.Thirst += Cfg.WaterSkinThirstPerCharge.Value;
			Needs.Clamp();
			Needs.Save(p);
			PlayDrinkEffects(p);
			((Character)p).Message((MessageType)1, "Drank from waterskin (" + (charges - 1) + "/" + MaxCharges + ")", 0, Icons.Thirst, false);
		}

		public static void RefillAll(Player p, bool message)
		{
			Inventory inventory = ((Humanoid)p).GetInventory();
			if (inventory == null)
			{
				return;
			}
			bool flag = false;
			foreach (ItemData allItem in inventory.GetAllItems())
			{
				if (Is(allItem) && GetCharges(allItem) < MaxCharges)
				{
					SetCharges(allItem, MaxCharges);
					flag = true;
				}
			}
			if (flag && message)
			{
				((Character)p).Message((MessageType)2, "Waterskin refilled", 0, Icons.WaterSkin, false);
			}
		}

		public static void PlayDrinkEffects(Player p)
		{
			//IL_0035: 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_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				ZSyncAnimation val = ZAnim.Invoke((Character)(object)p);
				if ((Object)(object)val != (Object)null)
				{
					val.SetTrigger("eat");
				}
				if (((Humanoid)p).m_consumeItemEffects != null)
				{
					((Humanoid)p).m_consumeItemEffects.Create(((Component)p).transform.position, Quaternion.identity, (Transform)null, 1f, -1, default(ZDOID));
				}
			}
			catch
			{
			}
		}

		public static bool InWater(Player p)
		{
			if ((Object)(object)p != (Object)null)
			{
				if (!((Character)p).InWater())
				{
					return ((Character)p).IsSwimming();
				}
				return true;
			}
			return false;
		}

		public static bool NearWater(Player p, float distance)
		{
			//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_002d: 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_0077: 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_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: 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)
			if (InWater(p))
			{
				return true;
			}
			if (distance <= 0f || (Object)(object)ZoneSystem.instance == (Object)null)
			{
				return false;
			}
			Vector3 position = ((Component)p).transform.position;
			if (WaterAt(position, position.y, distance))
			{
				return true;
			}
			float[] array = new float[2]
			{
				distance * 0.5f,
				distance
			};
			foreach (float num in array)
			{
				for (int j = 0; j < 12; j++)
				{
					float num2 = (float)j * MathF.PI * 2f / 12f;
					if (WaterAt(new Vector3(position.x + Mathf.Cos(num2) * num, position.y, position.z + Mathf.Sin(num2) * num), position.y, distance))
					{
						return true;
					}
				}
			}
			return false;
		}

		private static bool WaterAt(Vector3 pt, float playerY, float distance)
		{
			//IL_0000: 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)
			float liquidLevel = Floating.GetLiquidLevel(pt, 1f, (LiquidType)0);
			if (liquidLevel < -1000f)
			{
				return false;
			}
			float groundHeight = ZoneSystem.instance.GetGroundHeight(pt);
			if (liquidLevel <= groundHeight + 0.15f)
			{
				return false;
			}
			return Mathf.Abs(liquidLevel - playerY) <= distance + 1.5f;
		}
	}
}