The BepInEx console will not appear when launching like it does for other games on Thunderstore (you can turn it back on in your BepInEx.cfg file). If your PEAK crashes on startup, add -dx12 to your launch parameters.
Decompiled source of Full Belly v2.2.1
Full Belly.dll
Decompiled 3 weeks agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Photon.Pun; 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: AssemblyVersion("1.0.0.0")] namespace FullBellyMod; [BepInPlugin("tony4twentys.full_belly", "Full Belly", "2.1.1")] [BepInProcess("PEAK.exe")] public class FullBellyPlugin : BaseUnityPlugin { private static ConfigEntry<float> cfgFullnessDuration; private static ConfigEntry<float> cfgAccumulationWindow; private static ConfigEntry<float> cfgFullnessThreshold; private static ConfigEntry<string> cfgIgnoredItems; private static ConfigEntry<bool> cfgShowFullnessTimer; private void Awake() { //IL_0105: Unknown result type (might be due to invalid IL or missing references) //IL_010b: Expected O, but got Unknown cfgFullnessDuration = ((BaseUnityPlugin)this).Config.Bind<float>("Full Belly", "FullnessDuration", 300f, "Duration in seconds that hunger is paused after reaching fullness."); cfgAccumulationWindow = ((BaseUnityPlugin)this).Config.Bind<float>("Full Belly", "AccumulationWindow", 120f, "Time window in seconds to accumulate small hunger restorations."); cfgFullnessThreshold = ((BaseUnityPlugin)this).Config.Bind<float>("Full Belly", "FullnessThreshold", 0.25f, "Total hunger restoration percentage needed to trigger fullness."); cfgIgnoredItems = ((BaseUnityPlugin)this).Config.Bind<string>("Full Belly", "IgnoredItems", "", "Comma-separated list of item names to ignore when calculating fullness.Apple Berry Green,Apple Berry Red,Apple Berry Yellow,Airplane Food,Berrynana Blue,Berrynana Brown,Berrynana Pink,Berrynana Yellow,Clusterberry Black,Clusterberry Red,Clusterberry Yellow,Cure-All,Egg,Granola Bar,Item_Coconut_half,Item_Honeycomb,Kingberry Green,Kingberry Purple,Kingberry Yellow,Lollipop,Marshmallow,MedicinalRoot,Mushroom Chubby,Mushroom Cluster Poison,Mushroom Cluster,Mushroom Glow,Mushroom Lace Poison,Mushroom Lace,Mushroom Normie Poison,Mushroom Normie,Napberry,PandorasBox,Pepper Berry,ScoutCookies,TrailMix,Winterberry Orange,Winterberry Yellow,Glizzy."); cfgShowFullnessTimer = ((BaseUnityPlugin)this).Config.Bind<bool>("Full Belly", "ShowFullnessTimer", true, "Display countdown timer for fullness expiration."); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Full Belly mod started."); FullBellyManager.Initialize(((BaseUnityPlugin)this).Logger, cfgFullnessDuration.Value, cfgAccumulationWindow.Value, cfgFullnessThreshold.Value, cfgIgnoredItems.Value, cfgShowFullnessTimer.Value); Harmony val = new Harmony("tony4twentys.full_belly"); val.PatchAll(); } } public class FullBellyManager : MonoBehaviour { [CompilerGenerated] private sealed class <ApplyFullness>d__22 : IEnumerator<object>, IDisposable, IEnumerator { private int <>1__state; private object <>2__current; public CharacterAfflictions afflictions; public FullBellyManager <>4__this; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <ApplyFullness>d__22(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Expected O, but got Unknown //IL_00d0: Unknown result type (might be due to invalid IL or missing references) switch (<>1__state) { default: return false; case 0: <>1__state = -1; <>4__this.isFull = true; afflictions.hungerPerSecond = 0f; <>4__this.logger.LogInfo((object)$"Full Belly active for {<>4__this.fullnessDuration} seconds."); <>2__current = (object)new WaitForSeconds(<>4__this.fullnessDuration); <>1__state = 1; return true; case 1: <>1__state = -1; afflictions.hungerPerSecond = <>4__this.cachedHungerRate; <>4__this.isFull = false; <>4__this.logger.LogInfo((object)"Full Belly expired. Hunger gain restored."); <>4__this.ShowPopup("Getting hungry again", Color.yellow); return false; } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } [CompilerGenerated] private sealed class <FullnessCountdown>d__19 : IEnumerator<object>, IDisposable, IEnumerator { private int <>1__state; private object <>2__current; public float duration; public FullBellyManager <>4__this; private float <remaining>5__1; private int <minutes>5__2; private int <seconds>5__3; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <FullnessCountdown>d__19(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Expected O, but got Unknown switch (<>1__state) { default: return false; case 0: <>1__state = -1; <remaining>5__1 = duration; <>4__this.CreateOrUpdatePersistentTimer(); break; case 1: <>1__state = -1; <remaining>5__1 -= 1f; break; } if (<remaining>5__1 > 0f) { <minutes>5__2 = Mathf.FloorToInt(<remaining>5__1 / 60f); <seconds>5__3 = Mathf.FloorToInt(<remaining>5__1 % 60f); ((TMP_Text)<>4__this.timerText).text = $"Belly Full: {<minutes>5__2:0}:{<seconds>5__3:00}"; ((Graphic)<>4__this.timerText).color = Color.green; <>2__current = (object)new WaitForSeconds(1f); <>1__state = 1; return true; } TextMeshProUGUI timerText = <>4__this.timerText; Object.Destroy((Object)(object)((timerText != null) ? ((Component)timerText).gameObject : null)); <>4__this.timerText = null; return false; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } private static GameObject managerObject; private static FullBellyManager instance; private ManualLogSource logger; private float hungerRestoreAccumulator; private float timeSinceLastRestore; private float accumulationWindow; private float fullnessThreshold; private float fullnessDuration; private Coroutine fullnessCoroutine; private Coroutine timerCoroutine; private bool isFull; private bool hasLoggedReset; private float cachedHungerRate = -1f; private bool showFullnessTimer; private readonly Queue<(string itemName, float value)> restoreIntents = new Queue<(string, float)>(); private HashSet<string> ignoredItems = new HashSet<string>(); private TextMeshProUGUI timerText; public static void Initialize(ManualLogSource log, float duration, float window, float threshold, string ignoredItemsCSV, bool showTimer) { //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.fullnessDuration = duration; instance.accumulationWindow = window; instance.fullnessThreshold = threshold; instance.showFullnessTimer = showTimer; instance.ignoredItems = new HashSet<string>(from s in ignoredItemsCSV.Split(new char[1] { ',' }) select s.Trim() into s where !string.IsNullOrEmpty(s) select s, StringComparer.OrdinalIgnoreCase); SceneManager.activeSceneChanged += instance.OnSceneChanged; } private void OnSceneChanged(Scene oldScene, Scene newScene) { logger.LogInfo((object)"Scene changed. Resetting Full Belly state."); if (fullnessCoroutine != null) { ((MonoBehaviour)this).StopCoroutine(fullnessCoroutine); fullnessCoroutine = null; } if (timerCoroutine != null) { ((MonoBehaviour)this).StopCoroutine(timerCoroutine); timerCoroutine = null; } if ((Object)(object)timerText != (Object)null) { Object.Destroy((Object)(object)((Component)timerText).gameObject); timerText = null; } if (isFull && fullnessCoroutine != null && (Object)(object)Character.localCharacter?.refs?.afflictions != (Object)null) { ((MonoBehaviour)this).StopCoroutine(fullnessCoroutine); if (timerCoroutine != null) { ((MonoBehaviour)this).StopCoroutine(timerCoroutine); } Character.localCharacter.refs.afflictions.hungerPerSecond = 0.0005f; logger.LogInfo((object)"Full Belly effect cancelled due to scene change."); if ((Object)(object)timerText != (Object)null) { Object.Destroy((Object)(object)((Component)timerText).gameObject); timerText = null; } } isFull = false; hungerRestoreAccumulator = 0f; timeSinceLastRestore = 0f; hasLoggedReset = false; restoreIntents.Clear(); try { float num = 0.0005f * Ascents.hungerRateMultiplier; if (num > 0f) { instance.cachedHungerRate = num; logger.LogInfo((object)$"[Scene Init] Cached base hungerPerSecond: {num}"); } else { logger.LogWarning((object)"[Scene Init] Hunger rate multiplier returned zero or negative. Skipping cache."); } } catch (Exception ex) { logger.LogWarning((object)("[Scene Init] Could not determine hunger rate: " + ex.Message)); } } [IteratorStateMachine(typeof(<FullnessCountdown>d__19))] private IEnumerator FullnessCountdown(float duration) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <FullnessCountdown>d__19(0) { <>4__this = this, duration = duration }; } private void CreateOrUpdatePersistentTimer() { //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Expected O, but got Unknown //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Expected O, but got Unknown //IL_0102: Unknown result type (might be due to invalid IL or missing references) //IL_0119: Unknown result type (might be due to invalid IL or missing references) //IL_0130: Unknown result type (might be due to invalid IL or missing references) //IL_0147: Unknown result type (might be due to invalid IL or missing references) //IL_015e: Unknown result type (might be due to invalid IL or missing references) GameObject obj = GameObject.Find("FullBellyNotificationCanvas"); Canvas val = ((obj != null) ? obj.GetComponent<Canvas>() : null); if ((Object)(object)val == (Object)null) { GameObject val2 = new GameObject("FullBellyNotificationCanvas"); val = val2.AddComponent<Canvas>(); val.renderMode = (RenderMode)0; val.sortingOrder = 999; val2.AddComponent<CanvasScaler>(); val2.AddComponent<GraphicRaycaster>(); Object.DontDestroyOnLoad((Object)(object)val2); } if ((Object)(object)timerText == (Object)null) { GameObject val3 = new GameObject("FullBellyTimerText"); val3.transform.SetParent(((Component)val).transform); timerText = val3.AddComponent<TextMeshProUGUI>(); ((TMP_Text)timerText).fontSize = 30f; ((TMP_Text)timerText).alignment = (TextAlignmentOptions)514; ((Graphic)timerText).raycastTarget = false; ((TMP_Text)timerText).enableWordWrapping = false; ((TMP_Text)timerText).fontStyle = (FontStyles)1; RectTransform component = ((Component)timerText).GetComponent<RectTransform>(); component.sizeDelta = new Vector2(400f, 100f); component.anchoredPosition = new Vector2(-500f, -400f); component.anchorMin = new Vector2(0.5f, 0.5f); component.anchorMax = new Vector2(0.5f, 0.5f); component.pivot = new Vector2(0.5f, 0.5f); } } private void LateUpdate() { timeSinceLastRestore += Time.deltaTime; Character localCharacter = Character.localCharacter; if ((Object)(object)localCharacter == (Object)null || localCharacter.refs == null || (Object)(object)localCharacter.refs.afflictions == (Object)null) { return; } if (!isFull && localCharacter.refs.afflictions.hungerPerSecond <= 0f && cachedHungerRate > 0f) { localCharacter.refs.afflictions.hungerPerSecond = cachedHungerRate; logger.LogWarning((object)$"[Fallback Reset] hungerPerSecond was 0 while not full. Restored to cached value: {cachedHungerRate}"); } while (restoreIntents.Count > 0) { var (arg, num) = restoreIntents.Dequeue(); logger.LogInfo((object)$"Applying hunger from: {arg}, value: {num * 100f:0.00}%"); if (num >= fullnessThreshold || hungerRestoreAccumulator + num >= fullnessThreshold) { logger.LogInfo((object)"Fullness triggered."); if (fullnessCoroutine != null) { ((MonoBehaviour)this).StopCoroutine(fullnessCoroutine); } if (timerCoroutine != null) { ((MonoBehaviour)this).StopCoroutine(timerCoroutine); } fullnessCoroutine = ((MonoBehaviour)this).StartCoroutine(ApplyFullness(localCharacter.refs.afflictions)); if (showFullnessTimer) { timerCoroutine = ((MonoBehaviour)this).StartCoroutine(FullnessCountdown(fullnessDuration)); } hungerRestoreAccumulator = 0f; timeSinceLastRestore = 0f; hasLoggedReset = false; } else { hungerRestoreAccumulator += num; timeSinceLastRestore = 0f; hasLoggedReset = false; logger.LogInfo((object)$"Accumulated: {num * 100f:0.00}%, Total: {hungerRestoreAccumulator * 100f:0.00}%"); } } if (timeSinceLastRestore >= accumulationWindow && !hasLoggedReset) { logger.LogInfo((object)"Accumulation window expired. Resetting."); hungerRestoreAccumulator = 0f; hasLoggedReset = true; } } [IteratorStateMachine(typeof(<ApplyFullness>d__22))] private IEnumerator ApplyFullness(CharacterAfflictions afflictions) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <ApplyFullness>d__22(0) { <>4__this = this, afflictions = afflictions }; } private void ShowPopup(string message, Color color) { //IL_002c: Unknown result type (might be due to invalid IL or missing references) CreateOrUpdatePersistentTimer(); if ((Object)(object)timerText != (Object)null) { ((TMP_Text)timerText).text = message; ((Graphic)timerText).color = color; } } public static void HandleHungerRestore(string itemName, float hungerRestored) { if (!((Object)(object)instance == (Object)null) && !(hungerRestored <= 0f)) { string text = itemName.Replace("(Clone)", "").Trim(); if (text.Contains(":")) { text = text.Split(new char[1] { ':' }).Last(); } if (instance.ignoredItems.Contains(text)) { instance.logger.LogInfo((object)("[FOOD IGNORED] " + text + " (ignored by config)")); return; } instance.logger.LogInfo((object)$"[FOOD LOG] Used: {text}, hunger restore: {hungerRestored * 100f:0.00}%"); instance.restoreIntents.Enqueue((text, hungerRestored)); } } } [HarmonyPatch(typeof(Item))] [HarmonyPatch(/*Could not decode attribute arguments.*/)] public class Patch_FinishCastPrimary { public static MethodBase TargetMethod() { return typeof(Item).GetMethod("FinishCastPrimary", BindingFlags.Instance | BindingFlags.NonPublic); } public static void Postfix(Item __instance) { if ((Object)(object)__instance == (Object)null) { return; } Character localCharacter = Character.localCharacter; if ((Object)(object)__instance.holderCharacter != (Object)(object)localCharacter) { return; } ItemAction[] components = ((Component)__instance).GetComponents<ItemAction>(); ItemAction[] array = components; foreach (ItemAction val in array) { if (((object)val).GetType().Name == "Action_RestoreHunger") { FieldInfo field = ((object)val).GetType().GetField("restorationAmount", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (!(field == null)) { float num = (float)field.GetValue(val); if (num > 0f) { FullBellyManager.HandleHungerRestore(((Object)__instance).name, num); } } } else if (((object)val).GetType().Name == "Action_ModifyStatus") { FieldInfo field2 = ((object)val).GetType().GetField("statusType", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); FieldInfo field3 = ((object)val).GetType().GetField("changeAmount", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field2 == null || field3 == null) { continue; } object value = field2.GetValue(val); if (value != null && value.ToString().Contains("Hunger")) { float num2 = (float)field3.GetValue(val); if (num2 < 0f) { FullBellyManager.HandleHungerRestore(((Object)__instance).name, 0f - num2); } } } else { if (!(((object)val).GetType().Name == "Action_ApplyAffliction")) { continue; } FieldInfo field4 = ((object)val).GetType().GetField("affliction", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (!(field4 == null)) { object value2 = field4.GetValue(val); if (value2 != null && value2.ToString().Contains("Chaos")) { float currentStatus = Character.localCharacter.refs.afflictions.GetCurrentStatus((STATUSTYPE)1); FullBellyManager.HandleHungerRestore("Pandora's Box", currentStatus); } } } } } } [HarmonyPatch(typeof(Character), "GetFedItemRPC")] public class Patch_GetFedItemRPC { public static void Postfix(Character __instance, int itemPhotonID) { bool? obj; if (__instance == null) { obj = null; } else { PhotonView photonView = ((MonoBehaviourPun)__instance).photonView; obj = ((photonView != null) ? new bool?(!photonView.IsMine) : null); } bool? flag = obj; if (flag.GetValueOrDefault(true)) { return; } PhotonView val = PhotonView.Find(itemPhotonID); if ((Object)(object)val == (Object)null) { return; } Item component = ((Component)val).GetComponent<Item>(); if ((Object)(object)component == (Object)null) { return; } ItemAction[] components = ((Component)component).GetComponents<ItemAction>(); ItemAction[] array = components; foreach (ItemAction val2 in array) { if (((object)val2).GetType().Name == "Action_RestoreHunger") { FieldInfo field = ((object)val2).GetType().GetField("restorationAmount", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (!(field == null)) { float num = (float)field.GetValue(val2); if (num > 0f) { FullBellyManager.HandleHungerRestore(((Object)component).name + " (Fed)", num); } } } else { if (!(((object)val2).GetType().Name == "Action_ModifyStatus")) { continue; } FieldInfo field2 = ((object)val2).GetType().GetField("statusType", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); FieldInfo field3 = ((object)val2).GetType().GetField("changeAmount", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field2 == null || field3 == null) { continue; } object value = field2.GetValue(val2); if (value != null && value.ToString().Contains("Hunger")) { float num2 = (float)field3.GetValue(val2); if (num2 < 0f) { FullBellyManager.HandleHungerRestore(((Object)component).name + " (Fed)", 0f - num2); } } } } } }