Decompiled source of Hunger Pangs v1.0.2

plugins/HungerPangs.dll

Decompiled 2 days ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("HungerPangs")]
[assembly: AssemblyDescription("Valheim HungerPangs Mod by DrummerCraig")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("HungerPangs")]
[assembly: AssemblyCopyright("Copyright ©  2026")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("ea00b146-e2c4-41cd-8e70-7bbada299b8e")]
[assembly: AssemblyFileVersion("1.0.2.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyVersion("1.0.2.0")]
namespace DrummerCraig.HungerPangs;

[BepInPlugin("drummercraig.hungerpangs", "Hunger Pangs", "1.0.2")]
public class HungerPangs : BaseUnityPlugin
{
	[CompilerGenerated]
	private sealed class <FoodCheckLoop>d__13 : IEnumerator<object>, IDisposable, IEnumerator
	{
		private int <>1__state;

		private object <>2__current;

		public HungerPangs <>4__this;

		private WaitForSeconds <wait>5__2;

		object IEnumerator<object>.Current
		{
			[DebuggerHidden]
			get
			{
				return <>2__current;
			}
		}

		object IEnumerator.Current
		{
			[DebuggerHidden]
			get
			{
				return <>2__current;
			}
		}

		[DebuggerHidden]
		public <FoodCheckLoop>d__13(int <>1__state)
		{
			this.<>1__state = <>1__state;
		}

		[DebuggerHidden]
		void IDisposable.Dispose()
		{
			<wait>5__2 = null;
			<>1__state = -2;
		}

		private bool MoveNext()
		{
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Expected O, but got Unknown
			int num = <>1__state;
			HungerPangs hungerPangs = <>4__this;
			if (num != 0)
			{
				if (num != 1)
				{
					return false;
				}
				<>1__state = -1;
				hungerPangs.CheckFoods();
			}
			else
			{
				<>1__state = -1;
				<wait>5__2 = new WaitForSeconds(1f);
			}
			<>2__current = <wait>5__2;
			<>1__state = 1;
			return true;
		}

		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 ConfigEntry<bool> modStatus;

	private ConfigEntry<bool> autoEat;

	private ConfigEntry<bool> foodExpiryNotify;

	private ConfigEntry<int> autoEatPercent;

	private ConfigEntry<int> foodExpiryPercent;

	private ConfigEntry<bool> autoEatNotify;

	private ConfigEntry<bool> lowSupplyNotify;

	private ConfigEntry<int> lowSupplyCount;

	private readonly HashSet<string> hasShown = new HashSet<string>();

	private readonly HashSet<string> seenThisTick = new HashSet<string>();

	private readonly HashSet<string> lowSupplyShown = new HashSet<string>();

	private Predicate<string> _notSeenThisTick;

	private void Awake()
	{
		//IL_0066: Unknown result type (might be due to invalid IL or missing references)
		//IL_0070: Expected O, but got Unknown
		//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e6: Expected O, but got Unknown
		modStatus = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "1. Mod Status", true, "Master toggle for the entire mod. When disabled, no auto-eating or notifications occur.");
		autoEat = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "2. Auto-Eat", true, "Automatically re-eat food before it expires. When disabled, the mod will only show notifications if NotificationsEnabled is on, allowing you to eat manually on cue.");
		autoEatPercent = ((BaseUnityPlugin)this).Config.Bind<int>("General", "3. Auto-Eat Percent", 5, new ConfigDescription("Percentage of a food's total duration remaining when it is automatically re-eaten. Food becomes eligible to re-eat at 50% remaining (when it starts blinking), so this is your window. Lower values eat later and waste less food; higher values re-eat sooner after becoming eligible. Example: 5 = eat when 5% remains (e.g. ~1.5 min of a 30-min food). Requires Auto-Eat Enabled.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 49), Array.Empty<object>()));
		autoEatNotify = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "4. Auto-Eat Notify", true, "Show a HUD notification when a food item is automatically eaten. Requires AutomaticallyEat.");
		foodExpiryNotify = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "5. Food Expiry Notify", true, "Show a HUD notification when a food item nears its expiry threshold. Useful on its own when Auto-Eat is Disabled,as a manual reminder to eat.");
		foodExpiryPercent = ((BaseUnityPlugin)this).Config.Bind<int>("General", "6. Food Expiry Percent", 30, new ConfigDescription("Percentage of a food's total duration remaining when the HUD notification appears. Food becomes eligible to re-eat at 50% remaining (when it starts blinking). Set this higher than Auto-Eat Percent so the notification fires before auto-eat. Example: 30 = notify when 30% remains (e.g. ~9 min of a 30-min food). Requires Notifications.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 50), Array.Empty<object>()));
		lowSupplyNotify = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "7. Low Supply Notify", true, "Show a HUD notification when AutomaticallyEat is enabled, a food item is consumed and the remaining count in your inventory is at or below the LowSupplyThreshold.");
		lowSupplyCount = ((BaseUnityPlugin)this).Config.Bind<int>("General", "8. Low Supply Count", 1, "The number of food items in your inventory for the LowSupplyNotification to appear.");
		_notSeenThisTick = (string key) => !seenThisTick.Contains(key);
		((MonoBehaviour)this).StartCoroutine(FoodCheckLoop());
	}

	[IteratorStateMachine(typeof(<FoodCheckLoop>d__13))]
	private IEnumerator FoodCheckLoop()
	{
		//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
		return new <FoodCheckLoop>d__13(0)
		{
			<>4__this = this
		};
	}

	private void CheckFoods()
	{
		if (!modStatus.Value)
		{
			return;
		}
		Player localPlayer = Player.m_localPlayer;
		if ((Object)(object)localPlayer == (Object)null)
		{
			return;
		}
		Humanoid val = (Humanoid)(object)localPlayer;
		Inventory inventory = val.GetInventory();
		List<Food> foods = localPlayer.GetFoods();
		seenThisTick.Clear();
		for (int i = 0; i < foods.Count; i++)
		{
			Food val2 = foods[i];
			string name = val2.m_item.m_shared.m_name;
			seenThisTick.Add(name);
			float time = val2.m_time;
			float foodBurnTime = val2.m_item.m_shared.m_foodBurnTime;
			float num = ((foodBurnTime > 0f) ? (time / foodBurnTime * 100f) : 0f);
			if (!val2.CanEatAgain())
			{
				hasShown.Remove(name);
				lowSupplyShown.Remove(name);
				continue;
			}
			if (foodExpiryNotify.Value && !hasShown.Contains(name) && num <= (float)foodExpiryPercent.Value)
			{
				string arg = Localization.instance.Localize(val2.m_item.m_shared.m_name);
				int num2 = Mathf.CeilToInt(time / 60f);
				MessageHud instance = MessageHud.instance;
				if (instance != null)
				{
					instance.ShowMessage((MessageType)2, $"{arg} expires in {num2} min", 0, (Sprite)null, false);
				}
				hasShown.Add(name);
			}
			if (!autoEat.Value || !(num <= (float)autoEatPercent.Value))
			{
				continue;
			}
			ItemData item = inventory.GetItem(name, -1, false);
			if (item == null || !val.ConsumeItem(inventory, item, false))
			{
				continue;
			}
			hasShown.Remove(name);
			if (autoEatNotify.Value)
			{
				string text = Localization.instance.Localize(val2.m_item.m_shared.m_name);
				MessageHud instance2 = MessageHud.instance;
				if (instance2 != null)
				{
					instance2.ShowMessage((MessageType)2, text + " automatically re-eaten", 0, (Sprite)null, false);
				}
			}
			if (!lowSupplyNotify.Value || lowSupplyCount.Value <= 0 || lowSupplyShown.Contains(name))
			{
				continue;
			}
			int num3 = inventory.CountItems(name, -1, true);
			if (num3 <= lowSupplyCount.Value)
			{
				string arg2 = Localization.instance.Localize(val2.m_item.m_shared.m_name);
				MessageHud instance3 = MessageHud.instance;
				if (instance3 != null)
				{
					instance3.ShowMessage((MessageType)2, $"Low supply: {num3} {arg2} remaining", 0, (Sprite)null, false);
				}
				lowSupplyShown.Add(name);
			}
		}
		hasShown.RemoveWhere(_notSeenThisTick);
		lowSupplyShown.RemoveWhere(_notSeenThisTick);
	}
}