Decompiled source of Full Belly v4.0.1

tony4twentys-Full Belly.dll

Decompiled a month ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using PEAKLib.Core;
using Peak.Afflictions;
using Photon.Pun;
using Photon.Realtime;
using PhotonCustomPropsUtils;
using TMPro;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: AssemblyTitle("Full Belly")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Full Belly")]
[assembly: AssemblyCopyright("Copyright ©  2025")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("940386f2-efcb-440c-a8e5-aa2361ab032b")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
}
namespace FullBellyMod
{
	internal static class BloatedEffects
	{
		[HarmonyPatch(typeof(CharacterMovement), "GetMovementForce")]
		private static class GetMovementForce_Patch
		{
			private static void Postfix(CharacterMovement __instance, ref float __result)
			{
				if (!((Object)(object)__instance == (Object)null) && WantMoveSlow(__instance.character))
				{
					__result *= MoveMultiplier();
				}
			}
		}

		[HarmonyPatch(typeof(Character), "UseStamina")]
		private static class UseStamina_Patch
		{
			private static float _pendingUsage;

			private static void Prefix(Character __instance, ref float usage)
			{
				_pendingUsage = 0f;
				if ((Object)(object)__instance == (Object)null || (Object)(object)__instance != (Object)(object)Character.localCharacter || !FullBellyPlugin.isHandshakeValid)
				{
					return;
				}
				FullBellyManager instance = FullBellyManager.instance;
				if (!((Object)(object)instance == (Object)null))
				{
					if (instance.GetBloatedAmount() > 0f && FullBellyPlugin.syncedEnableBloatedStaminaCost)
					{
						float num = Mathf.Max(1f, FullBellyPlugin.syncedBloatedStaminaCostMultiplier);
						usage *= num;
					}
					_pendingUsage = usage;
				}
			}

			private static void Postfix(Character __instance, bool __result)
			{
				float pendingUsage = _pendingUsage;
				_pendingUsage = 0f;
				if (!__result || pendingUsage <= 0f || (Object)(object)__instance == (Object)null || (Object)(object)__instance != (Object)(object)Character.localCharacter || !FullBellyPlugin.isHandshakeValid || !FullBellyPlugin.syncedEnableStaminaSpeedsDecay)
				{
					return;
				}
				FullBellyManager instance = FullBellyManager.instance;
				if (!((Object)(object)instance == (Object)null) && !(instance.foodConsumptionAmount <= 0f))
				{
					float num = FullBellyPlugin.syncedStaminaDecayBoost * pendingUsage;
					if (num > 0f)
					{
						instance.ApplyExtraDecay(num, restartDecay: false);
					}
				}
			}
		}

		private static Character _climbAppliedTo;

		private static float _savedClimbMod = 1f;

		private static float _savedRopeMod = 1f;

		private static float _savedVineMod = 1f;

		private static bool _haveClimbSaved;

		internal static void Tick()
		{
			if (!FullBellyPlugin.isHandshakeValid)
			{
				RestoreClimb();
				return;
			}
			Character localCharacter = Character.localCharacter;
			FullBellyManager instance = FullBellyManager.instance;
			bool flag = (Object)(object)instance != (Object)null && instance.GetBloatedAmount() > 0f;
			if (flag && FullBellyPlugin.syncedEnableBloatedSlowdown && localCharacter?.refs != null)
			{
				ApplyClimbSlow(localCharacter);
			}
			else
			{
				RestoreClimb();
			}
			if (flag && FullBellyPlugin.syncedEnableSprintFartBoost && FullBellyPlugin.syncedEnableFarting && (Object)(object)localCharacter != (Object)null && (Object)(object)localCharacter.data != (Object)null && localCharacter.data.isSprinting)
			{
				float syncedSprintFartChancePerSecond = FullBellyPlugin.syncedSprintFartChancePerSecond;
				if (syncedSprintFartChancePerSecond > 0f && Random.value < syncedSprintFartChancePerSecond * Time.deltaTime)
				{
					instance.TryFartPublic(localCharacter);
				}
			}
		}

		internal static void Shutdown()
		{
			RestoreClimb();
		}

		private static bool WantMoveSlow(Character character)
		{
			if (!FullBellyPlugin.isHandshakeValid || !FullBellyPlugin.syncedEnableBloatedSlowdown)
			{
				return false;
			}
			if ((Object)(object)character == (Object)null || (Object)(object)character != (Object)(object)Character.localCharacter)
			{
				return false;
			}
			FullBellyManager instance = FullBellyManager.instance;
			return (Object)(object)instance != (Object)null && instance.GetBloatedAmount() > 0f;
		}

		private static float MoveMultiplier()
		{
			return Mathf.Clamp(FullBellyPlugin.syncedBloatedMoveSpeedMultiplier, 0.1f, 1f);
		}

		private static float ClimbMultiplier()
		{
			return Mathf.Clamp(FullBellyPlugin.syncedBloatedClimbSpeedMultiplier, 0.1f, 1f);
		}

		private static void ApplyClimbSlow(Character local)
		{
			if ((Object)(object)_climbAppliedTo != (Object)(object)local || !_haveClimbSaved)
			{
				RestoreClimb();
				_savedClimbMod = (((Object)(object)local.refs.climbing != (Object)null) ? local.refs.climbing.climbSpeedMod : 1f);
				_savedRopeMod = (((Object)(object)local.refs.ropeHandling != (Object)null) ? local.refs.ropeHandling.climbSpeedMod : 1f);
				_savedVineMod = (((Object)(object)local.refs.vineClimbing != (Object)null) ? local.refs.vineClimbing.climbSpeedMod : 1f);
				_haveClimbSaved = true;
				_climbAppliedTo = local;
			}
			float num = ClimbMultiplier();
			if ((Object)(object)local.refs.climbing != (Object)null)
			{
				local.refs.climbing.climbSpeedMod = _savedClimbMod * num;
			}
			if ((Object)(object)local.refs.ropeHandling != (Object)null)
			{
				local.refs.ropeHandling.climbSpeedMod = _savedRopeMod * num;
			}
			if ((Object)(object)local.refs.vineClimbing != (Object)null)
			{
				local.refs.vineClimbing.climbSpeedMod = _savedVineMod * num;
			}
		}

		private static void RestoreClimb()
		{
			if (!_haveClimbSaved || (Object)(object)_climbAppliedTo == (Object)null)
			{
				_haveClimbSaved = false;
				_climbAppliedTo = null;
				return;
			}
			try
			{
				if ((Object)(object)_climbAppliedTo.refs?.climbing != (Object)null)
				{
					_climbAppliedTo.refs.climbing.climbSpeedMod = _savedClimbMod;
				}
				if ((Object)(object)_climbAppliedTo.refs?.ropeHandling != (Object)null)
				{
					_climbAppliedTo.refs.ropeHandling.climbSpeedMod = _savedRopeMod;
				}
				if ((Object)(object)_climbAppliedTo.refs?.vineClimbing != (Object)null)
				{
					_climbAppliedTo.refs.vineClimbing.climbSpeedMod = _savedVineMod;
				}
			}
			catch
			{
			}
			_haveClimbSaved = false;
			_climbAppliedTo = null;
		}
	}
	internal static class BloatedUi
	{
		internal const STATUSTYPE BloatedType = (STATUSTYPE)100;

		internal static readonly Color DefaultFillColor = new Color(1f, 0.5f, 0.8f, 1f);

		internal static readonly Color DefaultOutlineColor = new Color(1f, 0.7f, 0.9f, 0.6f);

		internal static readonly Color DefaultIconColor = new Color(1f, 0.9f, 1f, 1f);

		private static BarAffliction _bar;

		private static StaminaBar _staminaBar;

		private static float _lastDrivenAmount = -1f;

		private static readonly FieldInfo AfflictionsField = typeof(StaminaBar).GetField("afflictions", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

		internal static Color FillColor => ColorUtil.ParseHex(FullBellyPlugin.cfgBloatedFillColor?.Value, DefaultFillColor);

		internal static Color OutlineColor => ColorUtil.ParseHex(FullBellyPlugin.cfgBloatedOutlineColor?.Value, DefaultOutlineColor);

		internal static Color IconColor => ColorUtil.ParseHex(FullBellyPlugin.cfgBloatedIconColor?.Value, DefaultIconColor);

		internal static BarAffliction Bar => _bar;

		internal static bool IsBloatedType(STATUSTYPE type)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0003: Invalid comparison between Unknown and I4
			return (int)type == 100;
		}

		internal static void Invalidate()
		{
			_bar = null;
			_staminaBar = null;
			_lastDrivenAmount = -1f;
		}

		internal static void EnsureOnStaminaBar(StaminaBar staminaBar, ManualLogSource logger)
		{
			//IL_005d: 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_010f: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)staminaBar == (Object)null)
			{
				return;
			}
			_staminaBar = staminaBar;
			if ((Object)(object)_bar != (Object)null && Object.op_Implicit((Object)(object)_bar) && ((Component)_bar).transform.IsChildOf(((Component)staminaBar).transform.parent))
			{
				_bar.afflictionType = (STATUSTYPE)100;
				return;
			}
			_bar = FindExistingBloated(staminaBar);
			if ((Object)(object)_bar != (Object)null)
			{
				_bar.afflictionType = (STATUSTYPE)100;
				StyleBar(_bar);
				return;
			}
			BarAffliction val = FindTemplate(staminaBar);
			if ((Object)(object)val == (Object)null)
			{
				if (logger != null)
				{
					logger.LogWarning((object)"[Full Belly] No BarAffliction template for Bloated UI.");
				}
				return;
			}
			GameObject val2 = Object.Instantiate<GameObject>(((Component)val).gameObject, ((Component)val).transform.parent);
			((Object)val2).name = "BarAffliction_Bloated";
			BarAffliction component = val2.GetComponent<BarAffliction>();
			if ((Object)(object)component == (Object)null)
			{
				Object.Destroy((Object)(object)val2);
				return;
			}
			component.afflictionType = (STATUSTYPE)100;
			component.size = 0f;
			component.width = 0f;
			StyleBar(component);
			Transform val3 = val2.transform.Find("StaminaInfo");
			if ((Object)(object)val3 != (Object)null)
			{
				Object.Destroy((Object)(object)((Component)val3).gameObject);
			}
			val2.SetActive(true);
			_bar = component;
			if (logger != null)
			{
				logger.LogInfo((object)"[Full Belly] Bloated bar injected before StaminaBar.Start cache.");
			}
		}

		internal static void EnsureInCachedAfflictions(StaminaBar staminaBar, ManualLogSource logger)
		{
			if (!((Object)(object)staminaBar == (Object)null))
			{
				EnsureOnStaminaBar(staminaBar, logger);
				RefreshAfflictionsArray(staminaBar, logger);
			}
		}

		internal static IEnumerator EnsureCreated(ManualLogSource logger)
		{
			for (int i = 0; i < 100; i++)
			{
				if (!((Object)(object)GUIManager.instance?.bar == (Object)null))
				{
					break;
				}
				yield return null;
			}
			StaminaBar staminaBar = GUIManager.instance?.bar;
			if (!((Object)(object)staminaBar == (Object)null))
			{
				EnsureOnStaminaBar(staminaBar, logger);
				RefreshAfflictionsArray(staminaBar, logger);
			}
		}

		private static BarAffliction FindExistingBloated(StaminaBar staminaBar)
		{
			Transform val = (((Object)(object)((Component)staminaBar).transform.parent != (Object)null) ? ((Component)staminaBar).transform.parent : ((Component)staminaBar).transform);
			BarAffliction[] componentsInChildren = ((Component)val).GetComponentsInChildren<BarAffliction>(true);
			foreach (BarAffliction val2 in componentsInChildren)
			{
				if ((Object)(object)val2 != (Object)null && ((Object)((Component)val2).gameObject).name == "BarAffliction_Bloated")
				{
					return val2;
				}
			}
			return null;
		}

		private static BarAffliction FindTemplate(StaminaBar staminaBar)
		{
			Transform val = (((Object)(object)((Component)staminaBar).transform.parent != (Object)null) ? ((Component)staminaBar).transform.parent : ((Component)staminaBar).transform);
			BarAffliction[] componentsInChildren = ((Component)val).GetComponentsInChildren<BarAffliction>(true);
			foreach (BarAffliction val2 in componentsInChildren)
			{
				if ((Object)(object)val2 != (Object)null && ((Object)((Component)val2).gameObject).name != "BarAffliction_Bloated")
				{
					return val2;
				}
			}
			return null;
		}

		private static void RefreshAfflictionsArray(StaminaBar staminaBar, ManualLogSource logger)
		{
			if ((Object)(object)staminaBar == (Object)null || AfflictionsField == null || (Object)(object)_bar == (Object)null)
			{
				return;
			}
			try
			{
				if (AfflictionsField.GetValue(staminaBar) is BarAffliction[] array)
				{
					for (int i = 0; i < array.Length; i++)
					{
						if ((Object)(object)array[i] == (Object)(object)_bar)
						{
							return;
						}
					}
				}
				Transform val = (((Object)(object)((Component)staminaBar).transform.parent != (Object)null) ? ((Component)staminaBar).transform.parent : ((Component)staminaBar).transform);
				BarAffliction[] componentsInChildren = ((Component)val).GetComponentsInChildren<BarAffliction>(true);
				List<BarAffliction> list = new List<BarAffliction>(componentsInChildren.Length);
				BarAffliction val2 = null;
				foreach (BarAffliction val3 in componentsInChildren)
				{
					if (!((Object)(object)val3 == (Object)null))
					{
						if ((Object)(object)val3 == (Object)(object)_bar || ((Object)((Component)val3).gameObject).name == "BarAffliction_Bloated")
						{
							val2 = val3;
						}
						else
						{
							list.Add(val3);
						}
					}
				}
				if ((Object)(object)val2 != (Object)null)
				{
					list.Add(val2);
				}
				AfflictionsField.SetValue(staminaBar, list.ToArray());
			}
			catch (Exception ex)
			{
				if (logger != null)
				{
					logger.LogWarning((object)("[Full Belly] Failed to refresh StaminaBar.afflictions: " + ex.Message));
				}
			}
		}

		internal static void Drive(float bloatedAmount)
		{
			//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_bar == (Object)null || (Object)(object)_staminaBar == (Object)null)
			{
				return;
			}
			if (Mathf.Abs(bloatedAmount - _lastDrivenAmount) < 0.0005f && ((((Component)_bar).gameObject.activeSelf && bloatedAmount > 0.01f) || (!((Component)_bar).gameObject.activeSelf && bloatedAmount <= 0.01f)))
			{
				_bar.UpdateAffliction(_staminaBar);
				return;
			}
			_lastDrivenAmount = bloatedAmount;
			if (bloatedAmount > 0.01f)
			{
				float num = _staminaBar.fullBar.sizeDelta.x * bloatedAmount;
				if (num < _staminaBar.minAfflictionWidth)
				{
					num = _staminaBar.minAfflictionWidth;
				}
				_bar.size = num;
				if (!((Component)_bar).gameObject.activeSelf)
				{
					((Component)_bar).gameObject.SetActive(true);
				}
			}
			else
			{
				_bar.size = 0f;
				if (((Component)_bar).gameObject.activeSelf)
				{
					((Component)_bar).gameObject.SetActive(false);
				}
			}
			_bar.UpdateAffliction(_staminaBar);
		}

		private static void StyleBar(BarAffliction bar)
		{
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_022b: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)bar == (Object)null)
			{
				return;
			}
			if ((Object)(object)bar.icon != (Object)null)
			{
				Sprite bloatedIcon = SpriteLoader.BloatedIcon;
				if ((Object)(object)bloatedIcon != (Object)null && (Object)(object)bloatedIcon.texture != (Object)null)
				{
					bar.icon.sprite = bloatedIcon;
				}
				((Graphic)bar.icon).color = IconColor;
			}
			if ((Object)(object)bar.rtf == (Object)null)
			{
				return;
			}
			Image[] componentsInChildren = ((Component)bar.rtf).GetComponentsInChildren<Image>(true);
			Sprite checkeredOverlay = SpriteLoader.CheckeredOverlay;
			bool flag = (Object)(object)checkeredOverlay != (Object)null && (Object)(object)checkeredOverlay.texture != (Object)null;
			Image[] array = componentsInChildren;
			foreach (Image val in array)
			{
				if ((Object)(object)val == (Object)null || (Object)(object)val == (Object)(object)bar.icon)
				{
					continue;
				}
				string name = ((Object)((Component)val).gameObject).name;
				bool flag2 = (Object)(object)((Component)val).gameObject == (Object)(object)((Component)bar.rtf).gameObject || name.IndexOf("Fill", StringComparison.OrdinalIgnoreCase) >= 0 || name.IndexOf("Background", StringComparison.OrdinalIgnoreCase) >= 0 || name.Equals("Bar", StringComparison.OrdinalIgnoreCase) || name.IndexOf("Base", StringComparison.OrdinalIgnoreCase) >= 0;
				bool flag3 = name.IndexOf("Dither", StringComparison.OrdinalIgnoreCase) >= 0 || name.IndexOf("Stripe", StringComparison.OrdinalIgnoreCase) >= 0 || name.IndexOf("Diagonal", StringComparison.OrdinalIgnoreCase) >= 0;
				bool flag4 = name.IndexOf("Outline", StringComparison.OrdinalIgnoreCase) >= 0 || name.IndexOf("Shadow", StringComparison.OrdinalIgnoreCase) >= 0 || name.IndexOf("Glow", StringComparison.OrdinalIgnoreCase) >= 0;
				if (flag2)
				{
					if (flag)
					{
						val.sprite = checkeredOverlay;
						val.type = (Type)2;
						val.preserveAspect = false;
					}
					((Graphic)val).color = FillColor;
					((Behaviour)val).enabled = true;
				}
				else if (flag3)
				{
					((Behaviour)val).enabled = false;
				}
				else if (flag4)
				{
					((Graphic)val).color = OutlineColor;
					((Behaviour)val).enabled = true;
				}
			}
		}

		internal static void RefreshStyle()
		{
			if ((Object)(object)_bar != (Object)null && Object.op_Implicit((Object)(object)_bar))
			{
				StyleBar(_bar);
			}
		}
	}
	internal static class ColorUtil
	{
		internal static Color ParseHex(string hex, Color fallback)
		{
			//IL_000b: 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_00cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b6: 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)
			if (string.IsNullOrEmpty(hex))
			{
				return fallback;
			}
			hex = hex.Trim();
			if (hex.StartsWith("#"))
			{
				hex = hex.Substring(1);
			}
			try
			{
				if (hex.Length == 6 || hex.Length == 8)
				{
					byte b = byte.Parse(hex.Substring(0, 2), NumberStyles.HexNumber);
					byte b2 = byte.Parse(hex.Substring(2, 2), NumberStyles.HexNumber);
					byte b3 = byte.Parse(hex.Substring(4, 2), NumberStyles.HexNumber);
					byte b4 = ((hex.Length == 8) ? byte.Parse(hex.Substring(6, 2), NumberStyles.HexNumber) : byte.MaxValue);
					return Color32.op_Implicit(new Color32(b, b2, b3, b4));
				}
			}
			catch
			{
			}
			return fallback;
		}
	}
	internal static class FoodRestoreTracker
	{
		private static float _hungerBeforeAction;

		private static int _lastAppliedItemId;

		private static float _lastAppliedTime = -999f;

		private const float ConsumeDedupeSeconds = 2f;

		internal static void CaptureHungerBefore(Character character)
		{
			_hungerBeforeAction = 0f;
			if (!((Object)(object)character?.refs?.afflictions == (Object)null))
			{
				_hungerBeforeAction = Mathf.Max(0f, character.refs.afflictions.GetCurrentStatus((STATUSTYPE)1));
			}
		}

		internal static void ApplyFromItemAction(Item item, float restoreAmount)
		{
			if ((Object)(object)item == (Object)null || restoreAmount <= 0f)
			{
				return;
			}
			Character holderCharacter = item.holderCharacter;
			if ((Object)(object)holderCharacter == (Object)null || (Object)(object)((MonoBehaviourPun)holderCharacter).photonView == (Object)null || !((MonoBehaviourPun)holderCharacter).photonView.IsMine || (Object)(object)holderCharacter != (Object)(object)Character.localCharacter)
			{
				return;
			}
			float hungerBeforeAction = _hungerBeforeAction;
			_hungerBeforeAction = 0f;
			float num = restoreAmount;
			if (FullBellyPlugin.syncedClearHungerBeforeFullness && hungerBeforeAction > 0f)
			{
				num = Mathf.Max(0f, restoreAmount - hungerBeforeAction);
			}
			MarkApplied(item);
			if (num > 0f)
			{
				FullBellyManager.HandleHungerRestore(((Object)item).name, num, restoreAmount, hungerBeforeAction);
				return;
			}
			FullBellyPlugin instance = FullBellyPlugin.Instance;
			if (instance != null)
			{
				instance.Logger.LogInfo((object)($"[Full Belly] Ate '{((Object)item).name}' restored {restoreAmount * 100f:0.00}% but all went to hunger " + $"(had {hungerBeforeAction * 100f:0.00}% hunger) — no fullness gained."));
			}
		}

		internal static bool ShouldSkipConsumeFallback(Item item)
		{
			if ((Object)(object)item == (Object)null)
			{
				return false;
			}
			if (IsMultiUseItem(item))
			{
				return true;
			}
			int instanceID = ((Object)item).GetInstanceID();
			return instanceID == _lastAppliedItemId && Time.unscaledTime - _lastAppliedTime < 2f;
		}

		internal static bool IsMultiUseItem(Item item)
		{
			if ((Object)(object)item == (Object)null)
			{
				return false;
			}
			if (item.totalUses > 1)
			{
				return true;
			}
			return (Object)(object)((Component)item).GetComponent<Action_ReduceUses>() != (Object)null && item.totalUses > 1;
		}

		private static void MarkApplied(Item item)
		{
			_lastAppliedItemId = ((Object)item).GetInstanceID();
			_lastAppliedTime = Time.unscaledTime;
		}
	}
	[HarmonyPatch(typeof(Action_RestoreHunger), "RunAction")]
	internal static class Patch_Action_RestoreHunger_Fullness
	{
		private static void Prefix(Action_RestoreHunger __instance)
		{
			if (!((Object)(object)((ItemActionBase)(__instance?)).item == (Object)null))
			{
				FoodRestoreTracker.CaptureHungerBefore(((ItemActionBase)__instance).character);
			}
		}

		private static void Postfix(Action_RestoreHunger __instance)
		{
			if (!((Object)(object)__instance == (Object)null) && !(__instance.restorationAmount <= 0f))
			{
				FoodRestoreTracker.ApplyFromItemAction(((ItemActionBase)__instance).item, __instance.restorationAmount);
			}
		}
	}
	[HarmonyPatch(typeof(Action_ModifyStatus), "RunAction")]
	internal static class Patch_Action_ModifyStatus_HungerFood
	{
		private static void Prefix(Action_ModifyStatus __instance)
		{
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Invalid comparison between Unknown and I4
			if (!((Object)(object)__instance == (Object)null) && !(__instance.changeAmount >= 0f) && (int)__instance.statusType == 1 && (!((Object)(object)((ItemActionBase)__instance).item != (Object)null) || !Patch_Action_ModifyStatus_CureAll.LooksLikeCureAll(((ItemActionBase)__instance).item)))
			{
				FoodRestoreTracker.CaptureHungerBefore(((ItemActionBase)__instance).character);
			}
		}

		private static void Postfix(Action_ModifyStatus __instance)
		{
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Invalid comparison between Unknown and I4
			if (!((Object)(object)__instance == (Object)null) && !(__instance.changeAmount >= 0f) && (int)__instance.statusType == 1 && (!((Object)(object)((ItemActionBase)__instance).item != (Object)null) || !Patch_Action_ModifyStatus_CureAll.LooksLikeCureAll(((ItemActionBase)__instance).item)) && (((ItemAction)__instance).OnCastFinished || ((ItemAction)__instance).OnConsumed || ((ItemAction)__instance).OnSecondaryCastFinished))
			{
				FoodRestoreTracker.ApplyFromItemAction(((ItemActionBase)__instance).item, 0f - __instance.changeAmount);
			}
		}
	}
	[HarmonyPatch(typeof(Item))]
	internal static class Patch_Item_Consume
	{
		private static float _hungerBeforeEat;

		[HarmonyPrefix]
		[HarmonyPatch("Consume")]
		private static void Prefix(int consumerID)
		{
			_hungerBeforeEat = 0f;
			PhotonView photonView = PhotonNetwork.GetPhotonView(consumerID);
			if ((Object)(object)photonView == (Object)null)
			{
				return;
			}
			Character component = ((Component)photonView).GetComponent<Character>();
			if (!((Object)(object)component == (Object)null) && !((Object)(object)((MonoBehaviourPun)component).photonView == (Object)null) && ((MonoBehaviourPun)component).photonView.IsMine)
			{
				CharacterAfflictions val = component.refs?.afflictions;
				if ((Object)(object)val != (Object)null)
				{
					_hungerBeforeEat = Mathf.Max(0f, val.GetCurrentStatus((STATUSTYPE)1));
				}
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch("Consume")]
		private static void Postfix(Item __instance, int consumerID)
		{
			//IL_00e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e9: Invalid comparison between Unknown and I4
			float hungerBeforeEat = _hungerBeforeEat;
			_hungerBeforeEat = 0f;
			if ((Object)(object)__instance == (Object)null)
			{
				return;
			}
			PhotonView photonView = PhotonNetwork.GetPhotonView(consumerID);
			if ((Object)(object)photonView == (Object)null)
			{
				return;
			}
			Character component = ((Component)photonView).GetComponent<Character>();
			if ((Object)(object)component == (Object)null || (Object)(object)((MonoBehaviourPun)component).photonView == (Object)null || !((MonoBehaviourPun)component).photonView.IsMine || FoodRestoreTracker.ShouldSkipConsumeFallback(__instance))
			{
				return;
			}
			float num = 0f;
			ItemActionBase[] components = ((Component)__instance).GetComponents<ItemActionBase>();
			foreach (ItemActionBase val in components)
			{
				Action_RestoreHunger val2 = (Action_RestoreHunger)(object)((val is Action_RestoreHunger) ? val : null);
				if (val2 != null && val2.restorationAmount > 0f)
				{
					num += val2.restorationAmount;
					continue;
				}
				Action_ModifyStatus val3 = (Action_ModifyStatus)(object)((val is Action_ModifyStatus) ? val : null);
				if (val3 != null && (int)val3.statusType == 1 && val3.changeAmount < 0f)
				{
					num += 0f - val3.changeAmount;
				}
			}
			if (num <= 0f)
			{
				return;
			}
			float num2 = num;
			if (FullBellyPlugin.syncedClearHungerBeforeFullness && hungerBeforeEat > 0f)
			{
				num2 = Mathf.Max(0f, num - hungerBeforeEat);
			}
			if (num2 > 0f)
			{
				FullBellyManager.HandleHungerRestore(((Object)__instance).name, num2, num, hungerBeforeEat);
				return;
			}
			FullBellyPlugin instance = FullBellyPlugin.Instance;
			if (instance != null)
			{
				instance.Logger.LogInfo((object)($"[Full Belly] Ate '{((Object)__instance).name}' restored {num * 100f:0.00}% but all went to hunger " + $"(had {hungerBeforeEat * 100f:0.00}% hunger) — no fullness gained."));
			}
		}
	}
	internal sealed class FullBellyConfigSnapshot
	{
		public float FullnessThreshold = 0.4f;

		public float FullnessDecayInterval = 3f;

		public float FullnessDecayAmount = 0.01f;

		public float BloatedDecayInterval = 3f;

		public float BloatedDecayAmount = 0.01f;

		public bool EnableOvereating = true;

		public bool ClearHungerBeforeFullness = false;

		public bool EnableFarting = true;

		public float FartChance = 0.1f;

		public float DecayFartChance = 0.25f;

		public bool EnableCureAllReducesBloated = true;

		public float CureAllBloatedReduction = 0.35f;

		public bool EnableBloatedSlowdown = true;

		public float BloatedMoveSpeedMultiplier = 0.5f;

		public float BloatedClimbSpeedMultiplier = 0.5f;

		public bool EnableBloatedStaminaCost = true;

		public float BloatedStaminaCostMultiplier = 2f;

		public bool EnableSprintFartBoost = true;

		public float SprintFartChancePerSecond = 0.5f;

		public bool EnableFartReducesBloated = true;

		public float FartBloatedReduction = 0.05f;

		public bool EnableStaminaSpeedsDecay = true;

		public float StaminaDecayBoost = 0.25f;
	}
	internal static class FullBellyConfigSyncService
	{
		public const int SchemaVersion = 2;

		public const char PairSeparator = ';';

		public const char KeyValueSeparator = '=';

		public static bool HasRemoteHostConfig { get; private set; }

		public static void ClearRemoteHostConfig()
		{
			HasRemoteHostConfig = false;
		}

		public static void ApplyLocalAuthoritativeConfig()
		{
			if (!((Object)(object)FullBellyPlugin.Instance == (Object)null))
			{
				ClearRemoteHostConfig();
				FullBellyPlugin.Instance.ApplySnapshot(FullBellyPlugin.Instance.BuildSnapshot(), fromRemote: false);
			}
		}

		public static void ApplyRemoteSnapshot(string payload)
		{
			if (PhotonNetwork.IsMasterClient || string.IsNullOrEmpty(payload))
			{
				return;
			}
			if (!TryDeserialize(payload, out var snapshot))
			{
				FullBellyPlugin instance = FullBellyPlugin.Instance;
				if (instance != null)
				{
					instance.Logger.LogError((object)"[Full Belly] Failed to apply host config snapshot.");
				}
			}
			else
			{
				HasRemoteHostConfig = true;
				FullBellyPlugin.Instance?.ApplySnapshot(snapshot, fromRemote: true);
			}
		}

		public static string Serialize(FullBellyConfigSnapshot snapshot)
		{
			if (snapshot == null)
			{
				return string.Empty;
			}
			StringBuilder stringBuilder = new StringBuilder(480);
			AppendPair(stringBuilder, "v", 2.ToString(CultureInfo.InvariantCulture));
			AppendPair(stringBuilder, "threshold", snapshot.FullnessThreshold.ToString(CultureInfo.InvariantCulture));
			AppendPair(stringBuilder, "fullInt", snapshot.FullnessDecayInterval.ToString(CultureInfo.InvariantCulture));
			AppendPair(stringBuilder, "fullAmt", snapshot.FullnessDecayAmount.ToString(CultureInfo.InvariantCulture));
			AppendPair(stringBuilder, "bloatInt", snapshot.BloatedDecayInterval.ToString(CultureInfo.InvariantCulture));
			AppendPair(stringBuilder, "bloatAmt", snapshot.BloatedDecayAmount.ToString(CultureInfo.InvariantCulture));
			AppendPair(stringBuilder, "overeat", snapshot.EnableOvereating ? "1" : "0");
			AppendPair(stringBuilder, "hungerFirst", snapshot.ClearHungerBeforeFullness ? "1" : "0");
			AppendPair(stringBuilder, "fart", snapshot.EnableFarting ? "1" : "0");
			AppendPair(stringBuilder, "fartChance", snapshot.FartChance.ToString(CultureInfo.InvariantCulture));
			AppendPair(stringBuilder, "decayFart", snapshot.DecayFartChance.ToString(CultureInfo.InvariantCulture));
			AppendPair(stringBuilder, "cureBloat", snapshot.EnableCureAllReducesBloated ? "1" : "0");
			AppendPair(stringBuilder, "cureAmt", snapshot.CureAllBloatedReduction.ToString(CultureInfo.InvariantCulture));
			AppendPair(stringBuilder, "slow", snapshot.EnableBloatedSlowdown ? "1" : "0");
			AppendPair(stringBuilder, "moveMul", snapshot.BloatedMoveSpeedMultiplier.ToString(CultureInfo.InvariantCulture));
			AppendPair(stringBuilder, "climbMul", snapshot.BloatedClimbSpeedMultiplier.ToString(CultureInfo.InvariantCulture));
			AppendPair(stringBuilder, "stamCost", snapshot.EnableBloatedStaminaCost ? "1" : "0");
			AppendPair(stringBuilder, "stamMul", snapshot.BloatedStaminaCostMultiplier.ToString(CultureInfo.InvariantCulture));
			AppendPair(stringBuilder, "sprintFart", snapshot.EnableSprintFartBoost ? "1" : "0");
			AppendPair(stringBuilder, "sprintFartChance", snapshot.SprintFartChancePerSecond.ToString(CultureInfo.InvariantCulture));
			AppendPair(stringBuilder, "fartBloat", snapshot.EnableFartReducesBloated ? "1" : "0");
			AppendPair(stringBuilder, "fartBloatAmt", snapshot.FartBloatedReduction.ToString(CultureInfo.InvariantCulture));
			AppendPair(stringBuilder, "stamDecay", snapshot.EnableStaminaSpeedsDecay ? "1" : "0");
			AppendPair(stringBuilder, "stamDecayBoost", snapshot.StaminaDecayBoost.ToString(CultureInfo.InvariantCulture));
			return stringBuilder.ToString();
		}

		public static bool TryDeserialize(string payload, out FullBellyConfigSnapshot snapshot)
		{
			snapshot = new FullBellyConfigSnapshot();
			if (string.IsNullOrEmpty(payload))
			{
				return false;
			}
			string[] array = payload.Split(new char[1] { ';' });
			int result = 0;
			bool flag = false;
			foreach (string text in array)
			{
				if (string.IsNullOrEmpty(text))
				{
					continue;
				}
				int num = text.IndexOf('=');
				if (num <= 0)
				{
					continue;
				}
				string text2 = text.Substring(0, num);
				string text3 = text.Substring(num + 1);
				switch (text2)
				{
				case "v":
					flag = int.TryParse(text3, NumberStyles.Integer, CultureInfo.InvariantCulture, out result);
					break;
				case "threshold":
					float.TryParse(text3, NumberStyles.Float, CultureInfo.InvariantCulture, out snapshot.FullnessThreshold);
					break;
				case "fullInt":
					float.TryParse(text3, NumberStyles.Float, CultureInfo.InvariantCulture, out snapshot.FullnessDecayInterval);
					break;
				case "fullAmt":
					float.TryParse(text3, NumberStyles.Float, CultureInfo.InvariantCulture, out snapshot.FullnessDecayAmount);
					break;
				case "bloatInt":
					float.TryParse(text3, NumberStyles.Float, CultureInfo.InvariantCulture, out snapshot.BloatedDecayInterval);
					break;
				case "bloatAmt":
					float.TryParse(text3, NumberStyles.Float, CultureInfo.InvariantCulture, out snapshot.BloatedDecayAmount);
					break;
				case "overeat":
					snapshot.EnableOvereating = text3 == "1";
					break;
				case "hungerFirst":
					snapshot.ClearHungerBeforeFullness = text3 == "1";
					break;
				case "fart":
					snapshot.EnableFarting = text3 == "1";
					break;
				case "fartChance":
				{
					if (float.TryParse(text3, NumberStyles.Float, CultureInfo.InvariantCulture, out var result5))
					{
						snapshot.FartChance = Mathf.Clamp01(result5);
					}
					break;
				}
				case "decayFart":
				{
					if (float.TryParse(text3, NumberStyles.Float, CultureInfo.InvariantCulture, out var result4))
					{
						snapshot.DecayFartChance = Mathf.Clamp01(result4);
					}
					break;
				}
				case "cureBloat":
					snapshot.EnableCureAllReducesBloated = text3 == "1";
					break;
				case "cureAmt":
				{
					if (float.TryParse(text3, NumberStyles.Float, CultureInfo.InvariantCulture, out var result3))
					{
						snapshot.CureAllBloatedReduction = Mathf.Clamp01(result3);
					}
					break;
				}
				case "slow":
					snapshot.EnableBloatedSlowdown = text3 == "1";
					break;
				case "moveMul":
					float.TryParse(text3, NumberStyles.Float, CultureInfo.InvariantCulture, out snapshot.BloatedMoveSpeedMultiplier);
					break;
				case "climbMul":
					float.TryParse(text3, NumberStyles.Float, CultureInfo.InvariantCulture, out snapshot.BloatedClimbSpeedMultiplier);
					break;
				case "stamCost":
					snapshot.EnableBloatedStaminaCost = text3 == "1";
					break;
				case "stamMul":
					float.TryParse(text3, NumberStyles.Float, CultureInfo.InvariantCulture, out snapshot.BloatedStaminaCostMultiplier);
					break;
				case "sprintFart":
					snapshot.EnableSprintFartBoost = text3 == "1";
					break;
				case "sprintFartChance":
					float.TryParse(text3, NumberStyles.Float, CultureInfo.InvariantCulture, out snapshot.SprintFartChancePerSecond);
					break;
				case "fartBloat":
					snapshot.EnableFartReducesBloated = text3 == "1";
					break;
				case "fartBloatAmt":
				{
					if (float.TryParse(text3, NumberStyles.Float, CultureInfo.InvariantCulture, out var result2))
					{
						snapshot.FartBloatedReduction = Mathf.Clamp01(result2);
					}
					break;
				}
				case "stamDecay":
					snapshot.EnableStaminaSpeedsDecay = text3 == "1";
					break;
				case "stamDecayBoost":
					float.TryParse(text3, NumberStyles.Float, CultureInfo.InvariantCulture, out snapshot.StaminaDecayBoost);
					break;
				}
			}
			return flag && result >= 1 && result <= 2;
		}

		private static void AppendPair(StringBuilder builder, string key, string value)
		{
			if (builder.Length > 0)
			{
				builder.Append(';');
			}
			builder.Append(key);
			builder.Append('=');
			builder.Append(value ?? string.Empty);
		}
	}
	public class FullBellyManager : MonoBehaviour
	{
		private static GameObject _managerObject;

		internal static FullBellyManager instance;

		private ManualLogSource _logger;

		internal float fullnessThreshold;

		internal bool isLabeledFull;

		internal bool isLabeledBloated;

		private HashSet<string> _ignoredItems = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

		internal float foodConsumptionAmount;

		private float _bloatedAmountStored;

		private Coroutine _decayCoroutine;

		private Coroutine _uiEnsureCoroutine;

		private TextMeshProUGUI _percentageText;

		private Canvas _canvas;

		private float _lastDisplayedFull = -1f;

		private float _lastDisplayedBloated = -1f;

		private string _lastDisplayText = string.Empty;

		internal bool ShouldBlockHunger
		{
			get
			{
				if (foodConsumptionAmount <= 0.0001f)
				{
					return false;
				}
				return isLabeledFull || isLabeledBloated || (fullnessThreshold > 0.0001f && foodConsumptionAmount >= fullnessThreshold);
			}
		}

		public static void Initialize(ManualLogSource log, float threshold, string ignoredItemsCsv)
		{
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Expected O, but got Unknown
			if ((Object)(object)_managerObject != (Object)null)
			{
				Object.Destroy((Object)(object)_managerObject);
			}
			_managerObject = new GameObject("FullBellyManager");
			Object.DontDestroyOnLoad((Object)(object)_managerObject);
			instance = _managerObject.AddComponent<FullBellyManager>();
			instance._logger = log;
			instance.ApplyLocalSettings(threshold, ignoredItemsCsv);
			SceneManager.activeSceneChanged += instance.OnSceneChanged;
		}

		internal void ApplyLocalSettings(float threshold, string ignoredItemsCsv)
		{
			fullnessThreshold = threshold;
			_ignoredItems = new HashSet<string>(from s in (ignoredItemsCsv ?? string.Empty).Split(new char[1] { ',' })
				select s.Trim() into s
				where !string.IsNullOrEmpty(s)
				select s, StringComparer.OrdinalIgnoreCase);
		}

		public void UpdateSyncedThreshold(float newThreshold)
		{
			if (Mathf.Abs(fullnessThreshold - newThreshold) > 0.001f)
			{
				fullnessThreshold = newThreshold;
			}
		}

		internal float GetBloatedAmount()
		{
			if (IsLocalSkeleton())
			{
				return 0f;
			}
			if (!FullBellyPlugin.syncedEnableOvereating)
			{
				return 0f;
			}
			return Mathf.Max(0f, foodConsumptionAmount - fullnessThreshold);
		}

		private void Update()
		{
			if (!FullBellyPlugin.isHandshakeValid)
			{
				return;
			}
			Character localCharacter = Character.localCharacter;
			CharacterAfflictions afflictions = localCharacter?.refs?.afflictions;
			if (IsLocalSkeleton(localCharacter))
			{
				if (foodConsumptionAmount > 0f || isLabeledFull || isLabeledBloated)
				{
					ResetFullBelly("Became skeleton (Book of Bones)");
				}
				NoHungerGuard.Tick(afflictions, shouldBlockHunger: true);
				BloatedEffects.Tick();
				return;
			}
			EnsureLabelsConsistency();
			if (ShouldBlockHunger)
			{
				NoHungerGuard.Tick(afflictions, shouldBlockHunger: true);
			}
			BloatedEffects.Tick();
			float bloatedAmount = GetBloatedAmount();
			if (bloatedAmount > 0f || _barNeedsTick())
			{
				BloatedUi.Drive(bloatedAmount);
			}
		}

		private void EnsureLabelsConsistency()
		{
			if (foodConsumptionAmount <= 0f)
			{
				foodConsumptionAmount = 0f;
				isLabeledFull = false;
				isLabeledBloated = false;
				return;
			}
			float bloatedAmount = GetBloatedAmount();
			if (bloatedAmount > 0f)
			{
				isLabeledFull = true;
				isLabeledBloated = true;
				return;
			}
			isLabeledBloated = false;
			if (foodConsumptionAmount > fullnessThreshold && !FullBellyPlugin.syncedEnableOvereating)
			{
				foodConsumptionAmount = fullnessThreshold;
			}
			if (foodConsumptionAmount >= fullnessThreshold)
			{
				isLabeledFull = true;
			}
		}

		internal static bool IsLocalSkeleton(Character character = null)
		{
			character = character ?? Character.localCharacter;
			return (Object)(object)character != (Object)null && (Object)(object)character.data != (Object)null && character.data.isSkeleton;
		}

		private bool _barNeedsTick()
		{
			return (Object)(object)BloatedUi.Bar != (Object)null && ((Component)BloatedUi.Bar).gameObject.activeSelf;
		}

		public static void HandleDeathStateChange(bool isNowDead)
		{
			if ((Object)(object)instance == (Object)null)
			{
				return;
			}
			Character localCharacter = Character.localCharacter;
			if (!((Object)(object)localCharacter == (Object)null) && !((Object)(object)((MonoBehaviourPun)localCharacter).photonView == (Object)null) && ((MonoBehaviourPun)localCharacter).photonView.IsMine)
			{
				if (isNowDead)
				{
					instance._logger.LogInfo((object)"[Full Belly] Player died - clearing fullness UI.");
					instance.ClearFullnessUI();
				}
				else
				{
					instance._logger.LogInfo((object)"[Full Belly] Player revived - resetting fullness.");
					instance.ResetFullBelly("Player revived");
				}
			}
		}

		private void ClearFullnessUI()
		{
			if (_decayCoroutine != null)
			{
				((MonoBehaviour)this).StopCoroutine(_decayCoroutine);
				_decayCoroutine = null;
			}
			DestroyPercentageText();
			foodConsumptionAmount = 0f;
			isLabeledFull = false;
			isLabeledBloated = false;
			_bloatedAmountStored = 0f;
			BloatedUi.Drive(0f);
			NoHungerGuard.Clear(Character.localCharacter?.refs?.afflictions);
		}

		public static void HandleHungerRestore(string itemName, float towardFullness, float totalRestore = -1f, float hungerBefore = 0f)
		{
			if ((Object)(object)instance == (Object)null || string.IsNullOrEmpty(itemName) || towardFullness <= 0f || !FullBellyPlugin.isHandshakeValid)
			{
				return;
			}
			Character localCharacter = Character.localCharacter;
			if ((Object)(object)localCharacter?.data != (Object)null && localCharacter.data.dead)
			{
				return;
			}
			if (IsLocalSkeleton(localCharacter))
			{
				instance._logger.LogInfo((object)"[Full Belly] Ignored hunger restore — local character is a skeleton.");
				return;
			}
			string text = itemName.Replace("(Clone)", string.Empty).Trim();
			int num = text.LastIndexOf(':');
			if (num >= 0 && num < text.Length - 1)
			{
				text = text.Substring(num + 1);
			}
			if (instance._ignoredItems.Contains(text))
			{
				instance._logger.LogInfo((object)("[Full Belly] Ignored hunger from '" + text + "'."));
				return;
			}
			instance.foodConsumptionAmount += towardFullness;
			if (!FullBellyPlugin.syncedEnableOvereating && instance.foodConsumptionAmount > instance.fullnessThreshold)
			{
				instance.foodConsumptionAmount = instance.fullnessThreshold;
			}
			if (totalRestore > 0f && FullBellyPlugin.syncedClearHungerBeforeFullness && hungerBefore > 0f)
			{
				instance._logger.LogInfo((object)($"[Full Belly] Ate {text}: restore {totalRestore * 100f:0.00}%, " + $"hunger was {hungerBefore * 100f:0.00}%, " + $"+{towardFullness * 100f:0.00}% fullness (surplus), total {instance.foodConsumptionAmount * 100f:0.00}%"));
			}
			else
			{
				instance._logger.LogInfo((object)$"[Full Belly] Ate {text}, +{towardFullness * 100f:0.00}%, total {instance.foodConsumptionAmount * 100f:0.00}%");
			}
			bool flag = instance.foodConsumptionAmount >= instance.fullnessThreshold;
			if (flag)
			{
				instance.isLabeledFull = true;
			}
			CharacterAfflictions val = localCharacter?.refs?.afflictions;
			if ((Object)(object)val == (Object)null)
			{
				instance._logger.LogWarning((object)"[Full Belly] Afflictions missing after eat — food tracked; NoHunger will apply next tick if full.");
				if (FullBellyPlugin.cfgShowPercentageDisplay.Value)
				{
					instance.EnsurePercentageText();
					instance.UpdatePercentageDisplay(force: true);
				}
				return;
			}
			if (flag)
			{
				NoHungerGuard.ForceRefresh(val);
				instance._logger.LogInfo((object)$"[Full Belly] FULL. Consumption: {instance.foodConsumptionAmount * 100f:0.00}% / threshold {instance.fullnessThreshold * 100f:0.00}%");
			}
			float num2 = instance.foodConsumptionAmount - instance.fullnessThreshold;
			if (FullBellyPlugin.syncedEnableOvereating && num2 > 0f)
			{
				if (!instance.isLabeledBloated)
				{
					instance.ApplyBloatedLabel(val);
				}
				else
				{
					instance.UpdateBloatedStatus(val);
				}
			}
			if (instance.isLabeledFull && FullBellyPlugin.syncedEnableFarting && Random.value < FullBellyPlugin.syncedFartChance)
			{
				instance.TryFart(localCharacter);
			}
			if (instance._decayCoroutine != null)
			{
				((MonoBehaviour)instance).StopCoroutine(instance._decayCoroutine);
			}
			instance._decayCoroutine = ((MonoBehaviour)instance).StartCoroutine(instance.DecayCoroutine(val));
			if (FullBellyPlugin.cfgShowPercentageDisplay.Value)
			{
				instance.EnsurePercentageText();
				instance.UpdatePercentageDisplay(force: true);
			}
		}

		private void OnSceneChanged(Scene oldScene, Scene newScene)
		{
			ResetFullBelly("Scene changed");
			BloatedUi.Invalidate();
			GameFontUtil.InvalidateCache();
			if (_uiEnsureCoroutine != null)
			{
				((MonoBehaviour)this).StopCoroutine(_uiEnsureCoroutine);
			}
			_uiEnsureCoroutine = ((MonoBehaviour)this).StartCoroutine(EnsureBloatedUiDelayed());
		}

		private IEnumerator EnsureBloatedUiDelayed()
		{
			yield return (object)new WaitForSeconds(1f);
			int waits = 0;
			while ((Object)(object)Character.localCharacter == (Object)null && waits < 50)
			{
				yield return (object)new WaitForSeconds(0.1f);
				waits++;
			}
			yield return ((MonoBehaviour)this).StartCoroutine(BloatedUi.EnsureCreated(_logger));
			_uiEnsureCoroutine = null;
		}

		public static void HandleBecameSkeleton()
		{
			if (!((Object)(object)instance == (Object)null) && FullBellyPlugin.isHandshakeValid)
			{
				instance.ResetFullBelly("Became skeleton (Book of Bones)");
				CharacterAfflictions afflictions = Character.localCharacter?.refs?.afflictions;
				NoHungerGuard.ForceRefresh(afflictions);
			}
		}

		public void ResetFullBelly(string reason)
		{
			if (_decayCoroutine != null)
			{
				((MonoBehaviour)this).StopCoroutine(_decayCoroutine);
				_decayCoroutine = null;
			}
			DestroyPercentageText();
			foodConsumptionAmount = 0f;
			_bloatedAmountStored = 0f;
			isLabeledFull = false;
			isLabeledBloated = false;
			BloatedUi.Drive(0f);
			NoHungerGuard.Clear(Character.localCharacter?.refs?.afflictions);
			ManualLogSource logger = _logger;
			if (logger != null)
			{
				logger.LogInfo((object)("[Full Belly] Reset: " + reason));
			}
		}

		private void ApplyFullLabel(CharacterAfflictions afflictions)
		{
			if (!((Object)(object)afflictions == (Object)null))
			{
				isLabeledFull = true;
				NoHungerGuard.ForceRefresh(afflictions);
				_logger.LogInfo((object)$"[Full Belly] FULL. Consumption: {foodConsumptionAmount * 100f:0.00}% / threshold {fullnessThreshold * 100f:0.00}%");
				if (FullBellyPlugin.cfgShowPercentageDisplay.Value)
				{
					EnsurePercentageText();
					UpdatePercentageDisplay(force: true);
				}
			}
		}

		private void ApplyBloatedLabel(CharacterAfflictions afflictions)
		{
			if (!((Object)(object)afflictions == (Object)null) && FullBellyPlugin.syncedEnableOvereating)
			{
				isLabeledBloated = true;
				NoHungerGuard.ForceRefresh(afflictions);
				UpdateBloatedStatus(afflictions);
				float num = foodConsumptionAmount - fullnessThreshold;
				_logger.LogInfo((object)$"[Full Belly] BLOATED. Amount: {num * 100f:0.00}%");
				if (FullBellyPlugin.cfgShowPercentageDisplay.Value)
				{
					EnsurePercentageText();
					UpdatePercentageDisplay(force: true);
				}
				if (_uiEnsureCoroutine == null && (Object)(object)BloatedUi.Bar == (Object)null)
				{
					_uiEnsureCoroutine = ((MonoBehaviour)this).StartCoroutine(EnsureBloatedUiDelayed());
				}
			}
		}

		internal void UpdateBloatedStatus(CharacterAfflictions afflictions)
		{
			SyncLabelsFromFood(afflictions);
		}

		private void SetBloatedAmount(float amount)
		{
			_bloatedAmountStored = Mathf.Max(0f, amount);
			BloatedUi.Drive(_bloatedAmountStored);
		}

		private IEnumerator DecayCoroutine(CharacterAfflictions afflictions)
		{
			if ((Object)(object)afflictions == (Object)null)
			{
				yield break;
			}
			Character character = Character.localCharacter;
			while (foodConsumptionAmount > 0f && (Object)(object)afflictions != (Object)null)
			{
				NoHungerGuard.Tick(afflictions, ShouldBlockHunger);
				float bloatedAmount = GetBloatedAmount();
				if (bloatedAmount > 0f)
				{
					yield return (object)new WaitForSeconds(FullBellyPlugin.syncedBloatedDecayInterval);
					if (foodConsumptionAmount <= 0f)
					{
						break;
					}
					bloatedAmount = GetBloatedAmount();
					if (bloatedAmount <= 0f)
					{
						SyncLabelsFromFood(afflictions);
						continue;
					}
					if (FullBellyPlugin.syncedEnableFarting && (Object)(object)character != (Object)null && Random.value < FullBellyPlugin.syncedDecayFartChance)
					{
						TryFart(character);
						bloatedAmount = GetBloatedAmount();
						if (bloatedAmount <= 0f)
						{
							SyncLabelsFromFood(afflictions);
							continue;
						}
					}
					float newBloated = Mathf.Max(0f, bloatedAmount - FullBellyPlugin.syncedBloatedDecayAmount);
					foodConsumptionAmount = fullnessThreshold + newBloated;
					SyncLabelsFromFood(afflictions);
				}
				else if (foodConsumptionAmount > fullnessThreshold && !FullBellyPlugin.syncedEnableOvereating)
				{
					foodConsumptionAmount = fullnessThreshold;
					SyncLabelsFromFood(afflictions);
					yield return (object)new WaitForSeconds(FullBellyPlugin.syncedFullnessDecayInterval);
				}
				else
				{
					yield return (object)new WaitForSeconds(FullBellyPlugin.syncedFullnessDecayInterval);
					if (foodConsumptionAmount <= 0f)
					{
						break;
					}
					float relativeDecay = FullBellyPlugin.syncedFullnessDecayAmount * fullnessThreshold;
					foodConsumptionAmount = Mathf.Max(0f, foodConsumptionAmount - relativeDecay);
					SyncLabelsFromFood(afflictions);
				}
				if (FullBellyPlugin.cfgShowPercentageDisplay.Value)
				{
					EnsurePercentageText();
					UpdatePercentageDisplay();
				}
			}
			if (foodConsumptionAmount <= 0f)
			{
				foodConsumptionAmount = 0f;
				isLabeledFull = false;
				isLabeledBloated = false;
				SetBloatedAmount(0f);
			}
			_decayCoroutine = null;
		}

		private void TryFart(Character character)
		{
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_0067: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)character == (Object)null))
			{
				_logger.LogInfo((object)"[Full Belly] Fart triggered!");
				try
				{
					GameUtils.instance.SpawnResourceAtPositionNetworked("VFX_SporeExploExploEdibleSpawn", character.Center, (RpcTarget)1);
					GameUtils.instance.RPC_SpawnResourceAtPosition("VFX_SporeExploExploEdibleSpawn_NoKnockback", character.Center);
					character.AddForceToBodyPart(character.GetBodypartRig((BodypartType)0), Vector3.zero, Vector3.up * 100f);
				}
				catch (Exception ex)
				{
					_logger.LogWarning((object)("[Full Belly] Failed to trigger fart: " + ex.Message));
				}
				if (FullBellyPlugin.syncedEnableFartReducesBloated && FullBellyPlugin.syncedFartBloatedReduction > 0f && GetBloatedAmount() > 0f)
				{
					ApplyExtraDecay(FullBellyPlugin.syncedFartBloatedReduction);
				}
			}
		}

		internal void TryFartPublic(Character character)
		{
			TryFart(character);
		}

		private void SyncLabelsFromFood(CharacterAfflictions afflictions)
		{
			float bloatedAmount = GetBloatedAmount();
			if (foodConsumptionAmount <= 0f)
			{
				foodConsumptionAmount = 0f;
				isLabeledFull = false;
				isLabeledBloated = false;
				SetBloatedAmount(0f);
				return;
			}
			if (bloatedAmount > 0f)
			{
				isLabeledFull = true;
				isLabeledBloated = true;
				SetBloatedAmount(bloatedAmount);
				return;
			}
			isLabeledBloated = false;
			SetBloatedAmount(0f);
			if (foodConsumptionAmount > fullnessThreshold)
			{
				foodConsumptionAmount = fullnessThreshold;
			}
			if (foodConsumptionAmount >= fullnessThreshold)
			{
				isLabeledFull = true;
			}
		}

		internal static bool ShouldBlockHungerFor(Character character)
		{
			if ((Object)(object)character == (Object)null || (Object)(object)character != (Object)(object)Character.localCharacter)
			{
				return false;
			}
			if (!FullBellyPlugin.isHandshakeValid)
			{
				return false;
			}
			if (IsLocalSkeleton(character))
			{
				return true;
			}
			FullBellyManager fullBellyManager = instance;
			return (Object)(object)fullBellyManager != (Object)null && fullBellyManager.ShouldBlockHunger;
		}

		internal void ApplyExtraDecay(float amount, bool restartDecay = true)
		{
			if (amount <= 0f || foodConsumptionAmount <= 0f)
			{
				return;
			}
			foodConsumptionAmount = Mathf.Max(0f, foodConsumptionAmount - amount);
			CharacterAfflictions val = Character.localCharacter?.refs?.afflictions;
			SyncLabelsFromFood(val);
			if (FullBellyPlugin.cfgShowPercentageDisplay.Value)
			{
				EnsurePercentageText();
				UpdatePercentageDisplay(restartDecay);
			}
			if (foodConsumptionAmount <= 0f)
			{
				if (_decayCoroutine != null)
				{
					((MonoBehaviour)this).StopCoroutine(_decayCoroutine);
					_decayCoroutine = null;
				}
			}
			else if (restartDecay && (Object)(object)val != (Object)null)
			{
				if (_decayCoroutine != null)
				{
					((MonoBehaviour)this).StopCoroutine(_decayCoroutine);
				}
				_decayCoroutine = ((MonoBehaviour)this).StartCoroutine(DecayCoroutine(val));
			}
		}

		public static void HandleCureAllUsed(string itemName)
		{
			if (!((Object)(object)instance == (Object)null) && FullBellyPlugin.isHandshakeValid && FullBellyPlugin.syncedEnableCureAllReducesBloated && !(FullBellyPlugin.syncedCureAllBloatedReduction <= 0f) && !(instance.GetBloatedAmount() <= 0f))
			{
				Character localCharacter = Character.localCharacter;
				if (!((Object)(object)localCharacter?.data != (Object)null) || !localCharacter.data.dead)
				{
					float syncedCureAllBloatedReduction = FullBellyPlugin.syncedCureAllBloatedReduction;
					instance._logger.LogInfo((object)$"[Full Belly] Cure-All '{itemName}' reduced bloated by {syncedCureAllBloatedReduction * 100f:0.00}%");
					instance.ApplyExtraDecay(syncedCureAllBloatedReduction);
				}
			}
		}

		private void EnsureCanvas()
		{
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Expected O, but got Unknown
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_canvas != (Object)null)
			{
				return;
			}
			GameObject val = GameObject.Find("FullBellyNotificationCanvas");
			if ((Object)(object)val != (Object)null)
			{
				_canvas = val.GetComponent<Canvas>();
				if ((Object)(object)_canvas != (Object)null)
				{
					return;
				}
			}
			GameObject val2 = new GameObject("FullBellyNotificationCanvas");
			_canvas = val2.AddComponent<Canvas>();
			_canvas.renderMode = (RenderMode)0;
			_canvas.sortingOrder = 999;
			CanvasScaler val3 = val2.AddComponent<CanvasScaler>();
			val3.uiScaleMode = (ScaleMode)1;
			val3.referenceResolution = new Vector2(1920f, 1080f);
			val3.screenMatchMode = (ScreenMatchMode)0;
			val3.matchWidthOrHeight = 0.5f;
			val2.AddComponent<GraphicRaycaster>();
			Object.DontDestroyOnLoad((Object)(object)val2);
		}

		private void EnsurePercentageText()
		{
			//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f1: 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_00fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0104: Unknown result type (might be due to invalid IL or missing references)
			//IL_0143: Unknown result type (might be due to invalid IL or missing references)
			//IL_0148: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Expected O, but got Unknown
			if (!FullBellyPlugin.cfgShowPercentageDisplay.Value)
			{
				return;
			}
			EnsureCanvas();
			if (!((Object)(object)_canvas == (Object)null))
			{
				if ((Object)(object)_percentageText == (Object)null)
				{
					GameObject val = new GameObject("FullBellyPercentageText");
					val.transform.SetParent(((Component)_canvas).transform, false);
					_percentageText = val.AddComponent<TextMeshProUGUI>();
					((Graphic)_percentageText).raycastTarget = false;
					((TMP_Text)_percentageText).enableWordWrapping = false;
					((TMP_Text)_percentageText).alignment = (TextAlignmentOptions)514;
					((TMP_Text)_percentageText).fontStyle = (FontStyles)0;
					GameFontUtil.ApplyTo(_percentageText);
				}
				RectTransform rectTransform = ((TMP_Text)_percentageText).rectTransform;
				rectTransform.sizeDelta = new Vector2(800f, 100f);
				Vector2 val2 = default(Vector2);
				((Vector2)(ref val2))..ctor(0.5f, 0.5f);
				rectTransform.pivot = val2;
				Vector2 anchorMin = (rectTransform.anchorMax = val2);
				rectTransform.anchorMin = anchorMin;
				UpdatePercentagePosition();
				((TMP_Text)_percentageText).fontSize = Mathf.Clamp(FullBellyPlugin.cfgPercentageFontSize.Value, 10, 96);
				((Graphic)_percentageText).color = ColorUtil.ParseHex(FullBellyPlugin.cfgPercentageColor.Value, Color.white);
				((Component)_percentageText).gameObject.SetActive(true);
			}
		}

		internal void RefreshPercentageStyle()
		{
			//IL_0081: 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)
			if (!FullBellyPlugin.cfgShowPercentageDisplay.Value)
			{
				DestroyPercentageText();
				return;
			}
			EnsurePercentageText();
			if (!((Object)(object)_percentageText == (Object)null))
			{
				GameFontUtil.ApplyTo(_percentageText);
				((TMP_Text)_percentageText).fontStyle = (FontStyles)0;
				((TMP_Text)_percentageText).fontSize = Mathf.Clamp(FullBellyPlugin.cfgPercentageFontSize.Value, 10, 96);
				((Graphic)_percentageText).color = ColorUtil.ParseHex(FullBellyPlugin.cfgPercentageColor.Value, Color.white);
				UpdatePercentagePosition();
				UpdatePercentageDisplay(force: true);
			}
		}

		private void UpdatePercentageDisplay(bool force = false)
		{
			//IL_01e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e6: Unknown result type (might be due to invalid IL or missing references)
			if (!FullBellyPlugin.cfgShowPercentageDisplay.Value)
			{
				return;
			}
			if ((Object)(object)_percentageText == (Object)null)
			{
				EnsurePercentageText();
				if ((Object)(object)_percentageText == (Object)null)
				{
					return;
				}
			}
			if (foodConsumptionAmount <= 0f)
			{
				if (((TMP_Text)_percentageText).text.Length > 0)
				{
					((TMP_Text)_percentageText).text = string.Empty;
					_lastDisplayText = string.Empty;
				}
				_lastDisplayedFull = 0f;
				_lastDisplayedBloated = 0f;
				return;
			}
			float num = foodConsumptionAmount - fullnessThreshold;
			float num2 = 0f;
			float num3;
			string text;
			if (foodConsumptionAmount < fullnessThreshold)
			{
				num3 = foodConsumptionAmount / Mathf.Max(0.0001f, fullnessThreshold) * 100f;
				text = $"{num3:0.0}% Full";
			}
			else if (num <= 0f || !FullBellyPlugin.syncedEnableOvereating)
			{
				num3 = 100f;
				text = "100% Full";
			}
			else
			{
				num3 = 100f;
				num2 = num * 100f;
				text = ((!StatusBarNumberDisplayDetector.ShouldShowBloatedInPercentageText()) ? "100% Full" : $"100% Full\n{num2:0.0}% Bloated");
			}
			if (force || !(Mathf.Abs(num3 - _lastDisplayedFull) < 0.05f) || !(Mathf.Abs(num2 - _lastDisplayedBloated) < 0.05f) || !(text == _lastDisplayText))
			{
				_lastDisplayedFull = num3;
				_lastDisplayedBloated = num2;
				_lastDisplayText = text;
				((TMP_Text)_percentageText).text = text;
				((Graphic)_percentageText).color = ColorUtil.ParseHex(FullBellyPlugin.cfgPercentageColor.Value, Color.white);
			}
		}

		private void UpdatePercentagePosition()
		{
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)_percentageText == (Object)null))
			{
				((TMP_Text)_percentageText).rectTransform.anchoredPosition = new Vector2(FullBellyPlugin.cfgPercentageOffsetX.Value, FullBellyPlugin.cfgPercentageOffsetY.Value);
			}
		}

		private void DestroyPercentageText()
		{
			if ((Object)(object)_percentageText != (Object)null)
			{
				Object.Destroy((Object)(object)((Component)_percentageText).gameObject);
				_percentageText = null;
			}
			_lastDisplayText = string.Empty;
			_lastDisplayedFull = -1f;
			_lastDisplayedBloated = -1f;
		}
	}
	internal static class FullBellyModAuthorizationService
	{
		public const string RoomConfigPropertyKey = "fullBellyConfigV1";

		private static PhotonScopedManager _photonManager;

		private static bool _photonManagerReady;

		private static bool _hostHasFullBellyMod;

		private static string _pendingHostConfigPayload;

		public static bool IsGameplayEnabled { get; private set; }

		public static void Initialize()
		{
			Networking.AddHostHasPluginListeners("tony4twentys.full_belly", (Action)OnHostHasFullBellyMod, (Action)OnHostMissingFullBellyMod);
			if (PhotonCustomPropsUtilsPlugin.IsReady)
			{
				SetupPhotonManager();
			}
			else
			{
				PhotonCustomPropsUtilsPlugin.OnReady += SetupPhotonManager;
			}
			if (!PhotonNetwork.InRoom)
			{
				SetSoloAuthorized();
			}
		}

		public static void SetSoloAuthorized()
		{
			_hostHasFullBellyMod = true;
			IsGameplayEnabled = true;
			FullBellyConfigSyncService.ApplyLocalAuthoritativeConfig();
		}

		private static void SetupPhotonManager()
		{
			if (_photonManagerReady)
			{
				return;
			}
			_photonManagerReady = true;
			_photonManager = PhotonCustomPropsUtilsPlugin.GetManager("tony4twentys.full_belly");
			_photonManager.RegisterOnJoinedRoom((Action<Player>)delegate
			{
				if (PhotonNetwork.InRoom && PhotonNetwork.IsMasterClient && _hostHasFullBellyMod)
				{
					FullBellyConfigSyncService.ApplyLocalAuthoritativeConfig();
					PublishHostConfigIfMaster();
				}
			});
			_photonManager.RegisterRoomProperty<string>("fullBellyConfigV1", (RoomEventType)2, (Action<string>)delegate(string value)
			{
				if (!PhotonNetwork.IsMasterClient && !string.IsNullOrEmpty(value))
				{
					if (!_hostHasFullBellyMod)
					{
						_pendingHostConfigPayload = value;
					}
					else
					{
						FullBellyConfigSyncService.ApplyRemoteSnapshot(value);
					}
				}
			});
		}

		private static void OnHostHasFullBellyMod()
		{
			if (PhotonNetwork.InRoom)
			{
				_hostHasFullBellyMod = true;
				IsGameplayEnabled = true;
				FullBellyPlugin instance = FullBellyPlugin.Instance;
				if (instance != null)
				{
					instance.Logger.LogInfo((object)"[Full Belly] Host handshake OK — gameplay enabled.");
				}
				if (PhotonNetwork.IsMasterClient)
				{
					FullBellyConfigSyncService.ApplyLocalAuthoritativeConfig();
					PublishHostConfigIfMaster();
				}
				else if (!string.IsNullOrEmpty(_pendingHostConfigPayload))
				{
					FullBellyConfigSyncService.ApplyRemoteSnapshot(_pendingHostConfigPayload);
					_pendingHostConfigPayload = null;
				}
			}
		}

		private static void OnHostMissingFullBellyMod()
		{
			if (PhotonNetwork.InRoom)
			{
				_hostHasFullBellyMod = false;
				IsGameplayEnabled = false;
				_pendingHostConfigPayload = null;
				FullBellyConfigSyncService.ClearRemoteHostConfig();
				FullBellyConfigSyncService.ApplyLocalAuthoritativeConfig();
				FullBellyManager.instance?.ResetFullBelly("Host missing Full Belly");
				FullBellyPlugin instance = FullBellyPlugin.Instance;
				if (instance != null)
				{
					instance.Logger.LogWarning((object)"[Full Belly] Host missing mod — gameplay disabled (anti-cheat).");
				}
			}
		}

		public static void OnHostConfigChangedLocally()
		{
			if (!PhotonNetwork.InRoom)
			{
				FullBellyConfigSyncService.ApplyLocalAuthoritativeConfig();
			}
			else if (IsGameplayEnabled && PhotonNetwork.IsMasterClient)
			{
				FullBellyConfigSyncService.ApplyLocalAuthoritativeConfig();
				PublishHostConfigIfMaster();
			}
		}

		private static void PublishHostConfigIfMaster()
		{
			if (_photonManager != null && PhotonNetwork.InRoom && PhotonNetwork.IsMasterClient && !((Object)(object)FullBellyPlugin.Instance == (Object)null))
			{
				string text = FullBellyConfigSyncService.Serialize(FullBellyPlugin.Instance.BuildSnapshot());
				_photonManager.SetRoomProperty("fullBellyConfigV1", (object)text);
				FullBellyPlugin.Instance.Logger.LogInfo((object)"[Full Belly] Host published gameplay config.");
			}
		}
	}
	[BepInPlugin("tony4twentys.full_belly", "Full Belly", "4.0.1")]
	[BepInProcess("PEAK.exe")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class FullBellyPlugin : BaseUnityPlugin
	{
		public const string PluginGuid = "tony4twentys.full_belly";

		public const string PluginName = "Full Belly";

		public const string PluginVersion = "4.0.1";

		public const string PhotonCustomPropsPluginGuid = "com.snosz.photoncustompropsutils";

		public static FullBellyPlugin Instance;

		internal static ConfigEntry<float> cfgFullnessThreshold;

		private static ConfigEntry<string> cfgIgnoredItems;

		internal static ConfigEntry<bool> cfgShowPercentageDisplay;

		internal static ConfigEntry<string> cfgShowBloatedPercentage;

		internal static ConfigEntry<int> cfgPercentageFontSize;

		internal static ConfigEntry<float> cfgPercentageOffsetX;

		internal static ConfigEntry<float> cfgPercentageOffsetY;

		internal static ConfigEntry<string> cfgPercentageColor;

		internal static ConfigEntry<string> cfgBloatedFillColor;

		internal static ConfigEntry<string> cfgBloatedOutlineColor;

		internal static ConfigEntry<string> cfgBloatedIconColor;

		internal static ConfigEntry<float> cfgFullnessDecayInterval;

		internal static ConfigEntry<float> cfgFullnessDecayAmount;

		internal static ConfigEntry<float> cfgBloatedDecayInterval;

		internal static ConfigEntry<float> cfgBloatedDecayAmount;

		internal static ConfigEntry<bool> cfgEnableOvereating;

		internal static ConfigEntry<bool> cfgClearHungerBeforeFullness;

		internal static ConfigEntry<bool> cfgEnableFarting;

		internal static ConfigEntry<float> cfgFartChance;

		internal static ConfigEntry<float> cfgDecayFartChance;

		internal static ConfigEntry<bool> cfgEnableCureAllReducesBloated;

		internal static ConfigEntry<float> cfgCureAllBloatedReduction;

		internal static ConfigEntry<bool> cfgEnableBloatedSlowdown;

		internal static ConfigEntry<float> cfgBloatedMoveSpeedMultiplier;

		internal static ConfigEntry<float> cfgBloatedClimbSpeedMultiplier;

		internal static ConfigEntry<bool> cfgEnableBloatedStaminaCost;

		internal static ConfigEntry<float> cfgBloatedStaminaCostMultiplier;

		internal static ConfigEntry<bool> cfgEnableSprintFartBoost;

		internal static ConfigEntry<float> cfgSprintFartChancePerSecond;

		internal static ConfigEntry<bool> cfgEnableFartReducesBloated;

		internal static ConfigEntry<float> cfgFartBloatedReduction;

		internal static ConfigEntry<bool> cfgEnableStaminaSpeedsDecay;

		internal static ConfigEntry<float> cfgStaminaDecayBoost;

		internal static float syncedFullnessThreshold;

		internal static float syncedFullnessDecayInterval;

		internal static float syncedFullnessDecayAmount;

		internal static float syncedBloatedDecayInterval;

		internal static float syncedBloatedDecayAmount;

		internal static bool syncedEnableOvereating;

		internal static bool syncedClearHungerBeforeFullness;

		internal static bool syncedEnableFarting;

		internal static float syncedFartChance;

		internal static float syncedDecayFartChance;

		internal static bool syncedEnableCureAllReducesBloated;

		internal static float syncedCureAllBloatedReduction;

		internal static bool syncedEnableBloatedSlowdown;

		internal static float syncedBloatedMoveSpeedMultiplier;

		internal static float syncedBloatedClimbSpeedMultiplier;

		internal static bool syncedEnableBloatedStaminaCost;

		internal static float syncedBloatedStaminaCostMultiplier;

		internal static bool syncedEnableSprintFartBoost;

		internal static float syncedSprintFartChancePerSecond;

		internal static bool syncedEnableFartReducesBloated;

		internal static float syncedFartBloatedReduction;

		internal static bool syncedEnableStaminaSpeedsDecay;

		internal static float syncedStaminaDecayBoost;

		private bool _applyingRemoteConfig;

		public ManualLogSource Logger => ((BaseUnityPlugin)this).Logger;

		public static bool IsModActive => FullBellyModAuthorizationService.IsGameplayEnabled;

		internal static bool isHandshakeValid => IsModActive;

		private void Awake()
		{
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			Instance = this;
			BindConfig();
			BindLiveConfigHandlers();
			ApplySnapshot(BuildSnapshot(), fromRemote: false);
			Logger.LogInfo((object)"Full Belly 4.0.1 started (NoHunger + bloated effects + PEAKLib handshake).");
			FullBellyModAuthorizationService.Initialize();
			FullBellyManager.Initialize(Logger, syncedFullnessThreshold, cfgIgnoredItems.Value);
			((MonoBehaviour)this).StartCoroutine(BloatedUi.EnsureCreated(Logger));
			new Harmony("tony4twentys.full_belly").PatchAll();
		}

		private void OnDestroy()
		{
			BloatedEffects.Shutdown();
		}

		private void BindConfig()
		{
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Expected O, but got Unknown
			//IL_00df: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e9: Expected O, but got Unknown
			//IL_011c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0126: Expected O, but got Unknown
			//IL_0159: Unknown result type (might be due to invalid IL or missing references)
			//IL_0163: Expected O, but got Unknown
			//IL_02d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e0: Expected O, but got Unknown
			//IL_0373: Unknown result type (might be due to invalid IL or missing references)
			//IL_037d: Expected O, but got Unknown
			//IL_03b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_03ba: Expected O, but got Unknown
			//IL_040d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0417: Expected O, but got Unknown
			//IL_046a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0474: Expected O, but got Unknown
			//IL_04c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_04d1: Expected O, but got Unknown
			//IL_0524: Unknown result type (might be due to invalid IL or missing references)
			//IL_052e: Expected O, but got Unknown
			//IL_0561: Unknown result type (might be due to invalid IL or missing references)
			//IL_056b: Expected O, but got Unknown
			//IL_05be: Unknown result type (might be due to invalid IL or missing references)
			//IL_05c8: Expected O, but got Unknown
			cfgFullnessThreshold = ((BaseUnityPlugin)this).Config.Bind<float>("Full Belly", "FullnessThreshold", 0.4f, "Food consumption amount needed to become full (threshold = 100%). Host-synced.");
			cfgIgnoredItems = ((BaseUnityPlugin)this).Config.Bind<string>("Full Belly", "IgnoredItems", "", "Comma-separated item names to ignore (local only). Check logs after eating for names.");
			cfgShowPercentageDisplay = ((BaseUnityPlugin)this).Config.Bind<bool>("Full Belly - UI", "ShowPercentageDisplay", true, "Display food consumption percentage (local only).");
			cfgShowBloatedPercentage = ((BaseUnityPlugin)this).Config.Bind<string>("Full Belly - UI", "ShowBloatedPercentage", "Auto", new ConfigDescription("Show \"% Bloated\" in Full Belly's text UI (local only). Auto hides it when a status-bar numbers mod is detected (PeakStatsEx, PlayersInfo, StaminaStats, etc.) or when a number is already on the Bloated bar. Always/Never override.", (AcceptableValueBase)(object)new AcceptableValueList<string>(new string[3] { "Auto", "Always", "Never" }), Array.Empty<object>()));
			cfgPercentageFontSize = ((BaseUnityPlugin)this).Config.Bind<int>("Full Belly - UI", "PercentageFontSize", 24, new ConfigDescription("Font size for the percentage display (local only).", (AcceptableValueBase)(object)new AcceptableValueRange<int>(10, 96), Array.Empty<object>()));
			cfgPercentageOffsetX = ((BaseUnityPlugin)this).Config.Bind<float>("Full Belly - UI", "PercentageOffsetX", 0f, new ConfigDescription("X from screen center in canvas units (local only). Negative = left, positive = right. ~±960 covers full width at 1920 reference.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(-1000f, 1000f), Array.Empty<object>()));
			cfgPercentageOffsetY = ((BaseUnityPlugin)this).Config.Bind<float>("Full Belly - UI", "PercentageOffsetY", -270f, new ConfigDescription("Y from screen center in canvas units (local only). Negative = down, positive = up. ~±540 covers full height at 1080 reference. Default -270 is lower third.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(-1000f, 1000f), Array.Empty<object>()));
			cfgPercentageColor = ((BaseUnityPlugin)this).Config.Bind<string>("Full Belly - UI", "PercentageColor", "#FFFFFFFF", "Percentage text color #RRGGBB or #RRGGBBAA (local only).");
			cfgBloatedFillColor = ((BaseUnityPlugin)this).Config.Bind<string>("Full Belly - UI", "BloatedFillColor", "#FF80CCFF", "Bloated bar checkered fill tint #RRGGBB or #RRGGBBAA (local only).");
			cfgBloatedOutlineColor = ((BaseUnityPlugin)this).Config.Bind<string>("Full Belly - UI", "BloatedOutlineColor", "#FFB3E699", "Bloated bar outline/glow tint #RRGGBB or #RRGGBBAA (local only). Lower alpha = softer border.");
			cfgBloatedIconColor = ((BaseUnityPlugin)this).Config.Bind<string>("Full Belly - UI", "BloatedIconColor", "#FFE6FFFF", "Bloated stomach icon tint #RRGGBB or #RRGGBBAA (local only).");
			cfgFullnessDecayInterval = ((BaseUnityPlugin)this).Config.Bind<float>("Full Belly - Decay", "FullnessDecayInterval", 3f, "Seconds between fullness decay ticks. Host-synced.");
			cfgFullnessDecayAmount = ((BaseUnityPlugin)this).Config.Bind<float>("Full Belly - Decay", "FullnessDecayAmount", 0.01f, "Fullness decayed per tick relative to threshold. Host-synced.");
			cfgBloatedDecayInterval = ((BaseUnityPlugin)this).Config.Bind<float>("Full Belly - Decay", "BloatedDecayInterval", 3f, "Seconds between bloated decay ticks. Host-synced.");
			cfgBloatedDecayAmount = ((BaseUnityPlugin)this).Config.Bind<float>("Full Belly - Decay", "BloatedDecayAmount", 0.01f, "Bloated amount decayed per tick. Host-synced.");
			cfgEnableStaminaSpeedsDecay = ((BaseUnityPlugin)this).Config.Bind<bool>("Full Belly - Decay", "EnableStaminaSpeedsDecay", true, "Using stamina (sprint/climb/jump/etc.) also digests fullness/bloated faster. Host-synced.");
			cfgStaminaDecayBoost = ((BaseUnityPlugin)this).Config.Bind<float>("Full Belly - Decay", "StaminaDecayBoost", 0.25f, new ConfigDescription("Extra fullness/bloated digested per 1.0 stamina spent (sprint uses ~0.025/s). Host-synced.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 5f), Array.Empty<object>()));
			cfgEnableOvereating = ((BaseUnityPlugin)this).Config.Bind<bool>("Full Belly - Overeating", "EnableOvereating", true, "Eating while full can cause Bloated. Host-synced.");
			cfgClearHungerBeforeFullness = ((BaseUnityPlugin)this).Config.Bind<bool>("Full Belly", "ClearHungerBeforeFullness", false, "When on, food clears existing Hunger first; only leftover restore becomes fullness (e.g. 10% hungry + 20% food → 10% full). When off, the full restore amount always adds to fullness. Host-synced.");
			cfgEnableFarting = ((BaseUnityPlugin)this).Config.Bind<bool>("Full Belly - Farting", "EnableFarting", true, "Random farting when full / bloated decay. Host-synced.");
			cfgFartChance = ((BaseUnityPlugin)this).Config.Bind<float>("Full Belly - Farting", "FartChance", 0.1f, new ConfigDescription("Chance to fart when you eat while already full (0 = never, 1 = always). Host-synced.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
			cfgDecayFartChance = ((BaseUnityPlugin)this).Config.Bind<float>("Full Belly - Farting", "DecayFartChance", 0.25f, new ConfigDescription("Chance to fart each bloated decay tick while bloated (0 = never, 1 = always). Host-synced.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
			cfgEnableSprintFartBoost = ((BaseUnityPlugin)this).Config.Bind<bool>("Full Belly - Farting", "EnableSprintFartBoost", true, "Sprinting while bloated can trigger extra farts. Host-synced.");
			cfgSprintFartChancePerSecond = ((BaseUnityPlugin)this).Config.Bind<float>("Full Belly - Farting", "SprintFartChancePerSecond", 0.5f, new ConfigDescription("Approximate fart chance per second while sprinting bloated (0 = never, 1 ≈ once/sec). Host-synced.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
			cfgEnableFartReducesBloated = ((BaseUnityPlugin)this).Config.Bind<bool>("Full Belly - Farting", "EnableFartReducesBloated", true, "Each fart reduces bloated amount. Host-synced.");
			cfgFartBloatedReduction = ((BaseUnityPlugin)this).Config.Bind<float>("Full Belly - Farting", "FartBloatedReduction", 0.05f, new ConfigDescription("Bloated amount removed per fart (0–1, same scale as afflictions). Host-synced.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
			cfgEnableCureAllReducesBloated = ((BaseUnityPlugin)this).Config.Bind<bool>("Full Belly - CureAll", "EnableCureAllReducesBloated", true, "Using a Cure-All (multi Action_ModifyStatus heal item) also reduces Bloated. Host-synced.");
			cfgCureAllBloatedReduction = ((BaseUnityPlugin)this).Config.Bind<float>("Full Belly - CureAll", "CureAllBloatedReduction", 0.35f, new ConfigDescription("Bloated amount removed per Cure-All use (0–1; vanilla injury heal is 0.35). Host-synced.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
			cfgEnableBloatedSlowdown = ((BaseUnityPlugin)this).Config.Bind<bool>("Full Belly - BloatedEffects", "EnableBloatedSlowdown", true, "While bloated, slow ground move and climb speeds. Host-synced.");
			cfgBloatedMoveSpeedMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("Full Belly - BloatedEffects", "BloatedMoveSpeedMultiplier", 0.5f, new ConfigDescription("Ground movement force multiplier while bloated (1 = normal, lower = slower). Host-synced.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 1f), Array.Empty<object>()));
			cfgBloatedClimbSpeedMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("Full Belly - BloatedEffects", "BloatedClimbSpeedMultiplier", 0.5f, new ConfigDescription("Wall/rope/vine climb speed multiplier while bloated (1 = normal). Host-synced.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 1f), Array.Empty<object>()));
			cfgEnableBloatedStaminaCost = ((BaseUnityPlugin)this).Config.Bind<bool>("Full Belly - BloatedEffects", "EnableBloatedStaminaCost", true, "While bloated, stamina actions cost more. Host-synced.");
			cfgBloatedStaminaCostMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("Full Belly - BloatedEffects", "BloatedStaminaCostMultiplier", 2f, new ConfigDescription("Stamina usage multiplier while bloated (1 = normal, higher = drains faster). Host-synced.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 5f), Array.Empty<object>()));
		}

		private void BindLiveConfigHandlers()
		{
			cfgFullnessThreshold.SettingChanged += OnGameplaySettingChanged;
			cfgIgnoredItems.SettingChanged += OnLocalOnlySettingChanged;
			cfgFullnessDecayInterval.SettingChanged += OnGameplaySettingChanged;
			cfgFullnessDecayAmount.SettingChanged += OnGameplaySettingChanged;
			cfgBloatedDecayInterval.SettingChanged += OnGameplaySettingChanged;
			cfgBloatedDecayAmount.SettingChanged += OnGameplaySettingChanged;
			cfgEnableOvereating.SettingChanged += OnGameplaySettingChanged;
			cfgClearHungerBeforeFullness.SettingChanged += OnGameplaySettingChanged;
			cfgEnableFarting.SettingChanged += OnGameplaySettingChanged;
			cfgFartChance.SettingChanged += OnGameplaySettingChanged;
			cfgDecayFartChance.SettingChanged += OnGameplaySettingChanged;
			cfgEnableCureAllReducesBloated.SettingChanged += OnGameplaySettingChanged;
			cfgCureAllBloatedReduction.SettingChanged += OnGameplaySettingChanged;
			cfgEnableBloatedSlowdown.SettingChanged += OnGameplaySettingChanged;
			cfgBloatedMoveSpeedMultiplier.SettingChanged += OnGameplaySettingChanged;
			cfgBloatedClimbSpeedMultiplier.SettingChanged += OnGameplaySettingChanged;
			cfgEnableBloatedStaminaCost.SettingChanged += OnGameplaySettingChanged;
			cfgBloatedStaminaCostMultiplier.SettingChanged += OnGameplaySettingChanged;
			cfgEnableSprintFartBoost.SettingChanged += OnGameplaySettingChanged;
			cfgSprintFartChancePerSecond.SettingChanged += OnGameplaySettingChanged;
			cfgEnableFartReducesBloated.SettingChanged += OnGameplaySettingChanged;
			cfgFartBloatedReduction.SettingChanged += OnGameplaySettingChanged;
			cfgEnableStaminaSpeedsDecay.SettingChanged += OnGameplaySettingChanged;
			cfgStaminaDecayBoost.SettingChanged += OnGameplaySettingChanged;
			cfgShowPercentageDisplay.SettingChanged += OnUiSettingChanged;
			cfgShowBloatedPercentage.SettingChanged += OnUiSettingChanged;
			cfgPercentageFontSize.SettingChanged += OnUiSettingChanged;
			cfgPercentageOffsetX.SettingChanged += OnUiSettingChanged;
			cfgPercentageOffsetY.SettingChanged += OnUiSettingChanged;
			cfgPercentageColor.SettingChanged += OnUiSettingChanged;
			cfgBloatedFillColor.SettingChanged += OnUiSettingChanged;
			cfgBloatedOutlineColor.SettingChanged += OnUiSettingChanged;
			cfgBloatedIconColor.SettingChanged += OnUiSettingChanged;
		}

		private void OnLocalOnlySettingChanged(object sender, EventArgs e)
		{
			if (!_applyingRemoteConfig)
			{
				FullBellyManager.instance?.ApplyLocalSettings(syncedFullnessThreshold, cfgIgnoredItems.Value);
			}
		}

		private void OnUiSettingChanged(object sender, EventArgs e)
		{
			FullBellyManager.instance?.RefreshPercentageStyle();
			BloatedUi.RefreshStyle();
		}

		private void OnGameplaySettingChanged(object sender, EventArgs e)
		{
			if (!_applyingRemoteConfig && (!PhotonNetwork.InRoom || PhotonNetwork.IsMasterClient))
			{
				FullBellyModAuthorizationService.OnHostConfigChangedLocally();
			}
		}

		internal FullBellyConfigSnapshot BuildSnapshot()
		{
			return new FullBellyConfigSnapshot
			{
				FullnessThreshold = cfgFullnessThreshold.Value,
				FullnessDecayInterval = cfgFullnessDecayInterval.Value,
				FullnessDecayAmount = cfgFullnessDecayAmount.Value,
				BloatedDecayInterval = cfgBloatedDecayInterval.Value,
				BloatedDecayAmount = cfgBloatedDecayAmount.Value,
				EnableOvereating = cfgEnableOvereating.Value,
				ClearHungerBeforeFullness = cfgClearHungerBeforeFullness.Value,
				EnableFarting = cfgEnableFarting.Value,
				FartChance = Mathf.Clamp01(cfgFartChance.Value),
				DecayFartChance = Mathf.Clamp01(cfgDecayFartChance.Value),
				EnableCureAllReducesBloated = cfgEnableCureAllReducesBloated.Value,
				CureAllBloatedReduction = Mathf.Clamp01(cfgCureAllBloatedReduction.Value),
				EnableBloatedSlowdown = cfgEnableBloatedSlowdown.Value,
				BloatedMoveSpeedMultiplier = Mathf.Clamp(cfgBloatedMoveSpeedMultiplier.Value, 0.1f, 1f),
				BloatedClimbSpeedMultiplier = Mathf.Clamp(cfgBloatedClimbSpeedMultiplier.Value, 0.1f, 1f),
				EnableBloatedStaminaCost = cfgEnableBloatedStaminaCost.Value,
				BloatedStaminaCostMultiplier = Mathf.Max(1f, cfgBloatedStaminaCostMultiplier.Value),
				EnableSprintFartBoost = cfgEnableSprintFartBoost.Value,
				SprintFartChancePerSecond = Mathf.Clamp01(cfgSprintFartChancePerSecond.Value),
				EnableFartReducesBloated = cfgEnableFartReducesBloated.Value,
				FartBloatedReduction = Mathf.Clamp01(cfgFartBloatedReduction.Value),
				EnableStaminaSpeedsDecay = cfgEnableStaminaSpeedsDecay.Value,
				StaminaDecayBoost = Mathf.Max(0f, cfgStaminaDecayBoost.Value)
			};
		}

		internal void ApplySnapshot(FullBellyConfigSnapshot snapshot, bool fromRemote)
		{
			if (snapshot == null)
			{
				return;
			}
			_applyingRemoteConfig = true;
			try
			{
				syncedFullnessThreshold = snapshot.FullnessThreshold;
				syncedFullnessDecayInterval = snapshot.FullnessDecayInterval;
				syncedFullnessDecayAmount = snapshot.FullnessDecayAmount;
				syncedBloatedDecayInterval = snapshot.BloatedDecayInterval;
				syncedBloatedDecayAmount = snapshot.BloatedDecayAmount;
				syncedEnableOvereating = snapshot.EnableOvereating;
				syncedClearHungerBeforeFullness = snapshot.ClearHungerBeforeFullness;
				syncedEnableFarting = snapshot.EnableFarting;
				syncedFartChance = Mathf.Clamp01(snapshot.FartChance);
				syncedDecayFartChance = Mathf.Clamp01(snapshot.DecayFartChance);
				syncedEnableCureAllReducesBloated = snapshot.EnableCureAllReducesBloated;
				syncedCureAllBloatedReduction = Mathf.Clamp01(snapshot.CureAllBloatedReduction);
				syncedEnableBloatedSlowdown = snapshot.EnableBloatedSlowdown;
				syncedBloatedMoveSpeedMultiplier = Mathf.Clamp(snapshot.BloatedMoveSpeedMultiplier, 0.1f, 1f);
				syncedBloatedClimbSpeedMultiplier = Mathf.Clamp(snapshot.BloatedClimbSpeedMultiplier, 0.1f, 1f);
				syncedEnableBloatedStaminaCost = snapshot.EnableBloatedStaminaCost;
				syncedBloatedStaminaCostMultiplier = Mathf.Max(1f, snapshot.BloatedStaminaCostMultiplier);
				syncedEnableSprintFartBoost = snapshot.EnableSprintFartBoost;
				syncedSprintFartChancePerSecond = Mathf.Clamp01(snapshot.SprintFartChancePerSecond);
				syncedEnableFartReducesBloated = snapshot.EnableFartReducesBloated;
				syncedFartBloatedReduction = Mathf.Clamp01(snapshot.FartBloatedReduction);
				syncedEnableStaminaSpeedsDecay = snapshot.EnableStaminaSpeedsDecay;
				syncedStaminaDecayBoost = Mathf.Max(0f, snapshot.StaminaDecayBoost);
			}
			finally
			{
				_applyingRemoteConfig = false;
			}
			FullBellyManager.instance?.ApplyLocalSettings(syncedFullnessThreshold, cfgIgnoredItems.Value);
			FullBellyManager.instance?.UpdateSyncedThreshold(syncedFullnessThreshold);
			if (!syncedEnableOvereating)
			{
				CharacterAfflictions val = Character.localCharacter?.refs?.afflictions;
				if ((Object)(object)val != (Object)null)
				{
					FullBellyManager.instance?.UpdateBloatedStatus(val);
				}
			}
			if (fromRemote)
			{
				Logger.LogInfo((object)$"[Full Belly] Applied host config — threshold {syncedFullnessThreshold * 100f:0.00}%");
			}
		}
	}
	internal static class GameFontUtil
	{
		private static TMP_FontAsset _cached;

		internal static TMP_FontAsset GetGameFont()
		{
			if ((Object)(object)_cached != (Object)null)
			{
				return _cached;
			}
			if ((Object)(object)FontFallbackSwapper.instance != (Object)null && (Object)(object)FontFallbackSwapper.instance.mainBaseFont != (Object)null)
			{
				_cached = FontFallbackSwapper.instance.mainBaseFont;
				return _cached;
			}
			GUIManager instance = GUIManager.instance;
			if ((Object)(object)instance != (Object)null)
			{
				TextMeshProUGUI heroText = instance.heroText;
				object obj = ((heroText != null) ? ((TMP_Text)heroText).font : null);
				if (obj == null)
				{
					TextMeshProUGUI itemPromptMain = instance.itemPromptMain;
					obj = ((itemPromptMain != null) ? ((TMP_Text)itemPromptMain).font : null);
					if (obj == null)
					{
						TextMeshProUGUI interactPromptText = instance.interactPromptText;
						obj = ((interactPromptText != null) ? ((TMP_Text)interactPromptText).font : null);
						if (obj == null)
						{
							TextMeshProUGUI text = instance.text;
							obj = ((text != null) ? ((TMP_Text)text).font : null);
						}
					}
				}
				TMP_FontAsset val = (TMP_FontAsset)obj;
				if ((Object)(object)val != (Object)null)
				{
					_cached = val;
					return _cached;
				}
			}
			TMP_FontAsset[] source = Resources.FindObjectsOfTypeAll<TMP_FontAsset>();
			_cached = ((IEnumerable<TMP_FontAsset>)source).FirstOrDefault((Func<TMP_FontAsset, bool>)((TMP_FontAsset f) => (Object)(object)f != (Object)null && ((Object)f).name != null && ((Object)f).name.IndexOf("Daruma", StringComparison.OrdinalIgnoreCase) >= 0));
			if ((Object)(object)_cached != (Object)null)
			{
				return _cached;
			}
			TextMeshProUGUI val2 = Object.FindFirstObjectByType<TextMeshProUGUI>();
			if ((Object)(object)val2 != (Object)null && (Object)(object)((TMP_Text)val2).font != (Object)null)
			{
				_cached = ((TMP_Text)val2).font;
			}
			return _cached;
		}

		internal static void ApplyTo(TextMeshProUGUI text)
		{
			if (!((Object)(object)text == (Object)null))
			{
				TMP_FontAsset gameFont = GetGameFont();
				if ((Object)(object)gameFont != (Object)null)
				{
					((TMP_Text)text).font = gameFont;
				}
			}
		}

		internal static void InvalidateCache()
		{
			_cached = null;
		}
	}
	internal static class NoHungerGuard
	{
		private static readonly Affliction_NoHunger BuffTemplate = new Affliction_NoHunger
		{
			totalTime = 10f
		};

		private const float RefreshInterval = 1f;

		private static float _lastApplied = -999f;

		private static bool _holdingBuff;

		internal static void Tick(CharacterAfflictions afflictions, bool shouldBlockHunger)
		{
			if ((Object)(object)afflictions == (Object)null)
			{
				return;
			}
			if (!shouldBlockHunger)
			{
				if (_holdingBuff)
				{
					Clear(afflictions);
				}
			}
			else if (afflictions.canGetHungry || !(Time.time - _lastApplied < 1f))
			{
				afflictions.AddAffliction((Affliction)(object)BuffTemplate, false);
				_lastApplied = Time.time;
				_holdingBuff = true;
			}
		}

		internal static void ForceRefresh(CharacterAfflictions afflictions)
		{
			if (!((Object)(object)afflictions == (Object)null))
			{
				afflictions.AddAffliction((Affliction)(object)BuffTemplate, false);
				_lastApplied = Time.time;
				_holdingBuff = true;
			}
		}

		internal static void Clear(CharacterAfflictions afflictions)
		{
			if ((Object)(object)afflictions == (Object)null)
			{
				_holdingBuff = false;
				_lastApplied = -999f;
				return;
			}
			Affliction val = default(Affliction);
			if (afflictions.HasAfflictionType((AfflictionType)21, ref val))
			{
				afflictions.RemoveAffliction((AfflictionType)21);
			}
			_holdingBuff = false;
			_lastApplied = -999f;
		}
	}
	[HarmonyPatch(typeof(Character), "Start")]
	internal static class Patch_Character_Start
	{
		private static void Postfix(Character __instance)
		{
			if (!((Object)(object)__instance == (Object)null) && !((Object)(object)((MonoBehaviourPun)__instance).photonView == (Object)null) && ((MonoBehaviourPun)__instance).photonView.IsMine)
			{
				__instance.reviveAction = (Action)Delegate.Combine(__instance.reviveAction, (Action)delegate
				{
					FullBellyManager.HandleDeathStateChange(isNowDead: false);
				});
			}
		}
	}
	[HarmonyPatch(typeof(Character), "RPCA_Die")]
	internal static class Patch_Character_RPCA_Die
	{
		private static void Postfix(Character __instance)
		{
			if (!((Object)(object)__instance == (Object)null) && !((Object)(object)((MonoBehaviourPun)__instance).photonView == (Object)null) && ((MonoBehaviourPun)__instance).photonView.IsMine)
			{
				FullBellyManager.HandleDeathStateChange(isNowDead: true);
			}
		}
	}
	[HarmonyPatch(typeof(StaminaBar), "Start")]
	internal static class Patch_StaminaBar_Start_InjectBloated
	{
		private static void Prefix(StaminaBar __instance)
		{
			BloatedUi.EnsureOnStaminaBar(__instance, FullBellyPlugin.Instance?.Logger);
		}

		private static void Postfix(StaminaBar __instance)
		{
			BloatedUi.EnsureInCachedAfflictions(__instance, FullBellyPlugin.Instance?.Logger);
		}
	}
	[HarmonyPatch(typeof(CharacterAfflictions), "GetCurrentStatus", new Type[] { typeof(STATUSTYPE) })]
	internal static class Patch_GetCurrentStatus_Bloated
	{
		private static void Postfix(CharacterAfflictions __instance, STATUSTYPE statusType, ref float __result)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			if (!BloatedUi.IsBloatedType(statusType))
			{
				return;
			}
			FullBellyManager instance = FullBellyManager.instance;
			if (!((Object)(object)instance == (Object)null) && !((Object)(object)__instance == (Object)null))
			{
				Character character = __instance.character;
				if (!((Object)(object)character == (Object)null) && !((Object)(object)((MonoBehaviourPun)character).photonView == (Object)null) && ((MonoBehaviourPun)character).photonView.IsMine)
				{
					__result = instance.GetBloatedAmount();
				}
			}
		}
	}
	[HarmonyPatch(/*Could not decode attribute arguments.*/)]
	internal static class Patch_statusSum_IncludeBloated
	{
		private static void Postfix(CharacterAfflictions __instance, ref float __result)
		{
			FullBellyManager instance = FullBellyManager.instance;
			if ((Object)(object)instance == (Object)null)
			{
				return;
			}
			Character val = (((Object)(object)__instance != (Object)null) ? __instance.character : null);
			if (!((Object)(object)val == (Object)null) && !((Object)(object)((MonoBehaviourPun)val).photonView == (Object)null) && ((MonoBehaviourPun)val).photonView.IsMine)
			{
				float bloatedAmount = instance.GetBloatedAmount();
				if (bloatedAmount > 0f)
				{
					__result += bloatedAmount;
				}
			}
		}
	}
	[HarmonyPatch(typeof(CharacterAfflictions), "SetAll")]
	internal static class Patch_SetAll_LengthSafe
	{
		private static bool Prefix(CharacterAfflictions __instance, float[] statuses)
		{
			if ((Object)(object)__instance == (Object)null || statuses == null || __instance.currentStatuses == null)
			{
				return false;
			}
			if (statuses.Length == __instance.currentStatuses.Length)
			{
				return true;
			}
			int num = Math.Min(statuses.Length, __instance.currentStatuses.Length);
			for (int i = 0; i < num; i++)
			{
				if (i != 13)
				{
					float num2 = statuses[i] - __instance.currentStatuses[i];
					if (num2 > 0f)
					{
						__instance.AddStatus((STATUSTYPE)i, num2, true, false, true);
					}
					else if (num2 < 0f)
					{
						__instance.SubtractStatus((STATUSTYPE)i, 0f - num2, true, false);
					}
				}
			}
			return false;
		}
	}
	[HarmonyPatch(typeof(CharacterAfflictions), "SetStatus")]
	internal static class Patch_SetStatus_BlockHungerWhileFull
	{
		private static void Prefix(CharacterAfflictions __instance, STATUSTYPE statusType, ref float amount)
		{
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Invalid comparison between Unknown and I4
			if (!((Object)(object)__instance == (Object)null) && (int)statusType == 1 && FullBellyManager.ShouldBlockHungerFor(__instance.character))
			{
				float currentStatus = __instance.GetCurrentStatus((STATUSTYPE)1);
				if (amount > currentStatus)
				{
					amount = currentStatus;
				}
			}
		}
	}
	[HarmonyPatch(typeof(CharacterData), "RPC_SyncSkeleton")]
	internal static class Patch_RPC_SyncSkeleton
	{
		private static void Postfix(CharacterData __instance, bool active)
		{
			if (active && !((Object)(object)__instance == (Object)null))
			{
				Character character = __instance.character;
				if (!((Object)(object)character == (Object)null) && !((Object)(object)character != (Object)(object)Character.localCharacter) && !((Object)(object)((MonoBehaviourPun)character).photonView == (Object)null) && ((MonoBehaviourPun)character).photonView.IsMine)
				{
					FullBellyManager.HandleBecameSkeleton();
				}
			}
		}
	}
	[HarmonyPatch(typeof(CharacterAfflictions), "PrettyPrintStaminaBar")]
	internal static class Patch_PrettyPrint_Safe
	{
		private static Exception Finalizer(Exception __exception)
		{
			if (__exception is IndexOutOfRangeException || __exception is ArgumentOutOfRangeException)
			{
				return null;
			}
			return __exception;
		}
	}
	[HarmonyPatch(typeof(Action_ModifyStatus), "RunAction")]
	internal static class Patch_Action_ModifyStatus_CureAll
	{
		private static int _lastFrame = -1;

		private static int _lastItemId;

		private static void Postfix(Action_ModifyStatus __instance)
		{
			if ((Object)(object)__instance == (Object)null || __instance.changeAmount >= 0f || (Object)(object)((ItemActionBase)__instance).character == (Object)null || (Object)(object)((ItemActionBase)__instance).character != (Object)(object)Character.localCharacter || (Object)(object)((MonoBehaviourPun)((ItemActionBase)__instance).character).photonView == (Object)null || !((MonoBehaviourPun)((ItemActionBase)__instance).character).photonView.IsMine)
			{
				return;
			}
			Item item = ((ItemActionBase)__instance).item;
			if (!((Object)(object)item == (Object)null) && LooksLikeCureAll(item))
			{
				int instanceID = ((Object)item).GetInstanceID();
				int frameCount = Time.frameCount;
				if (frameCount != _lastFrame || instanceID != _lastItemId)
				{
					_lastFrame = frameCount;
					_lastItemId = instanceID;
					FullBellyManager.HandleCureAllUsed(((Object)item).name);
				}
			}
		}

		internal static bool LooksLikeCureAll(Item item)
		{
			if ((Object)(object)item == (Object)null)
			{
				return false;
			}
			string text = ((Object)item).name ?? string.Empty;
			if (text.IndexOf("Cure-All", StringComparison.OrdinalIgnoreCase) >= 0 || text.IndexOf("CureAll", StringComparison.OrdinalIgnoreCase) >= 0)
			{
				return true;
			}
			Action_ModifyStatus[] components = ((Component)item).GetComponents<Action_ModifyStatus>();
			if (components == null || components.Length == 0)
			{
				return false;
			}
			int num = 0;
			for (int i = 0; i < components.Length; i++)
			{
				if ((Object)(object)components[i] != (Object)null && components[i].changeAmount < 0f)
				{
					num++;
				}
			}
			return num >= 4;
		}
	}
	[HarmonyPatch(typeof(Affliction_HealAll), "OnApplied")]
	internal static class Patch_Affliction_HealAll
	{
		private static void Postfix(Affliction_HealAll __instance)
		{
			if (!((Object)(object)((Affliction)(__instance?)).character == (Object)null) && !((Object)(object)((Affliction)__instance).character != (Object)(object)Character.localCharacter) && !((Object)(object)((MonoBehaviourPun)((Affliction)__instance).character).photonView == (Object)null) && ((MonoBehaviourPun)((Affliction)__instance).character).photonView.IsMine)
			{
				string itemName = ((Object)((Affliction)__instance).character).name ?? "HealAll";
				FullBellyManager.HandleCureAllUsed(itemName);
			}
		}
	}
	internal static class SpriteLoader
	{
		private static Sprite _bloatedIcon;

		private static Sprite _checkeredOverlay;

		internal static Sprite BloatedIcon
		{
			get
			{
				//IL_002c: Unknown result type (might be due to invalid IL or missing references)
				object obj = _bloatedIcon;
				if (obj == null)
				{
					obj = FromEmbedded("FullBellyMod.Sprites.BloatedIcon.png", (FilterMode)1) ?? CreatePlaceholder(new Color(1f, 0.5f, 0.8f, 1f));
					_bloatedIcon = (Sprite)obj;
				}
				return (Sprite)obj;
			}
		}

		internal static Sprite CheckeredOverlay => _checkeredOverlay ?? (_checkeredOverlay = FromEmbedded("FullBellyMod.Sprites.CheckeredOverlay.png", (FilterMode)0));

		internal static Sprite FromEmbedded(string resourceName, FilterMode filterMode = (FilterMode)1)
		{
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Expected O, but got Unknown
			//IL_00f7: 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_0129: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				Assembly executingAssembly = Assembly.GetExecutingAssembly();
				using Stream stream = executingAssembly.GetManifestResourceStream(resourceName);
				if (stream == null)
				{
					return null;
				}
				byte[] array = new byte[stream.Length];
				int num;
				for (int i = 0; i < array.Length; i += num)
				{
					num = stream.Read(array, i, array.Length - i);
					if (num <= 0)
					{
						break;
					}
				}
				Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false);
				MethodInfo methodInfo = Type.GetType("UnityEngine.ImageConversion, UnityEngine.ImageConversionModule")?.GetMethod("LoadImage", new Type[2]
				{
					typeof(Texture2D),
					typeof(byte[])
				});
				if (methodInfo == null || !(bool)methodInfo.Invoke(null, new object[2] { val, array }))
				{
					return null;