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 WarnedExpiring = new HashSet(); private static bool _warnedNoFood; internal static void TryAutoEat(Player player) { List 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 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 eatable, out bool isNormalPick, out string missingFocusName) { if (profile == FoodProfile.None) { return PickForNoneProfile(target, eatable, out isNormalPick, out missingFocusName); } List 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 eatable, out bool isNormalPick, out string missingFocusName) { missingFocusName = ""; if (target == null) { isNormalPick = true; return PickByFallbackPriority(eatable); } if (!AutoNoshPlugin.PreferBetterSameTypeFood.Value) { ItemData val = ((IEnumerable)eatable).FirstOrDefault((Func)((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 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 otherActive = new HashSet(from f in player.GetFoods() select f.m_item.m_shared.m_name into n where n != name select n); List 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 eatable) { FoodStatKind[] fallbackOrder = FoodProfiles.GetFallbackOrder(AutoNoshPlugin.FallbackPriorityConfig.Value); foreach (FoodStatKind kind in fallbackOrder) { List 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 GetFoodCandidates(Player player) { return ((Humanoid)player).GetInventory().GetAllItemsOfType((ItemType)2, false).Where(IsFood) .ToList(); } private static bool CanEatNow(List 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 VerboseLogging; internal static ConfigEntry Enabled; internal static ConfigEntry CheckIntervalSeconds; internal static ConfigEntry EatThresholdPercent; internal static ConfigEntry FoodProfileConfig; internal static ConfigEntry CycleProfileKey; internal static ConfigEntry PreferBetterSameTypeFood; internal static ConfigEntry FallbackPriorityConfig; internal static ConfigEntry ConsiderFeastDuration; internal static ConfigEntry AfkPauseEnabled; internal static ConfigEntry AfkThresholdMinutes; internal static ConfigEntry NotifyEating; internal static ConfigEntry NotifyExpiring; internal static ConfigEntry NotifyFallback; internal static ConfigEntry LowFoodWarningCount; internal static ConfigEntry 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("General", "VerboseLogging", false, "Log detailed per-event output. Noisy; for debugging only."); Enabled = ((BaseUnityPlugin)this).Config.Bind("General", "Enabled", true, "Master toggle for auto-eating."); CheckIntervalSeconds = ((BaseUnityPlugin)this).Config.Bind("General", "CheckIntervalSeconds", 2f, new ConfigDescription("How often to re-check whether a food slot needs refilling.", (AcceptableValueBase)(object)new AcceptableValueRange(0.5f, 10f), Array.Empty())); EatThresholdPercent = ((BaseUnityPlugin)this).Config.Bind("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(1f, 50f), Array.Empty())); FoodProfileConfig = ((BaseUnityPlugin)this).Config.Bind("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("Profile", "CycleProfileKey", (KeyCode)285, "Key that cycles FoodProfile to the next preset in game."); PreferBetterSameTypeFood = ((BaseUnityPlugin)this).Config.Bind("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("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("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("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("AFK", "AfkThresholdMinutes", 5f, new ConfigDescription("Minutes of no input before auto-eating pauses.", (AcceptableValueBase)(object)new AcceptableValueRange(1f, 60f), Array.Empty())); NotifyEating = ((BaseUnityPlugin)this).Config.Bind("Notifications", "NotifyEating", true, "Show a message when AutoNosh eats a food that matches your active profile."); NotifyExpiring = ((BaseUnityPlugin)this).Config.Bind("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("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("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(0, 50), Array.Empty())); NotifyNextFood = ((BaseUnityPlugin)this).Config.Bind("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 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 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) { } } }