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);
}
}