Decompiled source of AutoNosh v0.5.0

plugins/AutoNosh.dll

Decompiled 18 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using Jotunn.Configs;
using Jotunn.Managers;
using Jotunn.Utils;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.6.2", FrameworkDisplayName = ".NET Framework 4.6.2")]
[assembly: IgnoresAccessChecksTo("assembly_guiutils")]
[assembly: IgnoresAccessChecksTo("assembly_utils")]
[assembly: IgnoresAccessChecksTo("assembly_valheim")]
[assembly: AssemblyCompany("RAGEmedia")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Smart auto-eat for food only, with profile-based selection, a cycle key, and an AFK pause.")]
[assembly: AssemblyFileVersion("0.5.0.0")]
[assembly: AssemblyInformationalVersion("0.5.0")]
[assembly: AssemblyProduct("AutoNosh")]
[assembly: AssemblyTitle("AutoNosh")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.5.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class IsReadOnlyAttribute : Attribute
	{
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace AutoNosh
{
	internal static class AfkTracker
	{
		private static float _lastActiveTime = Time.time;

		internal static float IdleSeconds => Time.time - _lastActiveTime;

		internal static void NoteActivity()
		{
			_lastActiveTime = Time.time;
		}
	}
	internal static class FoodAutoEater
	{
		private static readonly HashSet<Food> WarnedExpiring = new HashSet<Food>();

		private static bool _warnedNoFood;

		internal static void TryAutoEat(Player player)
		{
			List<Food> activeFoods = player.GetFoods();
			WarnedExpiring.IntersectWith(activeFoods);
			if (AutoNoshPlugin.NotifyExpiring.Value)
			{
				foreach (Food item in activeFoods)
				{
					if (IsDueForReplacement(item) && WarnedExpiring.Add(item))
					{
						((Character)player).Message((MessageType)2, "AutoNosh: " + item.m_item.m_shared.m_name + " is running low", 0, (Sprite)null, false);
					}
				}
			}
			bool num = activeFoods.Count < 3;
			bool flag = activeFoods.Any(IsDueForReplacement);
			if (!num && !flag)
			{
				return;
			}
			List<ItemData> list = (from i in GetFoodCandidates(player)
				where CanEatNow(activeFoods, i)
				select i).ToList();
			if (list.Count == 0)
			{
				if (AutoNoshPlugin.NotifyFallback.Value && !_warnedNoFood)
				{
					_warnedNoFood = true;
					((Character)player).Message((MessageType)2, "AutoNosh: no food to eat", 0, (Sprite)null, false);
				}
				return;
			}
			_warnedNoFood = false;
			FoodProfile value = AutoNoshPlugin.FoodProfileConfig.Value;
			bool isNormalPick;
			string missingFocusName;
			ItemData val = PickFood(value, (from f in activeFoods.Where(IsDueForReplacement)
				orderby f.m_time
				select f).FirstOrDefault()?.m_item, list, out isNormalPick, out missingFocusName);
			if (!((Humanoid)player).ConsumeItem(((Humanoid)player).GetInventory(), val, false))
			{
				return;
			}
			string name = val.m_shared.m_name;
			string text = null;
			if (isNormalPick)
			{
				if (AutoNoshPlugin.NotifyEating.Value)
				{
					text = "AutoNosh: eating " + name;
				}
			}
			else if (AutoNoshPlugin.NotifyFallback.Value)
			{
				string text2 = (string.IsNullOrEmpty(missingFocusName) ? "no matching food" : ("no " + missingFocusName + " food"));
				text = "AutoNosh: " + text2 + ", eating next best: " + name;
			}
			string text3 = BuildLowStockWarning(player, value, val);
			if (text3 != null)
			{
				text = ((text == null) ? ("AutoNosh: " + text3) : (text + "\n" + text3));
			}
			if (text != null)
			{
				((Character)player).Message((MessageType)2, text, 0, (Sprite)null, false);
			}
			AutoNoshPlugin.LogVerbose($"Auto-ate {name} (profile {value}, normal pick: {isNormalPick})");
		}

		private static ItemData PickFood(FoodProfile profile, ItemData target, List<ItemData> eatable, out bool isNormalPick, out string missingFocusName)
		{
			if (profile == FoodProfile.None)
			{
				return PickForNoneProfile(target, eatable, out isNormalPick, out missingFocusName);
			}
			List<ItemData> list = eatable.Where((ItemData i) => FoodProfiles.Matches(profile, i)).ToList();
			if (list.Count > 0)
			{
				isNormalPick = true;
				missingFocusName = "";
				FoodWeights weights = FoodProfiles.GetWeights(profile);
				return list.OrderByDescending((ItemData i) => ScoreCandidate(i, weights)).First();
			}
			isNormalPick = false;
			missingFocusName = FoodProfiles.FocusName(profile);
			return PickByFallbackPriority(eatable);
		}

		private static ItemData PickForNoneProfile(ItemData target, List<ItemData> eatable, out bool isNormalPick, out string missingFocusName)
		{
			missingFocusName = "";
			if (target == null)
			{
				isNormalPick = true;
				return PickByFallbackPriority(eatable);
			}
			if (!AutoNoshPlugin.PreferBetterSameTypeFood.Value)
			{
				ItemData val = ((IEnumerable<ItemData>)eatable).FirstOrDefault((Func<ItemData, bool>)((ItemData i) => i.m_shared.m_name == target.m_shared.m_name));
				if (val != null)
				{
					isNormalPick = true;
					return val;
				}
			}
			FoodStatKind dominant = FoodProfiles.Classify(target);
			List<ItemData> list = eatable.Where((ItemData i) => FoodProfiles.Classify(i) == dominant).ToList();
			if (list.Count > 0)
			{
				isNormalPick = true;
				return list.OrderByDescending((ItemData i) => ScoreCandidate(i, FoodProfiles.WeightsForStat(dominant))).First();
			}
			isNormalPick = false;
			missingFocusName = FoodProfiles.StatKindName(dominant);
			return PickByFallbackPriority(eatable);
		}

		private static string BuildLowStockWarning(Player player, FoodProfile profile, ItemData eaten)
		{
			int value = AutoNoshPlugin.LowFoodWarningCount.Value;
			if (value <= 0)
			{
				return null;
			}
			string name = eaten.m_shared.m_name;
			int num = ((Humanoid)player).GetInventory().CountItems(name, -1, false);
			if (num > value)
			{
				return null;
			}
			string text = ((num == 0) ? ("that was your last " + name) : $"{name}: {num} left");
			if (!AutoNoshPlugin.NotifyNextFood.Value)
			{
				return text;
			}
			HashSet<string> otherActive = new HashSet<string>(from f in player.GetFoods()
				select f.m_item.m_shared.m_name into n
				where n != name
				select n);
			List<ItemData> list = (from i in GetFoodCandidates(player)
				where i.m_shared.m_name != name && !otherActive.Contains(i.m_shared.m_name)
				select i).ToList();
			if (list.Count == 0)
			{
				return text + ", no other food to fall back on";
			}
			bool isNormalPick;
			string missingFocusName;
			ItemData val = PickFood(profile, eaten, list, out isNormalPick, out missingFocusName);
			return text + ", then " + val.m_shared.m_name;
		}

		private static ItemData PickByFallbackPriority(List<ItemData> eatable)
		{
			FoodStatKind[] fallbackOrder = FoodProfiles.GetFallbackOrder(AutoNoshPlugin.FallbackPriorityConfig.Value);
			foreach (FoodStatKind kind in fallbackOrder)
			{
				List<ItemData> list = eatable.Where((ItemData item) => FoodProfiles.QualifiesFor(item, kind)).ToList();
				if (list.Count > 0)
				{
					return list.OrderByDescending((ItemData item) => ScoreCandidate(item, FoodProfiles.WeightsForStat(kind))).First();
				}
			}
			return eatable.OrderByDescending((ItemData item) => ScoreCandidate(item, FoodProfiles.BalancedWeights)).First();
		}

		private static float ScoreCandidate(ItemData item, FoodWeights weights)
		{
			return FoodProfiles.Score(item, weights, AutoNoshPlugin.ConsiderFeastDuration.Value);
		}

		private static List<ItemData> GetFoodCandidates(Player player)
		{
			return ((Humanoid)player).GetInventory().GetAllItemsOfType((ItemType)2, false).Where(IsFood)
				.ToList();
		}

		private static bool CanEatNow(List<Food> activeFoods, ItemData item)
		{
			foreach (Food activeFood in activeFoods)
			{
				if (activeFood.m_item.m_shared.m_name == item.m_shared.m_name)
				{
					return IsDueForReplacement(activeFood);
				}
			}
			if (activeFoods.Any(IsDueForReplacement))
			{
				return true;
			}
			return activeFoods.Count < 3;
		}

		private static bool IsDueForReplacement(Food food)
		{
			if (!food.CanEatAgain())
			{
				return false;
			}
			float foodBurnTime = food.m_item.m_shared.m_foodBurnTime;
			if (foodBurnTime <= 0f)
			{
				return true;
			}
			return food.m_time / foodBurnTime <= AutoNoshPlugin.EatThresholdPercent.Value / 100f;
		}

		private static bool IsFood(ItemData item)
		{
			SharedData shared = item.m_shared;
			if (!(shared.m_food > 0f) && !(shared.m_foodStamina > 0f))
			{
				return shared.m_foodEitr > 0f;
			}
			return true;
		}
	}
	public enum FoodProfile
	{
		Balanced,
		None,
		HeavyStamina,
		MassiveHealth,
		EitrMagic,
		MagicHealth
	}
	internal enum FoodStatKind
	{
		Health,
		Stamina,
		Eitr,
		Balanced
	}
	public enum FallbackPriority
	{
		HealthStaminaEitr,
		HealthEitrStamina,
		StaminaHealthEitr,
		StaminaEitrHealth,
		EitrHealthStamina,
		EitrStaminaHealth
	}
	internal readonly struct FoodWeights
	{
		internal readonly float Health;

		internal readonly float Stamina;

		internal readonly float Eitr;

		internal FoodWeights(float health, float stamina, float eitr)
		{
			Health = health;
			Stamina = stamina;
			Eitr = eitr;
		}
	}
	internal static class FoodProfiles
	{
		internal static readonly FoodWeights BalancedWeights = new FoodWeights(1f, 1f, 1f);

		internal static FoodWeights GetWeights(FoodProfile profile)
		{
			return profile switch
			{
				FoodProfile.HeavyStamina => new FoodWeights(0.3f, 2f, 0.3f), 
				FoodProfile.MassiveHealth => new FoodWeights(2f, 0.3f, 0.3f), 
				FoodProfile.EitrMagic => new FoodWeights(0.2f, 0.2f, 2.5f), 
				FoodProfile.MagicHealth => new FoodWeights(1.5f, 0.2f, 1.5f), 
				_ => BalancedWeights, 
			};
		}

		internal static bool Matches(FoodProfile profile, ItemData item)
		{
			SharedData shared = item.m_shared;
			switch (profile)
			{
			case FoodProfile.HeavyStamina:
				return shared.m_foodStamina > 0f;
			case FoodProfile.MassiveHealth:
				return shared.m_food > 0f;
			case FoodProfile.EitrMagic:
			case FoodProfile.MagicHealth:
				return shared.m_foodEitr > 0f;
			default:
				return true;
			}
		}

		internal static float Score(ItemData item, FoodWeights weights, bool considerDuration)
		{
			SharedData shared = item.m_shared;
			float num = shared.m_food * weights.Health + shared.m_foodStamina * weights.Stamina + shared.m_foodEitr * weights.Eitr;
			if (!considerDuration)
			{
				return num;
			}
			return num * shared.m_foodBurnTime;
		}

		internal static string FocusName(FoodProfile profile)
		{
			switch (profile)
			{
			case FoodProfile.HeavyStamina:
				return "Stamina";
			case FoodProfile.MassiveHealth:
				return "Health";
			case FoodProfile.EitrMagic:
			case FoodProfile.MagicHealth:
				return "Eitr";
			default:
				return "";
			}
		}

		internal static FoodProfile Next(FoodProfile current)
		{
			FoodProfile[] array = (FoodProfile[])Enum.GetValues(typeof(FoodProfile));
			int num = Array.IndexOf(array, current);
			return array[(num + 1) % array.Length];
		}

		internal static FoodStatKind Classify(ItemData item)
		{
			SharedData shared = item.m_shared;
			if (shared.m_food < shared.m_foodEitr / 2f && shared.m_foodStamina < shared.m_foodEitr / 2f)
			{
				return FoodStatKind.Eitr;
			}
			if (shared.m_foodStamina < shared.m_food / 2f)
			{
				return FoodStatKind.Health;
			}
			if (shared.m_food < shared.m_foodStamina / 2f)
			{
				return FoodStatKind.Stamina;
			}
			return FoodStatKind.Balanced;
		}

		internal static bool QualifiesFor(ItemData item, FoodStatKind kind)
		{
			FoodStatKind foodStatKind = Classify(item);
			if (foodStatKind == kind)
			{
				return true;
			}
			if (foodStatKind != FoodStatKind.Balanced)
			{
				return false;
			}
			SharedData shared = item.m_shared;
			return kind switch
			{
				FoodStatKind.Health => shared.m_food > 0f, 
				FoodStatKind.Stamina => shared.m_foodStamina > 0f, 
				FoodStatKind.Eitr => shared.m_foodEitr > 0f, 
				_ => false, 
			};
		}

		internal static FoodWeights WeightsForStat(FoodStatKind kind)
		{
			return kind switch
			{
				FoodStatKind.Stamina => GetWeights(FoodProfile.HeavyStamina), 
				FoodStatKind.Eitr => GetWeights(FoodProfile.EitrMagic), 
				FoodStatKind.Health => GetWeights(FoodProfile.MassiveHealth), 
				_ => BalancedWeights, 
			};
		}

		internal static string StatKindName(FoodStatKind kind)
		{
			return kind switch
			{
				FoodStatKind.Stamina => "Stamina", 
				FoodStatKind.Eitr => "Eitr", 
				FoodStatKind.Balanced => "Balanced", 
				_ => "Health", 
			};
		}

		internal static FoodStatKind[] GetFallbackOrder(FallbackPriority priority)
		{
			return priority switch
			{
				FallbackPriority.HealthEitrStamina => new FoodStatKind[3]
				{
					FoodStatKind.Health,
					FoodStatKind.Eitr,
					FoodStatKind.Stamina
				}, 
				FallbackPriority.StaminaHealthEitr => new FoodStatKind[3]
				{
					FoodStatKind.Stamina,
					FoodStatKind.Health,
					FoodStatKind.Eitr
				}, 
				FallbackPriority.StaminaEitrHealth => new FoodStatKind[3]
				{
					FoodStatKind.Stamina,
					FoodStatKind.Eitr,
					FoodStatKind.Health
				}, 
				FallbackPriority.EitrHealthStamina => new FoodStatKind[3]
				{
					FoodStatKind.Eitr,
					FoodStatKind.Health,
					FoodStatKind.Stamina
				}, 
				FallbackPriority.EitrStaminaHealth => new FoodStatKind[3]
				{
					FoodStatKind.Eitr,
					FoodStatKind.Stamina,
					FoodStatKind.Health
				}, 
				_ => new FoodStatKind[3]
				{
					FoodStatKind.Health,
					FoodStatKind.Stamina,
					FoodStatKind.Eitr
				}, 
			};
		}
	}
	[BepInPlugin("com.ragemedia.autonosh", "AutoNosh", "0.5.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[NetworkCompatibility(/*Could not decode attribute arguments.*/)]
	public class AutoNoshPlugin : BaseUnityPlugin
	{
		public const string PluginGuid = "com.ragemedia.autonosh";

		public const string PluginName = "AutoNosh";

		public const string PluginVersion = "0.5.0";

		internal const string CycleProfileButtonName = "AutoNosh_CycleProfile";

		internal static AutoNoshPlugin Instance;

		internal static ConfigEntry<bool> VerboseLogging;

		internal static ConfigEntry<bool> Enabled;

		internal static ConfigEntry<float> CheckIntervalSeconds;

		internal static ConfigEntry<float> EatThresholdPercent;

		internal static ConfigEntry<FoodProfile> FoodProfileConfig;

		internal static ConfigEntry<KeyCode> CycleProfileKey;

		internal static ConfigEntry<bool> PreferBetterSameTypeFood;

		internal static ConfigEntry<FallbackPriority> FallbackPriorityConfig;

		internal static ConfigEntry<bool> ConsiderFeastDuration;

		internal static ConfigEntry<bool> AfkPauseEnabled;

		internal static ConfigEntry<float> AfkThresholdMinutes;

		internal static ConfigEntry<bool> NotifyEating;

		internal static ConfigEntry<bool> NotifyExpiring;

		internal static ConfigEntry<bool> NotifyFallback;

		internal static ConfigEntry<int> LowFoodWarningCount;

		internal static ConfigEntry<bool> NotifyNextFood;

		private readonly Harmony _harmony = new Harmony("com.ragemedia.autonosh");

		private void Awake()
		{
			Instance = this;
			BindConfig();
			RegisterKeybind();
			_harmony.PatchAll(Assembly.GetExecutingAssembly());
			VerifyPatches();
		}

		private void OnDestroy()
		{
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}

		private void BindConfig()
		{
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Expected O, but got Unknown
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Expected O, but got Unknown
			//IL_01ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b6: Expected O, but got Unknown
			//IL_023e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0248: Expected O, but got Unknown
			VerboseLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "VerboseLogging", false, "Log detailed per-event output. Noisy; for debugging only.");
			Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Master toggle for auto-eating.");
			CheckIntervalSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("General", "CheckIntervalSeconds", 2f, new ConfigDescription("How often to re-check whether a food slot needs refilling.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 10f), Array.Empty<object>()));
			EatThresholdPercent = ((BaseUnityPlugin)this).Config.Bind<float>("General", "EatThresholdPercent", 50f, new ConfigDescription("Auto-eat when a food's remaining time drops to this percent of its total duration. 50 (default) acts the moment the game allows a top-up at all; lower it to wait longer and let food run closer to empty first. The game itself never allows a top-up above 50%, so values here only ever narrow that window, never widen it.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 50f), Array.Empty<object>()));
			FoodProfileConfig = ((BaseUnityPlugin)this).Config.Bind<FoodProfile>("Profile", "FoodProfile", FoodProfile.Balanced, "Which stat this favors when picking a replacement food. Balanced picks the highest total food value; None keeps whatever you're already eating (see PreferBetterSameTypeFood); the rest favor stamina, health, or eitr food. All fall back to FallbackPriority when nothing in your inventory fits.");
			CycleProfileKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Profile", "CycleProfileKey", (KeyCode)285, "Key that cycles FoodProfile to the next preset in game.");
			PreferBetterSameTypeFood = ((BaseUnityPlugin)this).Config.Bind<bool>("Profile", "PreferBetterSameTypeFood", false, "Only affects the None profile. False (default): re-eat the exact same food you already had whenever it's available. True: eat the best food of the same kind (fork color: red health, yellow stamina, blue eitr, white balanced) instead, even if it's a different item you have.");
			FallbackPriorityConfig = ((BaseUnityPlugin)this).Config.Bind<FallbackPriority>("Profile", "FallbackPriority", FallbackPriority.HealthStaminaEitr, "Stat category order to try when a profile (or None with nothing of the same kind left) can't find a matching food - picks the best food from the first category that has anything available.");
			ConsiderFeastDuration = ((BaseUnityPlugin)this).Config.Bind<bool>("Profile", "ConsiderFeastDuration", false, "False (default): pick by peak stat value only, so a food's numbers are compared the same way whether it lasts 20 minutes or 50. True: weight by peak value times duration, which can make a longer-lasting feast with lower peak stats win over a shorter, higher-peak food that needs re-eating sooner.");
			AfkPauseEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("AFK", "AfkPauseEnabled", true, "Stop auto-eating while the player has been idle for AfkThresholdMinutes. Food already active keeps decaying normally either way.");
			AfkThresholdMinutes = ((BaseUnityPlugin)this).Config.Bind<float>("AFK", "AfkThresholdMinutes", 5f, new ConfigDescription("Minutes of no input before auto-eating pauses.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 60f), Array.Empty<object>()));
			NotifyEating = ((BaseUnityPlugin)this).Config.Bind<bool>("Notifications", "NotifyEating", true, "Show a message when AutoNosh eats a food that matches your active profile.");
			NotifyExpiring = ((BaseUnityPlugin)this).Config.Bind<bool>("Notifications", "NotifyExpiring", true, "Show a message once when an active food drops to EatThresholdPercent remaining, whether or not a replacement is found.");
			NotifyFallback = ((BaseUnityPlugin)this).Config.Bind<bool>("Notifications", "NotifyFallback", true, "Show a message when no inventory food matches your active profile's focus stat and AutoNosh eats the next-best food instead, or when it can't find any food to eat at all.");
			LowFoodWarningCount = ((BaseUnityPlugin)this).Config.Bind<int>("Notifications", "LowFoodWarningCount", 3, new ConfigDescription("After AutoNosh eats a food, warn when you have this many or fewer of it left in your inventory. 0 turns the warning off.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 50), Array.Empty<object>()));
			NotifyNextFood = ((BaseUnityPlugin)this).Config.Bind<bool>("Notifications", "NotifyNextFood", true, "Add to the low-food warning which food AutoNosh will eat in that slot once you run out, or that there's nothing left to fall back on.");
		}

		private void RegisterKeybind()
		{
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Expected O, but got Unknown
			InputManager.Instance.AddButton("com.ragemedia.autonosh", new ButtonConfig
			{
				Name = "AutoNosh_CycleProfile",
				Config = CycleProfileKey,
				HintToken = "$autonosh_cycle_profile"
			});
		}

		private void VerifyPatches()
		{
			List<MethodBase> list = _harmony.GetPatchedMethods().ToList();
			LogInfo($"Harmony bound {list.Count} target(s):");
			foreach (MethodBase item in list)
			{
				LogInfo("  bound  " + item.DeclaringType?.FullName + "." + item.Name);
			}
			int num = 0;
			foreach (Type item2 in from t in Assembly.GetExecutingAssembly().GetTypes()
				where t.GetCustomAttributes(typeof(HarmonyPatch), inherit: true).Any()
				select t)
			{
				List<HarmonyMethod> list2 = (from HarmonyPatch a in item2.GetCustomAttributes(typeof(HarmonyPatch), inherit: true)
					select ((HarmonyAttribute)a).info).ToList();
				HarmonyMethod val;
				try
				{
					val = HarmonyMethod.Merge(list2);
				}
				catch (Exception ex)
				{
					LogError("  UNRESOLVED  " + item2.Name + ": could not merge patch attributes: " + ex.Message);
					num++;
					continue;
				}
				if (!(val?.declaringType == null) && !string.IsNullOrEmpty(val.methodName) && AccessTools.Method(val.declaringType, val.methodName, val.argumentTypes, (Type[])null) == null)
				{
					num++;
					LogError("  UNRESOLVED  " + item2.Name + " -> " + val.declaringType.Name + "." + val.methodName + " does not exist. This patch will never run. The game version likely changed; re-check the method against the decompiled source.");
				}
			}
			if (num > 0)
			{
				LogError(string.Format("{0}: {1} patch target(s) failed to resolve. ", "AutoNosh", num) + "This mod is loaded but not fully functional.");
			}
			else if (list.Count == 0)
			{
				LogWarning("AutoNosh: loaded, but bound zero patches. Expected if this mod only adds content via Jotunn.");
			}
			else
			{
				LogInfo("AutoNosh v0.5.0 ready.");
			}
		}

		internal static void LogInfo(string msg)
		{
			((BaseUnityPlugin)Instance).Logger.LogInfo((object)("[AutoNosh] " + msg));
		}

		internal static void LogWarning(string msg)
		{
			((BaseUnityPlugin)Instance).Logger.LogWarning((object)("[AutoNosh] " + msg));
		}

		internal static void LogError(string msg)
		{
			((BaseUnityPlugin)Instance).Logger.LogError((object)("[AutoNosh] " + msg));
		}

		internal static void LogVerbose(string msg)
		{
			if (VerboseLogging != null && VerboseLogging.Value)
			{
				((BaseUnityPlugin)Instance).Logger.LogInfo((object)("[AutoNosh] " + msg));
			}
		}
	}
}
namespace AutoNosh.Patches
{
	[HarmonyPatch(typeof(Player), "SetControls")]
	internal static class PlayerSetControlsPatch
	{
		private static void Postfix(Player __instance, Vector3 movedir, bool attack, bool attackHold, bool secondaryAttack, bool secondaryAttackHold, bool block, bool blockHold, bool jump, bool crouch, bool dodge)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)__instance != (Object)(object)Player.m_localPlayer) && (movedir != Vector3.zero || attack || attackHold || secondaryAttack || secondaryAttackHold || block || blockHold || jump || crouch || dodge))
			{
				AfkTracker.NoteActivity();
			}
		}
	}
	[HarmonyPatch(typeof(Player), "Update")]
	internal static class PlayerUpdatePatch
	{
		private static float _nextEatCheck;

		private static void Postfix(Player __instance)
		{
			if (!Player.m_localPlayerExists || (Object)(object)__instance != (Object)(object)Player.m_localPlayer)
			{
				return;
			}
			if (ZInput.GetButtonDown("AutoNosh_CycleProfile"))
			{
				CycleProfile(__instance);
			}
			if (AutoNoshPlugin.Enabled.Value && !(Time.time < _nextEatCheck))
			{
				_nextEatCheck = Time.time + AutoNoshPlugin.CheckIntervalSeconds.Value;
				if (AutoNoshPlugin.AfkPauseEnabled.Value && AfkTracker.IdleSeconds >= AutoNoshPlugin.AfkThresholdMinutes.Value * 60f)
				{
					AutoNoshPlugin.LogVerbose($"Skipping auto-eat, idle {AfkTracker.IdleSeconds:0}s.");
				}
				else if (((Character)__instance).GetSEMan().GetStatusEffects().Any((StatusEffect se) => se is SE_Puke))
				{
					AutoNoshPlugin.LogVerbose("Skipping auto-eat, player is feeling sick.");
				}
				else
				{
					FoodAutoEater.TryAutoEat(__instance);
				}
			}
		}

		private static void CycleProfile(Player player)
		{
			FoodProfile foodProfile = FoodProfiles.Next(AutoNoshPlugin.FoodProfileConfig.Value);
			AutoNoshPlugin.FoodProfileConfig.Value = foodProfile;
			AutoNoshPlugin.LogVerbose($"Food profile cycled to {foodProfile}.");
			((Character)player).Message((MessageType)2, $"AutoNosh: {foodProfile}", 0, (Sprite)null, false);
		}
	}
}
namespace System.Runtime.CompilerServices
{
	[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
	internal sealed class IgnoresAccessChecksToAttribute : Attribute
	{
		public IgnoresAccessChecksToAttribute(string assemblyName)
		{
		}
	}
}