Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of Supermans Advanced Cooking v1.2.0
SupermansAdvancedCooking.dll
Decompiled a day agousing System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text.RegularExpressions; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; 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("SupermansAdvancedCooking")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.2.0.0")] [assembly: AssemblyInformationalVersion("1.2.0")] [assembly: AssemblyProduct("SupermansAdvancedCooking")] [assembly: AssemblyTitle("SupermansAdvancedCooking")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.2.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [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 SupermansAdvancedCooking { [BepInPlugin("com.supermans.advancedcooking", "Supermans Advanced Cooking", "1.2.0")] public class AdvancedCookingPlugin : BaseUnityPlugin { public const string ModGuid = "com.supermans.advancedcooking"; public const string ModName = "Supermans Advanced Cooking"; public const string ModVersion = "1.2.0"; internal static ManualLogSource Log; private Harmony _harmony; private FileSystemWatcher _watcher; private volatile bool _reloadRequested; private void Awake() { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; ModConfig.Bind(((BaseUnityPlugin)this).Config); _harmony = new Harmony("com.supermans.advancedcooking"); _harmony.PatchAll(typeof(AdvancedCookingPlugin).Assembly); WatchConfigFile(); Log.LogInfo((object)"Supermans Advanced Cooking 1.2.0 loaded."); } private void Update() { if (_reloadRequested) { _reloadRequested = false; ((BaseUnityPlugin)this).Config.Reload(); Log.LogInfo((object)"Config reloaded from disk."); } } private void WatchConfigFile() { _watcher = new FileSystemWatcher(Paths.ConfigPath, Path.GetFileName(((BaseUnityPlugin)this).Config.ConfigFilePath)) { NotifyFilter = NotifyFilters.LastWrite, IncludeSubdirectories = false }; _watcher.Changed += delegate { _reloadRequested = true; }; _watcher.EnableRaisingEvents = true; } private void OnDestroy() { _watcher?.Dispose(); Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } } } internal static class ConfigSync { [HarmonyPatch(typeof(ZNet), "OnNewConnection")] private static class RegisterOnConnect { private static void Postfix(ZNet __instance, ZNetPeer peer) { if (!__instance.IsServer()) { ModConfig.ClearServerOverrides(); peer.m_rpc.Register<ZPackage>("SupermansAdvancedCooking_Config", (Action<ZRpc, ZPackage>)RPC_ReceiveConfig); } } } [HarmonyPatch(typeof(ZNet), "RPC_PeerInfo")] private static class SendOnPeerInfo { private static void Postfix(ZNet __instance, ZRpc rpc) { if (__instance.IsServer()) { rpc.Invoke("SupermansAdvancedCooking_Config", new object[1] { ModConfig.Serialize() }); } } } [HarmonyPatch(typeof(ZNet), "OnDestroy")] private static class ClearOnDisconnect { private static void Postfix() { ModConfig.ClearServerOverrides(); PerkMilestones.Reset(); } } private const string RpcName = "SupermansAdvancedCooking_Config"; public static void BroadcastToClients() { if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer()) { return; } foreach (ZNetPeer peer in ZNet.instance.GetPeers()) { if (peer?.m_rpc != null) { peer.m_rpc.Invoke("SupermansAdvancedCooking_Config", new object[1] { ModConfig.Serialize() }); } } } private static void RPC_ReceiveConfig(ZRpc rpc, ZPackage pkg) { if (!((Object)(object)ZNet.instance == (Object)null) && !ZNet.instance.IsServer()) { ModConfig.ApplyFromServer(pkg); } } } internal static class CookingMath { private const float MaxComfortReduction = 0.5f; public static int MaxTier => Mathf.Max(1, Mathf.FloorToInt(100f / ModConfig.LevelsPerTier.Value)); public static float Level(Player player) { if (!((Object)(object)player == (Object)null)) { return ((Character)player).GetSkillLevel((SkillType)105); } return 0f; } public static int Tier(Player player) { return Mathf.Clamp(Mathf.FloorToInt(Level(player) / ModConfig.LevelsPerTier.Value), 0, MaxTier); } public static int NextTierLevel(int tier) { return Mathf.CeilToInt((float)(tier + 1) * ModConfig.LevelsPerTier.Value); } public static int TierOf(ItemData item) { if (item != null) { return Mathf.Clamp(item.m_quality - 1, 0, MaxTier); } return 0; } public static int QualityForTier(int tier) { return tier + 1; } public static float CalorieMultiplier(int tier) { return 1f + (float)tier * ModConfig.CalorieBonusPerTier.Value; } public static float CalorieMultiplier(ItemData item) { return CalorieMultiplier(TierOf(item)) + (IsEitrFood(item) ? ModConfig.EitrFoodCalorieBonus.Value : 0f); } public static float Calories(ItemData item) { return item.m_shared.m_foodBurnTime * CalorieMultiplier(item); } public static float CalorieUsageMultiplier(Player player) { SEMan sEMan = ((Character)player).GetSEMan(); bool flag = sEMan.HaveStatusEffect(SEMan.s_statusEffectRested); bool flag2 = sEMan.HaveStatusEffect(SEMan.s_statusEffectResting); bool flag3 = sEMan.HaveStatusEffect(SEMan.s_statusEffectShelter); float num = 0f; if (flag) { num += ModConfig.RestedReduction.Value; } if (flag2) { num += ModConfig.RestingReduction.Value; } if (flag3) { num += ModConfig.ShelteredReduction.Value; } if (flag && flag2 && flag3) { num += ModConfig.AllThreeBonusReduction.Value; } return 1f - Mathf.Min(num, 0.5f); } public static string FormatCalories(float calories) { calories = Mathf.Max(0f, calories); if (!(calories >= 10000f)) { if (!(calories >= 1000f)) { return Mathf.CeilToInt(calories).ToString(); } return $"{calories / 1000f:0.0}k"; } return $"{calories / 1000f:0}k"; } public static bool IsEitrFood(ItemData item) { if (item != null) { return item.m_shared.m_foodEitr > 0f; } return false; } public static float QualityMultiplier(int tier) { return 1f + (float)tier * ModConfig.QualityBonusPerTier.Value; } public static bool IsFood(ItemData item) { if (item == null) { return false; } SharedData shared = item.m_shared; if (!(shared.m_food > 0f) && !(shared.m_foodStamina > 0f)) { return shared.m_foodEitr > 0f; } return true; } public static string Describe(int tier) { return $"tier {tier}/{MaxTier} (+{CalorieMultiplier(tier) - 1f:P0} calories, +{QualityMultiplier(tier) - 1f:P0} health/stamina/eitr)"; } } internal static class FoodBurn { private sealed class State { public float CalorieDebt; public float BaseHealth; public float BaseStamina; public float BaseEitr; public float WrittenHealth = float.NaN; public float WrittenStamina = float.NaN; public float WrittenEitr = float.NaN; } private const float MaxDebtCalories = 60f; private static readonly ConditionalWeakTable<Food, State> States = new ConditionalWeakTable<Food, State>(); private static readonly Func<Food, float> HealthShare = (Food f) => f.m_health; private static readonly Func<Food, float> StaminaShare = (Food f) => f.m_stamina; private static readonly Func<Food, float> EitrShare = (Food f) => f.m_eitr; public static void OnHealthRegen(Player player, float amount) { AddDebt(player.m_foods, amount * ModConfig.CaloriesPerHealthRegen.Value * CookingMath.CalorieUsageMultiplier(player), HealthShare); } public static void OnStaminaRegen(Player player, float amount) { AddDebt(player.m_foods, amount * ModConfig.CaloriesPerStaminaRegen.Value * CookingMath.CalorieUsageMultiplier(player), StaminaShare); } public static void OnEitrRegen(Player player, float amount) { AddDebt(player.m_foods, amount * ModConfig.CaloriesPerEitrRegen.Value * CookingMath.CalorieUsageMultiplier(player), EitrShare); } public static float CaloriesLeft(Food food) { return Mathf.Max(0f, food.m_time - States.GetOrCreateValue(food).CalorieDebt); } public static void ResetFood(Food food) { States.GetOrCreateValue(food).CalorieDebt = 0f; } public static void Tick(Player player, float dt) { float num = ModConfig.BaseMetabolism.Value * CookingMath.CalorieUsageMultiplier(player); float num2 = dt * (num - 1f); float num3 = Mathf.Max(0f, dt * (ModConfig.MaxCaloriesPerSecond.Value - num)); foreach (Food food in player.m_foods) { State orCreateValue = States.GetOrCreateValue(food); float num4 = Mathf.Min(orCreateValue.CalorieDebt, num3); orCreateValue.CalorieDebt = Mathf.Min(orCreateValue.CalorieDebt - num4, 60f); food.m_time = Mathf.Max(0f, food.m_time - num2 - num4); ApplyQuality(food, orCreateValue); } } private static void ApplyQuality(Food food, State state) { if (food.m_health != state.WrittenHealth || food.m_stamina != state.WrittenStamina || food.m_eitr != state.WrittenEitr) { state.BaseHealth = food.m_health; state.BaseStamina = food.m_stamina; state.BaseEitr = food.m_eitr; } float num = CookingMath.QualityMultiplier(CookingMath.TierOf(food.m_item)); food.m_health = (state.WrittenHealth = state.BaseHealth * num); food.m_stamina = (state.WrittenStamina = state.BaseStamina * num); food.m_eitr = (state.WrittenEitr = state.BaseEitr * num); } private static void AddDebt(List<Food> foods, float calories, Func<Food, float> share) { if (calories <= 0f || foods == null || foods.Count == 0) { return; } float num = 0f; foreach (Food food in foods) { num += Mathf.Max(0f, share(food)); } if (num <= 0f) { return; } foreach (Food food2 in foods) { float num2 = Mathf.Max(0f, share(food2)) / num; if (num2 > 0f) { States.GetOrCreateValue(food2).CalorieDebt += calories * num2; } } } } internal abstract class SyncedValue { internal abstract void Write(ZPackage pkg); internal abstract void Read(ZPackage pkg); internal abstract void ClearOverride(); } internal sealed class SyncedFloat : SyncedValue { private readonly ConfigEntry<float> _entry; private float? _override; public float Value => _override ?? _entry.Value; public SyncedFloat(ConfigEntry<float> entry) { _entry = entry; } internal override void Write(ZPackage pkg) { pkg.Write(_entry.Value); } internal override void Read(ZPackage pkg) { _override = pkg.ReadSingle(); } internal override void ClearOverride() { _override = null; } } internal sealed class SyncedBool : SyncedValue { private readonly ConfigEntry<bool> _entry; private bool? _override; public bool Value => _override ?? _entry.Value; public SyncedBool(ConfigEntry<bool> entry) { _entry = entry; } internal override void Write(ZPackage pkg) { pkg.Write(_entry.Value); } internal override void Read(ZPackage pkg) { _override = pkg.ReadBool(); } internal override void ClearOverride() { _override = null; } } internal static class ModConfig { private static readonly List<SyncedValue> Synced = new List<SyncedValue>(); public static SyncedBool Enabled; public static SyncedFloat LevelsPerTier; public static SyncedFloat CalorieBonusPerTier; public static SyncedFloat QualityBonusPerTier; public static SyncedFloat EitrFoodCalorieBonus; public static SyncedBool AllowRecook; public static SyncedFloat BaseMetabolism; public static SyncedFloat CaloriesPerHealthRegen; public static SyncedFloat CaloriesPerStaminaRegen; public static SyncedFloat CaloriesPerEitrRegen; public static SyncedFloat MaxCaloriesPerSecond; public static SyncedFloat RestedReduction; public static SyncedFloat RestingReduction; public static SyncedFloat ShelteredReduction; public static SyncedFloat AllThreeBonusReduction; public static ConfigEntry<bool> ShowCaloriesOnHud; public static ConfigEntry<bool> ShowPerkMessages; public static bool IsServerControlled { get; private set; } public static void Bind(ConfigFile cfg) { Enabled = Bool(cfg, "1 - General", "Enabled", def: true, "Master switch for all cooking perks and food changes."); LevelsPerTier = Float(cfg, "2 - Perks", "LevelsPerTier", 25f, "Cooking levels per perk tier. Perks only increase when you reach a new tier (25, 50, 75, 100 by default).", 1f, 100f); CalorieBonusPerTier = Float(cfg, "2 - Perks", "CalorieBonusPerTier", 0.05f, "Extra calories per tier the food was cooked at (0.05 = +5% per tier, +20% at level 100). Balanced so a level-100 cook's food lasts about as long as vanilla in typical play.", 0f, 0.25f); QualityBonusPerTier = Float(cfg, "2 - Perks", "QualityBonusPerTier", 0.03f, "Extra health/stamina/eitr per tier the food was cooked at (0.03 = +3% per tier, +12% at level 100).", 0f, 0.15f); EitrFoodCalorieBonus = Float(cfg, "2 - Perks", "EitrFoodCalorieBonus", 0.1f, "Extra calories for any food that gives eitr, at every tier, added on top of the tier bonus (0.10 = +10%). Eitr foods pay the whole eitr regen cost alone, so this keeps a caster's eitr food lasting about as long as their other food.", 0f, 0.5f); AllowRecook = Bool(cfg, "2 - Perks", "AllowRecook", def: true, "Use cooked food on the station that makes it (spit/grill/oven/cauldron) to upgrade the whole stack to your current tier."); BaseMetabolism = Float(cfg, "3 - Calories", "BaseMetabolism", 1f, "Calories each food burns per second just from being alive, before any activity. A food's calories equal its vanilla duration in seconds, so 1.0 means an idle player matches vanilla.", 0.5f, 1.5f); CaloriesPerHealthRegen = Float(cfg, "3 - Calories", "CaloriesPerHealthRegen", 0.5f, "Calories burned per point of health regenerated from food, split across foods that give health.", 0f, 2f); CaloriesPerStaminaRegen = Float(cfg, "3 - Calories", "CaloriesPerStaminaRegen", 0.13f, "Calories burned per point of stamina regenerated, split across foods that give stamina. Stamina regen replaces stamina you spent, so this is the calorie cost of exertion.", 0f, 1f); CaloriesPerEitrRegen = Float(cfg, "3 - Calories", "CaloriesPerEitrRegen", 0.06f, "Calories burned per point of eitr regenerated, split across foods that give eitr.", 0f, 1f); MaxCaloriesPerSecond = Float(cfg, "3 - Calories", "MaxCaloriesPerSecond", 2f, "Hard cap on how fast one food can burn calories, base metabolism included. Activity cost above the cap is deferred and paid once your bars are full again (up to 60 calories per food), so it is never lost or doubled.", 1f, 4f); RestedReduction = Float(cfg, "4 - Comfort", "RestedReduction", 0.05f, "Lower calorie usage while you have the Rested buff (0.05 = 5% less). Applies to resting burn and activity.", 0f, 0.25f); RestingReduction = Float(cfg, "4 - Comfort", "RestingReduction", 0.1f, "Lower calorie usage while you have the Resting buff (sitting by a fire in shelter).", 0f, 0.25f); ShelteredReduction = Float(cfg, "4 - Comfort", "ShelteredReduction", 0.05f, "Lower calorie usage while you have the Sheltered buff.", 0f, 0.25f); AllThreeBonusReduction = Float(cfg, "4 - Comfort", "AllThreeBonusReduction", 0.1f, "Extra reduction when Rested, Resting and Sheltered are all active at once, on top of the three above (defaults: 5% + 10% + 5% + 10% = 30% less). The total is capped at 50%.", 0f, 0.25f); ShowCaloriesOnHud = cfg.Bind<bool>("5 - Client", "ShowCaloriesOnHud", true, "Show calories left under each food icon instead of vanilla's time left. Not synced."); ShowPerkMessages = cfg.Bind<bool>("5 - Client", "ShowPerkMessages", true, "Show messages for recooking and perk tier milestones. Not synced."); cfg.SettingChanged += delegate { ConfigSync.BroadcastToClients(); }; } public static ZPackage Serialize() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown ZPackage val = new ZPackage(); val.Write("1.2.0"); val.Write(Synced.Count); foreach (SyncedValue item in Synced) { item.Write(val); } return val; } public static void ApplyFromServer(ZPackage pkg) { string text = pkg.ReadString(); int num = pkg.ReadInt(); if (num != Synced.Count) { AdvancedCookingPlugin.Log.LogWarning((object)(string.Format("Server runs {0} {1} with {2} settings, ", "Supermans Advanced Cooking", text, num) + string.Format("client {0} has {1}. Ignoring server config; update the mod.", "1.2.0", Synced.Count))); return; } foreach (SyncedValue item in Synced) { item.Read(pkg); } IsServerControlled = true; AdvancedCookingPlugin.Log.LogInfo((object)("Applied config from server (" + text + ").")); } public static void ClearServerOverrides() { foreach (SyncedValue item in Synced) { item.ClearOverride(); } IsServerControlled = false; } private static SyncedFloat Float(ConfigFile cfg, string section, string key, float def, string desc, float min, float max) { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Expected O, but got Unknown SyncedFloat syncedFloat = new SyncedFloat(cfg.Bind<float>(section, key, def, new ConfigDescription(desc + " [Server synced]", (AcceptableValueBase)(object)new AcceptableValueRange<float>(min, max), Array.Empty<object>()))); Synced.Add(syncedFloat); return syncedFloat; } private static SyncedBool Bool(ConfigFile cfg, string section, string key, bool def, string desc) { SyncedBool syncedBool = new SyncedBool(cfg.Bind<bool>(section, key, def, desc + " [Server synced]")); Synced.Add(syncedBool); return syncedBool; } } internal static class PerkMilestones { private static Player _player; private static float _timer; private static int _lastTier = -1; public static void Reset() { _player = null; _timer = 0f; _lastTier = -1; } public static void Tick(Player player, float dt) { if ((Object)(object)player != (Object)(object)_player) { Reset(); _player = player; } _timer += dt; if (_timer < 1f) { return; } _timer = 0f; int num = CookingMath.Tier(player); if (_lastTier < 0 || num <= _lastTier) { _lastTier = num; return; } _lastTier = num; if (ModConfig.ShowPerkMessages.Value) { ((Character)player).Message((MessageType)2, "Cooking perks improved: " + CookingMath.Describe(num) + "\nRecook older food at the station that made it to upgrade it.", 0, (Sprite)null, false); } } } } namespace SupermansAdvancedCooking.Patches { [HarmonyPatch(typeof(InventoryGui), "DoCrafting")] internal static class StampCraftedFoodPatch { private struct CraftState { public Recipe Recipe; public int CountBefore; } private static void Prefix(InventoryGui __instance, Player player, out CraftState __state) { __state = default(CraftState); Recipe craftRecipe = __instance.m_craftRecipe; if (ModConfig.Enabled.Value && !((Object)(object)craftRecipe == (Object)null) && !((Object)(object)craftRecipe.m_item == (Object)null) && __instance.m_craftUpgradeItem == null && CookingMath.IsFood(craftRecipe.m_item.m_itemData) && CookingMath.Tier(player) != 0) { __state.Recipe = craftRecipe; __state.CountBefore = CountTierZero(player, craftRecipe); } } private static void Postfix(Player player, CraftState __state) { Recipe recipe = __state.Recipe; if (!((Object)(object)recipe == (Object)null)) { int num = CountTierZero(player, recipe) - __state.CountBefore; if (num > 0) { ((Humanoid)player).GetInventory().RemoveItem(recipe.m_item.m_itemData.m_shared.m_name, num, 1, true); ItemData val = recipe.m_item.m_itemData.Clone(); val.m_dropPrefab = ((Component)recipe.m_item).gameObject; val.m_stack = num; val.m_quality = CookingMath.QualityForTier(CookingMath.Tier(player)); FoodItems.AddOrDrop(player, val); } } } private static int CountTierZero(Player player, Recipe recipe) { return ((Humanoid)player).GetInventory().CountItems(recipe.m_item.m_itemData.m_shared.m_name, 1, true); } } [HarmonyPatch(typeof(CookingStation), "Interact")] internal static class StampStationFoodPatch { private static void Prefix(CookingStation __instance, Humanoid user, bool hold, out string __state) { __state = null; if (!hold && ModConfig.Enabled.Value && !((Object)(object)user == (Object)null) && !((Object)(object)user != (Object)(object)Player.m_localPlayer) && !((Object)(object)__instance.m_nview == (Object)null) && __instance.m_nview.IsValid()) { __state = FoodItems.FirstDoneItem(__instance); } } private static void Postfix(Humanoid user, bool __result, string __state) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Expected O, but got Unknown if (__result && __state != null) { int num = CookingMath.Tier((Player)user); if (num > 0) { PendingCookedPickups.Expect(__state, num); } } } } [HarmonyPatch(typeof(Humanoid), "Pickup")] internal static class StampOnPickupPatch { private static void Prefix(Humanoid __instance, GameObject go, out int __state) { __state = -1; if (!((Object)(object)go == (Object)null) && !((Object)(object)__instance != (Object)(object)Player.m_localPlayer)) { ItemDrop component = go.GetComponent<ItemDrop>(); if (!((Object)(object)component == (Object)null) && component.m_itemData.m_quality == 1 && PendingCookedPickups.TryPeek(Utils.GetPrefabName(go), out var index, out var tier)) { component.m_itemData.m_quality = CookingMath.QualityForTier(tier); __state = index; } } } private static void Postfix(GameObject go, bool __result, int __state) { if (__state < 0) { return; } if (__result) { PendingCookedPickups.Consume(__state); } else if ((Object)(object)go != (Object)null) { ItemDrop component = go.GetComponent<ItemDrop>(); if ((Object)(object)component != (Object)null) { component.m_itemData.m_quality = 1; } } } } internal static class PendingCookedPickups { private struct Entry { public string Prefab; public int Tier; public float Expires; } private const float WindowSeconds = 10f; private static readonly List<Entry> Entries = new List<Entry>(); public static void Expect(string prefab, int tier) { Entries.Add(new Entry { Prefab = prefab, Tier = tier, Expires = Time.time + 10f }); } public static bool TryPeek(string prefab, out int index, out int tier) { Entries.RemoveAll((Entry e) => e.Expires < Time.time); index = Entries.FindIndex((Entry e) => e.Prefab == prefab); tier = ((index >= 0) ? Entries[index].Tier : 0); return index >= 0; } public static void Consume(int index) { if (index >= 0 && index < Entries.Count) { Entries.RemoveAt(index); } } } [HarmonyPatch(typeof(CookingStation), "UseItem")] internal static class RecookAtCookingStationPatch { private static bool Prefix(CookingStation __instance, Humanoid user, ItemData item, ref bool __result) { //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Expected O, but got Unknown if (!Recook.Allowed(user, item)) { return true; } if (FoodItems.OutputPrefab(__instance, Object.op_Implicit((Object)(object)item.m_dropPrefab) ? ((Object)item.m_dropPrefab).name : null) == null) { return true; } __result = true; if (!__instance.IsFireLit()) { ((Character)user).Message((MessageType)2, "The fire needs to be lit to recook food.", 0, (Sprite)null, false); return false; } Recook.Apply((Player)user, item); return false; } } [HarmonyPatch(typeof(CraftingStation), "UseItem")] internal static class RecookAtCraftingStationPatch { private static bool Prefix(CraftingStation __instance, Humanoid user, ItemData item, ref bool __result) { //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Expected O, but got Unknown //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Expected O, but got Unknown if (!Recook.Allowed(user, item)) { return true; } Recipe recipe = ObjectDB.instance.GetRecipe(item); if ((Object)(object)recipe == (Object)null || (Object)(object)recipe.m_craftingStation == (Object)null || recipe.m_craftingStation.m_name != __instance.m_name) { return true; } __result = true; if (!__instance.CheckUsable((Player)user, true)) { return false; } Recook.Apply((Player)user, item); return false; } } internal static class Recook { public static bool Allowed(Humanoid user, ItemData item) { if (ModConfig.Enabled.Value && ModConfig.AllowRecook.Value && (Object)(object)user != (Object)null && (Object)(object)user == (Object)(object)Player.m_localPlayer) { return CookingMath.IsFood(item); } return false; } public static void Apply(Player player, ItemData item) { int num = CookingMath.Tier(player); int num2 = CookingMath.TierOf(item); string text = Localization.instance.Localize(item.m_shared.m_name); if (num2 >= num) { string text2 = ((num < CookingMath.MaxTier) ? $" Reach Cooking {CookingMath.NextTierLevel(num)} to improve it." : ""); ((Character)player).Message((MessageType)2, text + " is already cooked at your best." + text2, 0, (Sprite)null, false); return; } Inventory inventory = ((Humanoid)player).GetInventory(); if (inventory.ContainsItem(item)) { ItemData val = item.Clone(); val.m_quality = CookingMath.QualityForTier(num); inventory.RemoveItem(item); FoodItems.AddOrDrop(player, val); ((Character)player).Message((MessageType)1, $"Recooked {val.m_stack} {text}: {CookingMath.Describe(num)}", 0, val.GetIcon(), false); } } } [HarmonyPatch] internal static class FoodTooltipPatch { private static readonly Regex DurationLine = new Regex("\\$item_food_duration: <color=orange>[^<]*</color>"); private static MethodBase TargetMethod() { return AccessTools.GetDeclaredMethods(typeof(ItemData)).FirstOrDefault((MethodInfo m) => m.Name == "GetTooltip" && m.IsStatic && m.GetParameters().Length != 0 && m.GetParameters()[0].ParameterType == typeof(ItemData)); } private static bool Prepare() { return TargetMethod() != null; } private static void Postfix(object[] __args, ref string __result) { if (!ModConfig.Enabled.Value) { return; } object obj = __args[0]; ItemData val = (ItemData)((obj is ItemData) ? obj : null); if (val != null && CookingMath.IsFood(val)) { string text = $"Calories: <color=orange>{Mathf.CeilToInt(CookingMath.Calories(val))}</color>"; if (DurationLine.IsMatch(__result)) { __result = DurationLine.Replace(__result, text, 1); } else { __result = __result + "\n" + text; } __result = __result + "\n<color=orange>Cooked at " + CookingMath.Describe(CookingMath.TierOf(val)) + "</color>"; float value = ModConfig.EitrFoodCalorieBonus.Value; if (CookingMath.IsEitrFood(val) && value > 0f) { __result += $"\n<color=orange>Eitr food: +{value:P0} extra calories</color>"; } } } } internal static class FoodItems { public static void AddOrDrop(Player player, ItemData item) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) if (!((Humanoid)player).GetInventory().AddItem(item)) { ItemDrop.DropItem(item, item.m_stack, ((Component)player).transform.position + ((Component)player).transform.forward + Vector3.up, Quaternion.identity); } } public static string FirstDoneItem(CookingStation station) { ZDO zDO = station.m_nview.GetZDO(); for (int i = 0; i < station.m_slots.Length; i++) { string text = zDO.GetString("slot" + i, ""); if (!string.IsNullOrEmpty(text)) { if ((Object)(object)station.m_overCookedItem != (Object)null && text == ((Object)((Component)station.m_overCookedItem).gameObject).name) { return null; } string text2 = OutputPrefab(station, text); if (text2 != null) { return text2; } } } return null; } public static string OutputPrefab(CookingStation station, string prefabName) { if (string.IsNullOrEmpty(prefabName)) { return null; } foreach (ItemConversion item in station.m_conversion) { if ((Object)(object)item.m_to != (Object)null && ((Object)((Component)item.m_to).gameObject).name == prefabName) { return prefabName; } } return null; } } [HarmonyPatch(typeof(Player), "EatFood")] internal static class EatFoodPatch { private static void Postfix(Player __instance, ItemData item, bool __result) { if (!__result || !ModConfig.Enabled.Value || (Object)(object)__instance != (Object)(object)Player.m_localPlayer) { return; } foreach (Food food in __instance.m_foods) { if (!(food.m_item.m_shared.m_name != item.m_shared.m_name)) { ItemData val = item.Clone(); val.m_stack = 1; food.m_item = val; food.m_time = CookingMath.Calories(val); FoodBurn.ResetFood(food); FoodBurn.Tick(__instance, 0f); break; } } } } [HarmonyPatch(typeof(Player), "UpdateFood")] internal static class UpdateFoodPatch { private static void Prefix(Player __instance, out float __state) { __state = ((Character)__instance).GetHealth(); } private static void Postfix(Player __instance, float dt, float __state) { if (ModConfig.Enabled.Value && !((Object)(object)__instance != (Object)(object)Player.m_localPlayer)) { float num = ((Character)__instance).GetHealth() - __state; if (num > 0f) { FoodBurn.OnHealthRegen(__instance, num); } PerkMilestones.Tick(__instance, dt); FoodBurn.Tick(__instance, dt); } } } [HarmonyPatch(typeof(Player), "UpdateStats", new Type[] { typeof(float) })] internal static class UpdateStatsPatch { private struct Snapshot { public float Stamina; public float Eitr; } private static void Prefix(Player __instance, out Snapshot __state) { __state = new Snapshot { Stamina = __instance.m_stamina, Eitr = __instance.m_eitr }; } private static void Postfix(Player __instance, Snapshot __state) { if (ModConfig.Enabled.Value && !((Object)(object)__instance != (Object)(object)Player.m_localPlayer)) { float num = __instance.m_stamina - __state.Stamina; if (num > 0f) { FoodBurn.OnStaminaRegen(__instance, num); } float num2 = __instance.m_eitr - __state.Eitr; if (num2 > 0f) { FoodBurn.OnEitrRegen(__instance, num2); } } } } [HarmonyPatch(typeof(Hud), "UpdateFood")] internal static class HudCaloriesPatch { private static void Postfix(Hud __instance, Player player) { if (ModConfig.Enabled.Value && ModConfig.ShowCaloriesOnHud.Value && !((Object)(object)player == (Object)null)) { List<Food> foods = player.GetFoods(); for (int i = 0; i < __instance.m_foodTime.Length && i < foods.Count; i++) { __instance.m_foodTime[i].text = CookingMath.FormatCalories(FoodBurn.CaloriesLeft(foods[i])); } } } } } namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] internal sealed class IgnoresAccessChecksToAttribute : Attribute { public IgnoresAccessChecksToAttribute(string assemblyName) { } } }