Decompiled source of Metabolism v1.0.1

Metabolism.dll

Decompiled 2 weeks ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using Jotunn.Configs;
using Jotunn.Entities;
using Jotunn.Managers;
using Jotunn.Utils;
using TMPro;
using UnityEngine;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.6.2", FrameworkDisplayName = ".NET Framework 4.6.2")]
[assembly: AssemblyCompany("Metabolism")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+c60233c15cdc95ee1f06b1262ff8f682192a1868")]
[assembly: AssemblyProduct("Metabolism")]
[assembly: AssemblyTitle("Metabolism")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace Metabolism;

internal static class IconFactory
{
	internal static Sprite CreateIntestineIcon()
	{
		//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_0016: Unknown result type (might be due to invalid IL or missing references)
		//IL_0028: 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_008b: Unknown result type (might be due to invalid IL or missing references)
		//IL_009a: 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_00a6: Expected O, but got Unknown
		//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b1: 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)
		//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
		//IL_00cc: Expected O, but got Unknown
		//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d7: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f2: Expected O, but got Unknown
		//IL_00f2: Unknown result type (might be due to invalid IL or missing references)
		//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
		//IL_010c: 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_0118: Expected O, but got Unknown
		//IL_0118: 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_0132: 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_013e: Expected O, but got Unknown
		//IL_013e: 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_0158: Unknown result type (might be due to invalid IL or missing references)
		//IL_015e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0164: Expected O, but got Unknown
		//IL_0164: Unknown result type (might be due to invalid IL or missing references)
		//IL_016f: Unknown result type (might be due to invalid IL or missing references)
		//IL_017e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0184: Unknown result type (might be due to invalid IL or missing references)
		//IL_018a: Expected O, but got Unknown
		//IL_018a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0195: Unknown result type (might be due to invalid IL or missing references)
		//IL_01a4: Unknown result type (might be due to invalid IL or missing references)
		//IL_01aa: Unknown result type (might be due to invalid IL or missing references)
		//IL_01b0: Expected O, but got Unknown
		//IL_01b0: Unknown result type (might be due to invalid IL or missing references)
		//IL_01bb: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ca: Unknown result type (might be due to invalid IL or missing references)
		//IL_01d0: Unknown result type (might be due to invalid IL or missing references)
		//IL_01d6: Expected O, but got Unknown
		//IL_01d6: Unknown result type (might be due to invalid IL or missing references)
		//IL_01e1: 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)
		//IL_01f6: Unknown result type (might be due to invalid IL or missing references)
		//IL_01fc: Expected O, but got Unknown
		//IL_01fc: 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_0216: Unknown result type (might be due to invalid IL or missing references)
		//IL_021c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0222: Expected O, but got Unknown
		//IL_0222: Unknown result type (might be due to invalid IL or missing references)
		//IL_022d: Unknown result type (might be due to invalid IL or missing references)
		//IL_023c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0242: Unknown result type (might be due to invalid IL or missing references)
		//IL_0248: Expected O, but got Unknown
		//IL_0248: Unknown result type (might be due to invalid IL or missing references)
		//IL_0253: Unknown result type (might be due to invalid IL or missing references)
		//IL_0262: Unknown result type (might be due to invalid IL or missing references)
		//IL_0268: Unknown result type (might be due to invalid IL or missing references)
		//IL_026e: Expected O, but got Unknown
		//IL_026e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0279: Unknown result type (might be due to invalid IL or missing references)
		//IL_0288: Unknown result type (might be due to invalid IL or missing references)
		//IL_028e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0294: Expected O, but got Unknown
		//IL_0294: Unknown result type (might be due to invalid IL or missing references)
		//IL_029f: 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_02b4: Unknown result type (might be due to invalid IL or missing references)
		//IL_02ba: Expected O, but got Unknown
		//IL_02ba: Unknown result type (might be due to invalid IL or missing references)
		//IL_02c5: Unknown result type (might be due to invalid IL or missing references)
		//IL_02d4: Unknown result type (might be due to invalid IL or missing references)
		//IL_02da: Unknown result type (might be due to invalid IL or missing references)
		//IL_02e0: Expected O, but got Unknown
		//IL_02e0: Unknown result type (might be due to invalid IL or missing references)
		//IL_02eb: Unknown result type (might be due to invalid IL or missing references)
		//IL_02fa: Unknown result type (might be due to invalid IL or missing references)
		//IL_0300: Unknown result type (might be due to invalid IL or missing references)
		//IL_0306: Expected O, but got Unknown
		//IL_0306: Unknown result type (might be due to invalid IL or missing references)
		//IL_0322: Unknown result type (might be due to invalid IL or missing references)
		//IL_0331: Unknown result type (might be due to invalid IL or missing references)
		//IL_0340: Expected O, but got Unknown
		Texture2D val = new Texture2D(48, 48, (TextureFormat)4, false)
		{
			name = "MetabolismIntestineIcon",
			filterMode = (FilterMode)1
		};
		Color[] pixels = (Color[])(object)new Color[2304];
		val.SetPixels(pixels);
		Color color = default(Color);
		((Color)(ref color))..ctor(0.33f, 0.035f, 0.055f, 1f);
		Color color2 = default(Color);
		((Color)(ref color2))..ctor(0.86f, 0.08f, 0.13f, 1f);
		Color color3 = default(Color);
		((Color)(ref color3))..ctor(1f, 0.24f, 0.25f, 1f);
		DrawLine(val, new Vector2(11f, 35f), new Vector2(11f, 17f), 5, color);
		DrawLine(val, new Vector2(37f, 35f), new Vector2(37f, 17f), 5, color);
		DrawLine(val, new Vector2(11f, 35f), new Vector2(19f, 39f), 5, color);
		DrawLine(val, new Vector2(37f, 35f), new Vector2(29f, 39f), 5, color);
		DrawLine(val, new Vector2(19f, 39f), new Vector2(29f, 39f), 5, color);
		DrawLine(val, new Vector2(11f, 17f), new Vector2(20f, 9f), 5, color);
		DrawLine(val, new Vector2(37f, 17f), new Vector2(28f, 9f), 5, color);
		DrawLine(val, new Vector2(20f, 9f), new Vector2(28f, 9f), 5, color);
		DrawLine(val, new Vector2(13f, 34f), new Vector2(19f, 37f), 3, color2);
		DrawLine(val, new Vector2(19f, 37f), new Vector2(29f, 37f), 3, color2);
		DrawLine(val, new Vector2(29f, 37f), new Vector2(35f, 34f), 3, color2);
		DrawLine(val, new Vector2(14f, 30f), new Vector2(34f, 30f), 3, color3);
		DrawLine(val, new Vector2(34f, 30f), new Vector2(16f, 24f), 3, color2);
		DrawLine(val, new Vector2(16f, 24f), new Vector2(33f, 18f), 3, color3);
		DrawLine(val, new Vector2(33f, 18f), new Vector2(24f, 11f), 3, color2);
		DrawLine(val, new Vector2(14f, 32f), new Vector2(14f, 18f), 3, color2);
		DrawLine(val, new Vector2(34f, 32f), new Vector2(34f, 18f), 3, color2);
		val.Apply(false, true);
		return Sprite.Create(val, new Rect(0f, 0f, 48f, 48f), new Vector2(0.5f, 0.5f), 48f);
	}

	private static void DrawLine(Texture2D texture, Vector2 from, Vector2 to, int radius, Color color)
	{
		//IL_0001: Unknown result type (might be due to invalid IL or missing references)
		//IL_0002: Unknown result type (might be due to invalid IL or missing references)
		//IL_0020: 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_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_0046: 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_0088: Unknown result type (might be due to invalid IL or missing references)
		int num = Mathf.Max(1, Mathf.CeilToInt(Vector2.Distance(from, to) * 2f));
		for (int i = 0; i <= num; i++)
		{
			Vector2 val = Vector2.Lerp(from, to, (float)i / (float)num);
			for (int j = -radius; j <= radius; j++)
			{
				for (int k = -radius; k <= radius; k++)
				{
					if (k * k + j * j <= radius * radius)
					{
						int num2 = Mathf.RoundToInt(val.x) + k;
						int num3 = Mathf.RoundToInt(val.y) + j;
						if (num2 >= 0 && num3 >= 0 && num2 < ((Texture)texture).width && num3 < ((Texture)texture).height)
						{
							texture.SetPixel(num2, num3, color);
						}
					}
				}
			}
		}
	}
}
internal sealed class MetabolismController : MonoBehaviour
{
	private const string FullnessKey = "com.igentuman.valheim.metabolism.fullness";

	private const string CapacityKey = "com.igentuman.valheim.metabolism.capacity";

	private const string CyclesKey = "com.igentuman.valheim.metabolism.cycles";

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

	private static readonly FieldInfo BaseStaminaField = AccessTools.Field(typeof(Player), "m_baseStamina");

	private Player _player;

	private float _fullness;

	private float _capacity;

	private int _cycles;

	private bool _relieving;

	private float _reliefElapsed;

	private float _reliefStartFullness;

	internal float Fullness => _fullness;

	internal float Capacity => _capacity;

	internal float FullnessRatio
	{
		get
		{
			if (!(_capacity <= 0f))
			{
				return Mathf.Clamp01(_fullness / _capacity);
			}
			return 0f;
		}
	}

	internal bool IsRelieving => _relieving;

	internal float ReliefProgress
	{
		get
		{
			if (!_relieving)
			{
				return 0f;
			}
			return Mathf.Clamp01(_reliefElapsed / Plugin.ToiletDuration.Value);
		}
	}

	internal bool IsOverfull => FullnessRatio >= 0.9999f;

	private void Awake()
	{
		_player = ((Component)this).GetComponent<Player>();
		LoadState();
	}

	private void Update()
	{
		if (!((Object)(object)_player == (Object)null) && !((Object)(object)_player != (Object)(object)Player.m_localPlayer))
		{
			NormalizeState();
			if (_relieving)
			{
				UpdateRelief(Time.deltaTime);
			}
			UpdateOverfullEffect();
		}
	}

	private void OnDestroy()
	{
		if ((Object)(object)_player != (Object)null)
		{
			SaveState();
		}
	}

	internal void LoadState()
	{
		_capacity = ReadFloat("com.igentuman.valheim.metabolism.capacity", Plugin.InitialCapacity.Value);
		_fullness = ReadFloat("com.igentuman.valheim.metabolism.fullness", 0f);
		_cycles = Mathf.Max(0, Mathf.RoundToInt(ReadFloat("com.igentuman.valheim.metabolism.cycles", 0f)));
		NormalizeState();
	}

	internal void SaveState()
	{
		if (!((Object)(object)_player == (Object)null))
		{
			_player.m_customData["com.igentuman.valheim.metabolism.fullness"] = _fullness.ToString("R", CultureInfo.InvariantCulture);
			_player.m_customData["com.igentuman.valheim.metabolism.capacity"] = _capacity.ToString("R", CultureInfo.InvariantCulture);
			_player.m_customData["com.igentuman.valheim.metabolism.cycles"] = _cycles.ToString(CultureInfo.InvariantCulture);
		}
	}

	internal void ApplyDigestion(Dictionary<Food, float> timesBeforeTick)
	{
		if ((Object)(object)_player != (Object)(object)Player.m_localPlayer || _relieving || timesBeforeTick == null)
		{
			return;
		}
		float num = 0f;
		foreach (KeyValuePair<Food, float> item in timesBeforeTick)
		{
			Food key = item.Key;
			if (key != null && key.m_item != null && !(key.m_item.m_shared.m_foodBurnTime <= 0f))
			{
				float num2 = Mathf.Max(0f, item.Value - Mathf.Max(0f, key.m_time));
				num += Plugin.WastePerFood.Value * num2 / key.m_item.m_shared.m_foodBurnTime;
			}
		}
		if (!(num <= 0f))
		{
			_fullness = Mathf.Min(_capacity, _fullness + num);
			SaveState();
			UpdateOverfullEffect();
		}
	}

	internal void TryStartRelieving()
	{
		if (_relieving)
		{
			return;
		}
		if (FullnessRatio < Plugin.ReadyThreshold.Value)
		{
			Show("$metabolism_not_ready");
			return;
		}
		if (((Character)_player).IsDead() || ((Character)_player).IsTeleporting() || ((Character)_player).InCutscene() || ((Character)_player).IsSwimming() || ((Character)_player).InBed() || ((Character)_player).IsAttached() || ((Character)_player).InAttack() || !((Character)_player).CanMove())
		{
			Show("$metabolism_bad_state");
			return;
		}
		PrivacyKind privacyKind = PrivacyEvaluator.Evaluate(_player);
		if (privacyKind == PrivacyKind.None)
		{
			Show("$metabolism_no_privacy");
			return;
		}
		Show((privacyKind == PrivacyKind.Bush) ? "$metabolism_privacy_bush" : "$metabolism_privacy_enclosure");
		_relieving = true;
		_reliefElapsed = 0f;
		_reliefStartFullness = _fullness;
		ForceCrouch(crouch: true);
		Show("$metabolism_started");
	}

	internal void ApplySkillBonuses()
	{
		//IL_001e: Unknown result type (might be due to invalid IL or missing references)
		if (!((Object)(object)_player != (Object)(object)Player.m_localPlayer))
		{
			float skillLevel = ((Character)_player).GetSkills().GetSkillLevel(Plugin.MetabolismSkill);
			float num = _player.GetBaseFoodHP();
			float num2 = ((BaseStaminaField == null) ? 75f : ((float)BaseStaminaField.GetValue(_player)));
			List<Food> foods = _player.GetFoods();
			for (int i = 0; i < foods.Count; i++)
			{
				num += foods[i].m_health;
				num2 += foods[i].m_stamina;
			}
			_player.SetMaxHealth(num * (1f + skillLevel * Plugin.HealthPerSkillLevel.Value), false);
			_player.SetMaxStamina(num2 * (1f + skillLevel * Plugin.StaminaPerSkillLevel.Value), false);
		}
	}

	private void UpdateRelief(float deltaTime)
	{
		//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
		if (((Character)_player).IsDead() || ((Character)_player).IsTeleporting())
		{
			CancelRelief();
			return;
		}
		_reliefElapsed = Mathf.Min(Plugin.ToiletDuration.Value, _reliefElapsed + deltaTime);
		_fullness = Mathf.Lerp(_reliefStartFullness, 0f, ReliefProgress);
		ForceCrouch(crouch: true);
		UpdateOverfullEffect();
		if (!(_reliefElapsed < Plugin.ToiletDuration.Value))
		{
			_fullness = 0f;
			_cycles++;
			_capacity = Mathf.Min(Plugin.MaximumCapacity.Value, Mathf.Max(_capacity, Plugin.InitialCapacity.Value) + Plugin.CapacityPerCycle.Value);
			_relieving = false;
			ForceCrouch(crouch: false);
			((Character)_player).RaiseSkill(Plugin.MetabolismSkill, Plugin.SkillExperience.Value);
			ApplySkillBonuses();
			SaveState();
			Show("$metabolism_finished");
		}
	}

	private void CancelRelief()
	{
		_relieving = false;
		ForceCrouch(crouch: false);
		SaveState();
	}

	private void UpdateOverfullEffect()
	{
		if (((Character)_player).GetSEMan() == null || (Object)(object)ObjectDB.instance == (Object)null)
		{
			return;
		}
		int stableHashCode = StringExtensionMethods.GetStableHashCode("SE_MetabolismOverfull");
		if (IsOverfull || _relieving)
		{
			if (!((Character)_player).GetSEMan().HaveStatusEffect(stableHashCode))
			{
				((Character)_player).GetSEMan().AddStatusEffect(stableHashCode, false, 0, 0f, (short)(-1));
			}
		}
		else if (((Character)_player).GetSEMan().HaveStatusEffect(stableHashCode))
		{
			((Character)_player).GetSEMan().RemoveStatusEffect(stableHashCode, false);
		}
	}

	private void NormalizeState()
	{
		float num = Plugin.InitialCapacity.Value + (float)_cycles * Plugin.CapacityPerCycle.Value;
		float num2 = Mathf.Max(Plugin.InitialCapacity.Value, Plugin.MaximumCapacity.Value);
		_capacity = Mathf.Clamp(Mathf.Max(_capacity, num), Plugin.InitialCapacity.Value, num2);
		_fullness = Mathf.Clamp(_fullness, 0f, _capacity);
	}

	private float ReadFloat(string key, float fallback)
	{
		if (!((Object)(object)_player != (Object)null) || !_player.m_customData.TryGetValue(key, out var value) || !float.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out var result) || float.IsNaN(result) || float.IsInfinity(result))
		{
			return fallback;
		}
		return result;
	}

	private void ForceCrouch(bool crouch)
	{
		if (SetCrouchMethod != null)
		{
			SetCrouchMethod.Invoke(_player, new object[1] { crouch });
		}
	}

	private void Show(string token)
	{
		((Character)_player).Message((MessageType)2, token, 0, (Sprite)null, false);
	}
}
internal sealed class MetabolismHud : MonoBehaviour
{
	private static Sprite _solidSprite;

	private GameObject _root;

	private RectTransform _rootRect;

	private RectTransform _foodRoot;

	private RectTransform _healthRoot;

	private Image _fill;

	private Image _threshold;

	private TMP_Text _value;

	private TMP_Text _status;

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

	internal void Initialize(Hud hud)
	{
		//IL_0064: Unknown result type (might be due to invalid IL or missing references)
		//IL_006e: Expected O, but got Unknown
		//IL_00be: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f2: Unknown result type (might be due to invalid IL or missing references)
		//IL_010c: Unknown result type (might be due to invalid IL or missing references)
		//IL_013b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0157: Unknown result type (might be due to invalid IL or missing references)
		//IL_016c: 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_018c: 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_01c5: Unknown result type (might be due to invalid IL or missing references)
		//IL_01d9: 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)
		//IL_0262: Unknown result type (might be due to invalid IL or missing references)
		//IL_028f: Unknown result type (might be due to invalid IL or missing references)
		//IL_02a9: Unknown result type (might be due to invalid IL or missing references)
		//IL_02bd: Unknown result type (might be due to invalid IL or missing references)
		//IL_02ec: Unknown result type (might be due to invalid IL or missing references)
		//IL_02f1: Unknown result type (might be due to invalid IL or missing references)
		//IL_030f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0324: Unknown result type (might be due to invalid IL or missing references)
		//IL_0339: Unknown result type (might be due to invalid IL or missing references)
		//IL_0344: Unknown result type (might be due to invalid IL or missing references)
		//IL_0358: Unknown result type (might be due to invalid IL or missing references)
		//IL_03f9: Unknown result type (might be due to invalid IL or missing references)
		//IL_040e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0423: Unknown result type (might be due to invalid IL or missing references)
		//IL_0438: Unknown result type (might be due to invalid IL or missing references)
		//IL_044c: Unknown result type (might be due to invalid IL or missing references)
		if (!((Object)(object)_root != (Object)null) && !((Object)(object)hud == (Object)null) && !((Object)(object)hud.m_foodBarRoot == (Object)null))
		{
			_foodRoot = hud.m_foodBarRoot;
			_healthRoot = hud.m_healthBarRoot;
			_root = new GameObject("MetabolismBowelBar", new Type[2]
			{
				typeof(RectTransform),
				typeof(CanvasGroup)
			});
			_rootRect = _root.GetComponent<RectTransform>();
			Transform val = (((Object)(object)hud.m_rootObject == (Object)null) ? ((Component)hud).transform : hud.m_rootObject.transform);
			((Transform)_rootRect).SetParent(val, false);
			_rootRect.anchorMin = new Vector2(0.5f, 0.5f);
			_rootRect.anchorMax = new Vector2(0.5f, 0.5f);
			_rootRect.pivot = new Vector2(0f, 0.5f);
			_rootRect.sizeDelta = new Vector2(32f, 128f);
			UpdatePosition();
			GameObject obj = CreateImage("Background", (Transform)(object)_rootRect, new Color(0.12f, 0.035f, 0.025f, 0.92f));
			RectTransform component = obj.GetComponent<RectTransform>();
			component.anchorMin = new Vector2(0.5f, 1f);
			component.anchorMax = new Vector2(0.5f, 1f);
			component.pivot = new Vector2(0.5f, 1f);
			component.anchoredPosition = Vector2.zero;
			component.sizeDelta = new Vector2(32f, 96f);
			Outline obj2 = obj.AddComponent<Outline>();
			((Shadow)obj2).effectColor = new Color(0.06f, 0.015f, 0.01f, 1f);
			((Shadow)obj2).effectDistance = new Vector2(2f, -2f);
			GameObject val2 = CreateImage("Fill", (Transform)(object)component, new Color(0.72f, 0.08f, 0.08f, 1f));
			_fill = val2.GetComponent<Image>();
			Stretch(val2.GetComponent<RectTransform>(), 3f);
			_fill.type = (Type)3;
			_fill.fillMethod = (FillMethod)1;
			_fill.fillOrigin = 0;
			GameObject val3 = CreateImage("ReadyThreshold", (Transform)(object)component, new Color(1f, 0.78f, 0.3f, 0.9f));
			_threshold = val3.GetComponent<Image>();
			RectTransform component2 = val3.GetComponent<RectTransform>();
			component2.anchorMin = new Vector2(0f, Plugin.ReadyThreshold.Value);
			component2.anchorMax = new Vector2(1f, Plugin.ReadyThreshold.Value);
			component2.sizeDelta = new Vector2(0f, 2f);
			GameObject val4 = new GameObject("Icon", new Type[2]
			{
				typeof(RectTransform),
				typeof(Image)
			});
			RectTransform component3 = val4.GetComponent<RectTransform>();
			((Transform)component3).SetParent((Transform)(object)_rootRect, false);
			component3.anchorMin = new Vector2(0.5f, 0f);
			component3.anchorMax = new Vector2(0.5f, 0f);
			component3.pivot = new Vector2(0.5f, 0f);
			component3.anchoredPosition = Vector2.zero;
			component3.sizeDelta = new Vector2(28f, 28f);
			val4.GetComponent<Image>().sprite = Plugin.IntestineIcon;
			TMP_Text template = ((hud.m_foodTime != null && hud.m_foodTime.Length != 0) ? hud.m_foodTime[0] : null);
			_value = CreateText("Value", (Transform)(object)component, template, 13f, (TextAlignmentOptions)514);
			Stretch(_value.rectTransform, 1f);
			_status = CreateText("Status", (Transform)(object)_rootRect, template, 12f, (TextAlignmentOptions)513);
			RectTransform rectTransform = _status.rectTransform;
			rectTransform.anchorMin = new Vector2(0f, 0f);
			rectTransform.anchorMax = new Vector2(0f, 0f);
			rectTransform.pivot = new Vector2(0f, 0.5f);
			rectTransform.anchoredPosition = new Vector2(36f, 14f);
			rectTransform.sizeDelta = new Vector2(220f, 28f);
			Plugin.LogInfo("Bowel HUD created outside the food-bar clipping hierarchy");
		}
	}

	private void Update()
	{
		//IL_0090: 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_00aa: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f9: 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_0178: Unknown result type (might be due to invalid IL or missing references)
		//IL_01bf: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)_root == (Object)null)
		{
			return;
		}
		Player localPlayer = Player.m_localPlayer;
		MetabolismController metabolismController = (((Object)(object)localPlayer == (Object)null) ? null : ((Component)localPlayer).GetComponent<MetabolismController>());
		bool flag = (Object)(object)localPlayer != (Object)null && (Object)(object)metabolismController != (Object)null && (Object)(object)Hud.instance != (Object)null && !Hud.instance.m_userHidden;
		_root.SetActive(flag);
		if (flag)
		{
			float fullnessRatio = metabolismController.FullnessRatio;
			_fill.fillAmount = fullnessRatio;
			((Graphic)_fill).color = Color.Lerp(new Color(0.62f, 0.28f, 0.28f), new Color(0.85f, 0.24f, 0.15f), fullnessRatio);
			_value.text = Mathf.RoundToInt(fullnessRatio * 100f) + "%";
			RectTransform rectTransform = ((Graphic)_threshold).rectTransform;
			rectTransform.anchorMin = new Vector2(0f, Plugin.ReadyThreshold.Value);
			rectTransform.anchorMax = new Vector2(1f, Plugin.ReadyThreshold.Value);
			if (metabolismController.IsRelieving)
			{
				_status.text = Localization.instance.Localize("$metabolism_progress") + " " + Mathf.CeilToInt((1f - metabolismController.ReliefProgress) * Plugin.ToiletDuration.Value) + "s";
				((Graphic)_status).color = Color.white;
			}
			else if (fullnessRatio >= Plugin.ReadyThreshold.Value)
			{
				_status.text = Localization.instance.Localize("$metabolism_ready");
				((Graphic)_status).color = new Color(1f, 0.78f, 0.25f);
			}
			else
			{
				_status.text = string.Empty;
			}
		}
	}

	private void LateUpdate()
	{
		if ((Object)(object)_root != (Object)null && (Object)(object)_foodRoot != (Object)null)
		{
			UpdatePosition();
		}
	}

	private void UpdatePosition()
	{
		//IL_004c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0051: 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_0059: 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_00b5: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
		//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ef: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
		//IL_0105: 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_0119: Unknown result type (might be due to invalid IL or missing references)
		//IL_0131: Unknown result type (might be due to invalid IL or missing references)
		//IL_013b: Unknown result type (might be due to invalid IL or missing references)
		//IL_014b: Unknown result type (might be due to invalid IL or missing references)
		if (!((Object)(object)_rootRect == (Object)null) && !((Object)(object)_foodRoot == (Object)null))
		{
			Transform parent = ((Transform)_rootRect).parent;
			_foodRoot.GetWorldCorners(_foodCorners);
			float num = float.PositiveInfinity;
			for (int i = 0; i < _foodCorners.Length; i++)
			{
				Vector3 val = parent.InverseTransformPoint(_foodCorners[i]);
				num = Mathf.Min(num, val.x);
			}
			(((Object)(object)_healthRoot == (Object)null) ? _foodRoot : _healthRoot).GetWorldCorners(_foodCorners);
			float num2 = float.PositiveInfinity;
			for (int j = 0; j < _foodCorners.Length; j++)
			{
				Vector3 val2 = parent.InverseTransformPoint(_foodCorners[j]);
				num2 = Mathf.Min(num2, val2.y);
			}
			RectTransform rootRect = _rootRect;
			float num3 = num - 8f;
			Rect rect = _rootRect.rect;
			float num4 = num3 - ((Rect)(ref rect)).width;
			float num5 = num2;
			rect = _rootRect.rect;
			((Transform)rootRect).localPosition = new Vector3(num4, num5 + ((Rect)(ref rect)).height * _rootRect.pivot.y - 32f, ((Transform)_rootRect).localPosition.z);
			((Transform)_rootRect).localRotation = Quaternion.identity;
		}
	}

	private static GameObject CreateImage(string name, Transform parent, Color color)
	{
		//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_0033: 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_004b: Expected O, but got Unknown
		GameObject val = new GameObject(name, new Type[2]
		{
			typeof(RectTransform),
			typeof(Image)
		});
		val.transform.SetParent(parent, false);
		Image component = val.GetComponent<Image>();
		component.sprite = GetSolidSprite();
		((Graphic)component).color = color;
		return val;
	}

	private static Sprite GetSolidSprite()
	{
		//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_0027: 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)
		//IL_0034: 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_005f: Unknown result type (might be due to invalid IL or missing references)
		//IL_006e: Expected O, but got Unknown
		if ((Object)(object)_solidSprite != (Object)null)
		{
			return _solidSprite;
		}
		Texture2D val = new Texture2D(1, 1, (TextureFormat)4, false)
		{
			name = "MetabolismSolidPixel"
		};
		val.SetPixel(0, 0, Color.white);
		val.Apply(false, true);
		_solidSprite = Sprite.Create(val, new Rect(0f, 0f, 1f, 1f), new Vector2(0.5f, 0.5f), 1f);
		((Object)_solidSprite).name = "MetabolismSolidSprite";
		return _solidSprite;
	}

	private static TMP_Text CreateText(string name, Transform parent, TMP_Text template, float size, TextAlignmentOptions alignment)
	{
		//IL_0014: Unknown result type (might be due to invalid IL or missing references)
		//IL_001a: Expected O, but got Unknown
		//IL_006a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0058: Unknown result type (might be due to invalid IL or missing references)
		GameObject val = new GameObject(name, new Type[1] { typeof(RectTransform) });
		val.SetActive(false);
		val.transform.SetParent(parent, false);
		TextMeshProUGUI val2 = val.AddComponent<TextMeshProUGUI>();
		if ((Object)(object)template != (Object)null)
		{
			((TMP_Text)val2).font = template.font;
			((TMP_Text)val2).fontSharedMaterial = template.fontSharedMaterial;
			((Graphic)val2).color = ((Graphic)template).color;
		}
		((TMP_Text)val2).fontSize = size;
		((TMP_Text)val2).alignment = alignment;
		((TMP_Text)val2).textWrappingMode = (TextWrappingModes)0;
		((Graphic)val2).raycastTarget = false;
		val.SetActive(true);
		return (TMP_Text)(object)val2;
	}

	private static void Stretch(RectTransform rect, float inset)
	{
		//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_0019: Unknown result type (might be due to invalid IL or missing references)
		//IL_0028: Unknown result type (might be due to invalid IL or missing references)
		rect.anchorMin = Vector2.zero;
		rect.anchorMax = Vector2.one;
		rect.offsetMin = new Vector2(inset, inset);
		rect.offsetMax = new Vector2(0f - inset, 0f - inset);
	}
}
[HarmonyPatch]
internal static class PlayerAwakePatch
{
	private static MethodBase TargetMethod()
	{
		return AccessTools.Method(typeof(Player), "Awake", (Type[])null, (Type[])null);
	}

	private static void Postfix(Player __instance)
	{
		if ((Object)(object)((Component)__instance).GetComponent<MetabolismController>() == (Object)null)
		{
			((Component)__instance).gameObject.AddComponent<MetabolismController>();
		}
	}
}
[HarmonyPatch(typeof(Player), "Load")]
internal static class PlayerLoadPatch
{
	private static void Postfix(Player __instance)
	{
		MetabolismController component = ((Component)__instance).GetComponent<MetabolismController>();
		if ((Object)(object)component != (Object)null)
		{
			component.LoadState();
		}
	}
}
[HarmonyPatch(typeof(Player), "Save")]
internal static class PlayerSavePatch
{
	private static void Prefix(Player __instance)
	{
		MetabolismController component = ((Component)__instance).GetComponent<MetabolismController>();
		if ((Object)(object)component != (Object)null)
		{
			component.SaveState();
		}
	}
}
[HarmonyPatch]
internal static class FoodTickPatch
{
	private static MethodBase TargetMethod()
	{
		return AccessTools.Method(typeof(Player), "UpdateFood", (Type[])null, (Type[])null);
	}

	private static void Prefix(Player __instance, out Dictionary<Food, float> __state)
	{
		__state = null;
		if (!((Object)(object)__instance != (Object)(object)Player.m_localPlayer))
		{
			__state = new Dictionary<Food, float>();
			List<Food> foods = __instance.GetFoods();
			for (int i = 0; i < foods.Count; i++)
			{
				__state[foods[i]] = foods[i].m_time;
			}
		}
	}

	private static void Postfix(Player __instance, Dictionary<Food, float> __state)
	{
		MetabolismController component = ((Component)__instance).GetComponent<MetabolismController>();
		if (!((Object)(object)component == (Object)null))
		{
			component.ApplyDigestion(__state);
			component.ApplySkillBonuses();
		}
	}
}
[HarmonyPatch(typeof(Player), "SetControls")]
internal static class PlayerControlsPatch
{
	private static void Prefix(Player __instance, ref Vector3 movedir, ref bool attack, ref bool attackHold, ref bool secondaryAttack, ref bool secondaryAttackHold, ref bool block, ref bool blockHold, ref bool jump, ref bool crouch, ref bool run, ref bool autoRun, ref bool dodge)
	{
		//IL_001a: 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)
		MetabolismController component = ((Component)__instance).GetComponent<MetabolismController>();
		if (!((Object)(object)component == (Object)null))
		{
			if (component.IsRelieving)
			{
				movedir = Vector3.zero;
				attack = false;
				attackHold = false;
				secondaryAttack = false;
				secondaryAttackHold = false;
				block = false;
				blockHold = false;
				jump = false;
				crouch = false;
				run = false;
				autoRun = false;
				dodge = false;
			}
			else if (component.IsOverfull)
			{
				jump = false;
				run = false;
				autoRun = false;
			}
		}
	}
}
[HarmonyPatch(typeof(Character), "Jump")]
internal static class JumpPatch
{
	private static bool Prefix(Character __instance)
	{
		Player val = (Player)(object)((__instance is Player) ? __instance : null);
		if ((Object)(object)val == (Object)null)
		{
			return true;
		}
		MetabolismController component = ((Component)val).GetComponent<MetabolismController>();
		if (!((Object)(object)component == (Object)null))
		{
			if (!component.IsOverfull)
			{
				return !component.IsRelieving;
			}
			return false;
		}
		return true;
	}
}
[HarmonyPatch(typeof(Player), "CanMove")]
internal static class CanMovePatch
{
	private static void Postfix(Player __instance, ref bool __result)
	{
		MetabolismController component = ((Component)__instance).GetComponent<MetabolismController>();
		if ((Object)(object)component != (Object)null && component.IsRelieving)
		{
			__result = false;
		}
	}
}
[HarmonyPatch]
internal static class RestedDurationPatch
{
	private struct OriginalDurations
	{
		internal float Base;

		internal float PerComfort;
	}

	private static MethodBase TargetMethod()
	{
		return AccessTools.Method(typeof(SE_Rested), "UpdateTTL", (Type[])null, (Type[])null);
	}

	private static void Prefix(SE_Rested __instance, out OriginalDurations __state)
	{
		//IL_0052: Unknown result type (might be due to invalid IL or missing references)
		__state = new OriginalDurations
		{
			Base = __instance.m_baseTTL,
			PerComfort = __instance.m_TTLPerComfortLevel
		};
		Character character = ((StatusEffect)__instance).m_character;
		Player val = (Player)(object)((character is Player) ? character : null);
		if (!((Object)(object)val == (Object)null) && !((Object)(object)val != (Object)(object)Player.m_localPlayer))
		{
			float skillLevel = ((Character)val).GetSkills().GetSkillLevel(Plugin.MetabolismSkill);
			float num = 1f + skillLevel * Plugin.RestedDurationPerSkillLevel.Value;
			__instance.m_baseTTL *= num;
			__instance.m_TTLPerComfortLevel *= num;
		}
	}

	private static void Postfix(SE_Rested __instance, OriginalDurations __state)
	{
		__instance.m_baseTTL = __state.Base;
		__instance.m_TTLPerComfortLevel = __state.PerComfort;
	}
}
[HarmonyPatch(typeof(Hud), "Awake")]
internal static class HudAwakePatch
{
	private static void Postfix(Hud __instance)
	{
		if (!GUIManager.IsHeadless())
		{
			MetabolismHud metabolismHud = ((Component)__instance).GetComponent<MetabolismHud>();
			if ((Object)(object)metabolismHud == (Object)null)
			{
				metabolismHud = ((Component)__instance).gameObject.AddComponent<MetabolismHud>();
			}
			metabolismHud.Initialize(__instance);
		}
	}
}
[HarmonyPatch]
internal static class ControlsSettingsPatch
{
	private static readonly Type SettingsType = AccessTools.TypeByName("Valheim.SettingsGui.KeyboardMouseSettings");

	private static readonly Type KeySettingType = AccessTools.TypeByName("Valheim.SettingsGui.KeySetting");

	private static MethodBase TargetMethod()
	{
		if (!(SettingsType == null))
		{
			return AccessTools.Method(SettingsType, "Initialize", (Type[])null, (Type[])null);
		}
		return null;
	}

	private static void Prefix(object __instance)
	{
		//IL_00e0: 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)
		//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_016b: Unknown result type (might be due to invalid IL or missing references)
		//IL_016d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0179: Unknown result type (might be due to invalid IL or missing references)
		//IL_017b: Unknown result type (might be due to invalid IL or missing references)
		if (Plugin.ToiletButton == null || KeySettingType == null)
		{
			return;
		}
		FieldInfo fieldInfo = AccessTools.Field(SettingsType, "m_keys");
		FieldInfo fieldInfo2 = AccessTools.Field(SettingsType, "m_keyRows");
		IList list = ((fieldInfo == null) ? null : (fieldInfo.GetValue(__instance) as IList));
		if (list == null || list.Count < 2)
		{
			return;
		}
		string name = Plugin.ToiletButton.Name;
		string text = "Joy!" + Plugin.ToiletButton.Name;
		bool flag = ContainsAction(list, name);
		bool flag2 = ContainsAction(list, text);
		if (flag && flag2)
		{
			return;
		}
		int num = ((fieldInfo2 == null) ? list.Count : ((int)fieldInfo2.GetValue(__instance)));
		int num2 = Mathf.Clamp(num - 1, 1, list.Count - 1);
		object template = list[num2];
		object previous = list[num2 - 1];
		Vector2 rowStep = Vector2.zero;
		object obj = (flag ? null : CloneSetting(template, previous, name, "$metabolism_control_keyboard", insertAfterTemplate: true, out rowStep));
		if (obj != null)
		{
			list.Insert(num2 + 1, obj);
		}
		object template2 = list[list.Count - 1];
		object previous2 = list[list.Count - 2];
		Vector2 rowStep2 = Vector2.zero;
		object obj2 = (flag2 ? null : CloneSetting(template2, previous2, text, "$metabolism_control_gamepad", insertAfterTemplate: false, out rowStep2));
		if (obj2 != null)
		{
			list.Add(obj2);
		}
		if (obj != null || obj2 != null)
		{
			if (rowStep == Vector2.zero)
			{
				rowStep = rowStep2;
			}
			if (fieldInfo2 != null)
			{
				fieldInfo2.SetValue(__instance, num + 1);
			}
			Plugin.LogInfo("Added keyboard and gamepad toilet bindings to Controls; actions: " + name + ", " + text);
		}
	}

	private static void Postfix(object __instance)
	{
		MonoBehaviour val = (MonoBehaviour)((__instance is MonoBehaviour) ? __instance : null);
		if ((Object)(object)val != (Object)null && (Object)(object)Plugin.Instance != (Object)null)
		{
			((MonoBehaviour)Plugin.Instance).StartCoroutine(EnableBindingsScrollAfterLayout(val));
		}
	}

	private static IEnumerator EnableBindingsScrollAfterLayout(MonoBehaviour settings)
	{
		while ((Object)(object)settings != (Object)null && !((Component)settings).gameObject.activeInHierarchy)
		{
			yield return null;
		}
		if (!((Object)(object)settings == (Object)null))
		{
			yield return null;
			Canvas.ForceUpdateCanvases();
			FieldInfo fieldInfo = AccessTools.Field(SettingsType, "m_keys");
			FieldInfo columnsField = AccessTools.Field(SettingsType, "m_keyCols");
			IList keys = ((fieldInfo == null) ? null : (fieldInfo.GetValue(settings) as IList));
			if (keys != null && keys.Count != 0)
			{
				MakeBindingsScrollable(keys[0], GetColumnCount(settings, columnsField));
				yield return (object)new WaitForEndOfFrame();
				MakeBindingsScrollable(keys[0], GetColumnCount(settings, columnsField));
			}
		}
	}

	private static int GetColumnCount(object settings, FieldInfo columnsField)
	{
		if (!(columnsField == null))
		{
			return Mathf.Max(1, (int)columnsField.GetValue(settings));
		}
		return 2;
	}

	private static bool ContainsAction(IList keys, string action)
	{
		FieldInfo fieldInfo = AccessTools.Field(KeySettingType, "m_keyName");
		for (int i = 0; i < keys.Count; i++)
		{
			if ((string)fieldInfo.GetValue(keys[i]) == action)
			{
				return true;
			}
		}
		return false;
	}

	private static object CloneSetting(object template, object previous, string action, string labelToken, bool insertAfterTemplate, out Vector2 rowStep)
	{
		//IL_0002: 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_004d: 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)
		//IL_0058: 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_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_0099: Unknown result type (might be due to invalid IL or missing references)
		rowStep = Vector2.zero;
		FieldInfo fieldInfo = AccessTools.Field(KeySettingType, "m_keyTransform");
		object? value = fieldInfo.GetValue(template);
		RectTransform val = (RectTransform)((value is RectTransform) ? value : null);
		object? value2 = fieldInfo.GetValue(previous);
		RectTransform val2 = (RectTransform)((value2 is RectTransform) ? value2 : null);
		if ((Object)(object)val == (Object)null || (Object)(object)val2 == (Object)null)
		{
			return null;
		}
		rowStep = val.anchoredPosition - val2.anchoredPosition;
		GameObject obj = Object.Instantiate<GameObject>(((Component)val).gameObject, ((Transform)val).parent);
		((Object)obj).name = "MetabolismControl_" + action;
		RectTransform component = obj.GetComponent<RectTransform>();
		component.anchoredPosition = val.anchoredPosition + rowStep;
		if (insertAfterTemplate)
		{
			((Transform)component).SetSiblingIndex(((Transform)val).GetSiblingIndex() + 1);
		}
		Button componentInChildren = obj.GetComponentInChildren<Button>(true);
		TMP_Text[] componentsInChildren = obj.GetComponentsInChildren<TMP_Text>(true);
		for (int i = 0; i < componentsInChildren.Length; i++)
		{
			if ((Object)(object)componentInChildren == (Object)null || !componentsInChildren[i].transform.IsChildOf(((Component)componentInChildren).transform))
			{
				componentsInChildren[i].text = Localization.instance.Localize(labelToken);
				break;
			}
		}
		object obj2 = Activator.CreateInstance(KeySettingType);
		AccessTools.Field(KeySettingType, "m_keyName").SetValue(obj2, action);
		fieldInfo.SetValue(obj2, component);
		AccessTools.Field(KeySettingType, "m_blockedButtons").SetValue(obj2, new KeyCode[0]);
		return obj2;
	}

	private static void MakeBindingsScrollable(object template, int columnCount)
	{
		//IL_0062: 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_0084: 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_014f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0154: Unknown result type (might be due to invalid IL or missing references)
		//IL_0165: Unknown result type (might be due to invalid IL or missing references)
		//IL_016a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0111: Unknown result type (might be due to invalid IL or missing references)
		//IL_0129: Unknown result type (might be due to invalid IL or missing references)
		object? value = AccessTools.Field(KeySettingType, "m_keyTransform").GetValue(template);
		RectTransform val = (RectTransform)((value is RectTransform) ? value : null);
		if ((Object)(object)val == (Object)null)
		{
			return;
		}
		ScrollRect componentInParent = ((Component)val).GetComponentInParent<ScrollRect>();
		RectTransform val2 = (RectTransform)(((Object)(object)componentInParent == (Object)null) ? ((object)/*isinst with value type is only supported in some contexts*/) : ((object)componentInParent.content));
		if ((Object)(object)val2 == (Object)null)
		{
			return;
		}
		GridLayoutGroup component = ((Component)val2).GetComponent<GridLayoutGroup>();
		float num = 1f;
		Rect rect = val2.rect;
		float num2 = ((Rect)(ref rect)).height;
		if ((Object)(object)component != (Object)null)
		{
			num = Mathf.Max(1f, component.cellSize.y + component.spacing.y);
			int num3 = 0;
			for (int i = 0; i < ((Transform)val2).childCount; i++)
			{
				Transform child = ((Transform)val2).GetChild(i);
				LayoutElement component2 = ((Component)child).GetComponent<LayoutElement>();
				if (((Component)child).gameObject.activeSelf && ((Object)(object)component2 == (Object)null || !component2.ignoreLayout))
				{
					num3++;
				}
			}
			int num4 = Mathf.CeilToInt((float)num3 / (float)Mathf.Max(1, columnCount));
			num2 = (float)((LayoutGroup)component).padding.vertical + (float)num4 * component.cellSize.y + (float)Mathf.Max(0, num4 - 1) * component.spacing.y;
		}
		float height;
		if (!((Object)(object)componentInParent != (Object)null) || !((Object)(object)componentInParent.viewport != (Object)null))
		{
			rect = val2.rect;
			height = ((Rect)(ref rect)).height;
		}
		else
		{
			rect = componentInParent.viewport.rect;
			height = ((Rect)(ref rect)).height;
		}
		float num5 = height;
		float num6 = Mathf.Max(num2, num5 + num);
		val2.SetSizeWithCurrentAnchors((Axis)1, num6);
		if (!((Object)(object)componentInParent == (Object)null))
		{
			((Behaviour)componentInParent).enabled = true;
			componentInParent.vertical = true;
			componentInParent.scrollSensitivity = Mathf.Max(componentInParent.scrollSensitivity, num);
			componentInParent.verticalScrollbarVisibility = (ScrollbarVisibility)0;
			if ((Object)(object)componentInParent.verticalScrollbar != (Object)null)
			{
				((Component)componentInParent.verticalScrollbar).transform.SetAsLastSibling();
				((Component)componentInParent.verticalScrollbar).gameObject.SetActive(true);
			}
			LayoutRebuilder.ForceRebuildLayoutImmediate(val2);
			Canvas.ForceUpdateCanvases();
			componentInParent.StopMovement();
			componentInParent.verticalNormalizedPosition = 1f;
		}
	}
}
[BepInPlugin("com.igentuman.valheim.metabolism", "Metabolism", "1.0.1")]
[BepInDependency(/*Could not decode attribute arguments.*/)]
[NetworkCompatibility(/*Could not decode attribute arguments.*/)]
public sealed class Plugin : BaseUnityPlugin
{
	public const string Guid = "com.igentuman.valheim.metabolism";

	public const string Name = "Metabolism";

	public const string Version = "1.0.1";

	public const string OverfullEffectName = "SE_MetabolismOverfull";

	internal static ConfigEntry<float> InitialCapacity;

	internal static ConfigEntry<float> CapacityPerCycle;

	internal static ConfigEntry<float> MaximumCapacity;

	internal static ConfigEntry<float> WastePerFood;

	internal static ConfigEntry<float> ReadyThreshold;

	internal static ConfigEntry<float> ToiletDuration;

	internal static ConfigEntry<float> SkillExperience;

	internal static ConfigEntry<float> HealthPerSkillLevel;

	internal static ConfigEntry<float> StaminaPerSkillLevel;

	internal static ConfigEntry<float> RestedDurationPerSkillLevel;

	internal static ConfigEntry<float> PrivacyRadius;

	internal static ConfigEntry<int> RequiredBlockedDirections;

	private Harmony _harmony;

	internal static SkillType MetabolismSkill { get; private set; }

	internal static ButtonConfig ToiletButton { get; private set; }

	internal static Sprite IntestineIcon { get; private set; }

	internal static Plugin Instance { get; private set; }

	private void Awake()
	{
		//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_0051: Unknown result type (might be due to invalid IL or missing references)
		//IL_0061: Expected O, but got Unknown
		//IL_005c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0066: Unknown result type (might be due to invalid IL or missing references)
		//IL_006b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0076: Unknown result type (might be due to invalid IL or missing references)
		//IL_007e: 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_0096: Expected O, but got Unknown
		//IL_00de: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
		//IL_0107: Unknown result type (might be due to invalid IL or missing references)
		//IL_0111: Expected O, but got Unknown
		//IL_0118: Unknown result type (might be due to invalid IL or missing references)
		//IL_0122: Expected O, but got Unknown
		Instance = this;
		BindConfiguration();
		AddLocalization();
		IntestineIcon = IconFactory.CreateIntestineIcon();
		MetabolismSkill = SkillManager.Instance.AddSkill(new SkillConfig
		{
			Identifier = "com.igentuman.valheim.metabolism.skill",
			Name = "$skill_metabolism",
			Description = "$skill_metabolism_description",
			Icon = IntestineIcon,
			IncreaseStep = 1f
		});
		ToiletButton = new ButtonConfig
		{
			Name = "MetabolismRelieve",
			Key = (KeyCode)110,
			GamepadButton = (GamepadButton)16,
			HintToken = "$metabolism_action"
		};
		InputManager.Instance.AddButton("com.igentuman.valheim.metabolism", ToiletButton);
		StatusEffect val = ScriptableObject.CreateInstance<StatusEffect>();
		((Object)val).name = "SE_MetabolismOverfull";
		val.m_name = "$se_metabolism_overfull";
		val.m_tooltip = "$se_metabolism_overfull_tooltip";
		val.m_icon = IntestineIcon;
		val.m_startMessageType = (MessageType)2;
		val.m_startMessage = "$metabolism_overfull_start";
		val.m_stopMessageType = (MessageType)1;
		val.m_stopMessage = "$metabolism_overfull_stop";
		ItemManager.Instance.AddStatusEffect(new CustomStatusEffect(val, false));
		_harmony = new Harmony("com.igentuman.valheim.metabolism");
		_harmony.PatchAll();
		((BaseUnityPlugin)this).Logger.LogInfo((object)"Metabolism loaded");
	}

	internal static void LogInfo(string message)
	{
		if ((Object)(object)Instance != (Object)null)
		{
			((BaseUnityPlugin)Instance).Logger.LogInfo((object)message);
		}
	}

	internal static void LogWarning(string message)
	{
		if ((Object)(object)Instance != (Object)null)
		{
			((BaseUnityPlugin)Instance).Logger.LogWarning((object)message);
		}
	}

	private void Update()
	{
		Player localPlayer = Player.m_localPlayer;
		if (!((Object)(object)localPlayer == (Object)null) && ToiletButton != null && ZInput.GetButtonDown(ToiletButton.Name))
		{
			MetabolismController component = ((Component)localPlayer).GetComponent<MetabolismController>();
			if ((Object)(object)component != (Object)null)
			{
				component.TryStartRelieving();
			}
		}
	}

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

	private void BindConfiguration()
	{
		//IL_01e5: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ef: Expected O, but got Unknown
		InitialCapacity = Bind("Bowel", "Initial capacity", 100f, 10f, 1000f, "Initial bowel capacity in waste units.");
		CapacityPerCycle = Bind("Bowel", "Capacity gained per cycle", 2f, 0f, 100f, "Permanent capacity gained after a completed toilet cycle.");
		MaximumCapacity = Bind("Bowel", "Maximum capacity", 200f, 10f, 5000f, "Maximum bowel capacity.");
		WastePerFood = Bind("Digestion", "Waste per food", 25f, 0f, 500f, "Waste produced while one food is fully digested.");
		ReadyThreshold = Bind("Toilet", "Ready threshold", 0.85f, 0.1f, 1f, "Minimum bowel fullness required to use the toilet (0-1).");
		ToiletDuration = Bind("Toilet", "Duration", 10f, 1f, 60f, "Seconds required to empty the bowel.");
		SkillExperience = Bind("Skill", "Experience per cycle", 1f, 0f, 100f, "Metabolism experience awarded for a completed toilet cycle.");
		HealthPerSkillLevel = Bind("Skill", "Health multiplier per level", 0.005f, 0f, 0.1f, "Extra maximum-health multiplier for each Metabolism level.");
		StaminaPerSkillLevel = Bind("Skill", "Stamina multiplier per level", 0.005f, 0f, 0.1f, "Extra maximum-stamina multiplier for each Metabolism level.");
		RestedDurationPerSkillLevel = Bind("Skill", "Rested duration multiplier per level", 0.003f, 0f, 0.1f, "Extra Rested-effect duration multiplier for each Metabolism level.");
		PrivacyRadius = Bind("Privacy", "Wall scan radius", 2.6f, 1f, 8f, "Maximum distance used when checking for privacy walls.");
		RequiredBlockedDirections = ((BaseUnityPlugin)this).Config.Bind<int>("Privacy", "Required blocked directions", 5, new ConfigDescription("Number of blocked directions out of eight required for an enclosure.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(3, 8), Array.Empty<object>()));
	}

	private ConfigEntry<float> Bind(string section, string key, float value, float min, float max, string description)
	{
		//IL_0019: Unknown result type (might be due to invalid IL or missing references)
		//IL_0023: Expected O, but got Unknown
		return ((BaseUnityPlugin)this).Config.Bind<float>(section, key, value, new ConfigDescription(description, (AcceptableValueBase)(object)new AcceptableValueRange<float>(min, max), Array.Empty<object>()));
	}

	private static void AddLocalization()
	{
		CustomLocalization localization = LocalizationManager.Instance.GetLocalization();
		string text = "English";
		localization.AddTranslation(ref text, new Dictionary<string, string>
		{
			{ "skill_metabolism", "Metabolism" },
			{ "skill_metabolism_description", "Improves health, stamina and the duration of Rested." },
			{ "se_metabolism_overfull", "Overfull" },
			{ "se_metabolism_overfull_tooltip", "Your bowel is full. You cannot run or jump, but walking does not drain stamina." },
			{ "metabolism_action", "Relieve yourself" },
			{ "metabolism_control_keyboard", "Relieve yourself (keyboard)" },
			{ "metabolism_control_gamepad", "Relieve yourself (gamepad)" },
			{ "metabolism_not_ready", "You do not need to go yet." },
			{ "metabolism_bad_state", "You cannot do that right now." },
			{ "metabolism_no_privacy", "Not enough privacy. Find walls around you or a nearby bush." },
			{ "metabolism_privacy_enclosure", "This enclosure is private enough." },
			{ "metabolism_privacy_bush", "This bush provides enough privacy." },
			{ "metabolism_started", "You squat down and begin..." },
			{ "metabolism_progress", "Relieving yourself" },
			{ "metabolism_finished", "Much better." },
			{ "metabolism_overfull_start", "Your bowel is completely full!" },
			{ "metabolism_overfull_stop", "You can move freely again." },
			{ "metabolism_ready", "Ready" }
		});
		text = "Russian";
		localization.AddTranslation(ref text, new Dictionary<string, string>
		{
			{ "skill_metabolism", "Метаболизм" },
			{ "skill_metabolism_description", "Увеличивает запас здоровья и выносливости, а также длительность Бодрости." },
			{ "se_metabolism_overfull", "Переполнение" },
			{ "se_metabolism_overfull_tooltip", "Кишечник переполнен. Нельзя бегать и прыгать, но ходьба не расходует выносливость." },
			{ "metabolism_action", "Сходить в туалет" },
			{ "metabolism_control_keyboard", "Сходить в туалет (клавиатура)" },
			{ "metabolism_control_gamepad", "Сходить в туалет (геймпад)" },
			{ "metabolism_not_ready", "Пока ещё рано идти в туалет." },
			{ "metabolism_bad_state", "Сейчас это сделать нельзя." },
			{ "metabolism_no_privacy", "Недостаточно уединённое место. Нужны стены вокруг или куст рядом." },
			{ "metabolism_privacy_enclosure", "Здесь достаточно уединённо." },
			{ "metabolism_privacy_bush", "Куст даёт достаточно укрытия." },
			{ "metabolism_started", "Вы присели и начали процесс..." },
			{ "metabolism_progress", "Поход в туалет" },
			{ "metabolism_finished", "Так гораздо лучше." },
			{ "metabolism_overfull_start", "Кишечник полностью переполнен!" },
			{ "metabolism_overfull_stop", "Теперь можно свободно двигаться." },
			{ "metabolism_ready", "Можно идти" }
		});
	}
}
internal enum PrivacyKind
{
	None,
	Enclosure,
	Bush
}
internal static class PrivacyEvaluator
{
	private static readonly string[] BushWords = new string[6] { "bush", "shrub", "raspberry", "blueberry", "cloudberry", "vegetation" };

	internal static PrivacyKind Evaluate(Player player)
	{
		//IL_0006: 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_001f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0029: 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_0033: Unknown result type (might be due to invalid IL or missing references)
		//IL_0061: 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)
		if (HasNearbyBush(((Component)player).transform.position))
		{
			return PrivacyKind.Bush;
		}
		Vector3 val = ((Component)player).transform.position + Vector3.up * 1.05f;
		int num = 0;
		Vector3 val2 = default(Vector3);
		RaycastHit val3 = default(RaycastHit);
		for (int i = 0; i < 8; i++)
		{
			float num2 = (float)i * (float)Math.PI * 0.25f;
			((Vector3)(ref val2))..ctor(Mathf.Sin(num2), 0f, Mathf.Cos(num2));
			if (Physics.Raycast(val, val2, ref val3, Plugin.PrivacyRadius.Value, -5, (QueryTriggerInteraction)1) && (Object)(object)((RaycastHit)(ref val3)).collider != (Object)null && IsPrivacySurface(((RaycastHit)(ref val3)).collider))
			{
				num++;
			}
		}
		if (num < Plugin.RequiredBlockedDirections.Value)
		{
			return PrivacyKind.None;
		}
		return PrivacyKind.Enclosure;
	}

	private static bool HasNearbyBush(Vector3 position)
	{
		//IL_0000: Unknown result type (might be due to invalid IL or missing references)
		//IL_0001: 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_0010: Unknown result type (might be due to invalid IL or missing references)
		Collider[] array = Physics.OverlapSphere(position + Vector3.up * 0.7f, 1.8f, -5, (QueryTriggerInteraction)2);
		for (int i = 0; i < array.Length; i++)
		{
			Transform val = ((Component)array[i]).transform;
			int num = 0;
			while ((Object)(object)val != (Object)null && num < 4)
			{
				string text = ((Object)val).name.ToLowerInvariant();
				for (int j = 0; j < BushWords.Length; j++)
				{
					if (text.IndexOf(BushWords[j], StringComparison.Ordinal) >= 0)
					{
						return true;
					}
				}
				num++;
				val = val.parent;
			}
		}
		return false;
	}

	private static bool IsPrivacySurface(Collider collider)
	{
		if ((Object)(object)((Component)collider).GetComponentInParent<Piece>() != (Object)null)
		{
			return true;
		}
		string text = ((Object)((Component)collider).transform.root).name.ToLowerInvariant();
		if (!text.Contains("wall") && !text.Contains("wood") && !text.Contains("stone") && !text.Contains("rock") && !text.Contains("crypt"))
		{
			return text.Contains("dungeon");
		}
		return true;
	}
}