Decompiled source of Full Belly v4.0.1
tony4twentys-Full Belly.dll
Decompiled a month ago
The result has been truncated due to the large size, download it to view full contents!
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;