Decompiled source of MasterCookingFixes v1.1.2

MasterCookingFixes.dll

Decompiled 11 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
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: AssemblyCompany("MasterCookingFixes")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("MasterCookingFixes")]
[assembly: AssemblyTitle("MasterCookingFixes")]
[assembly: AssemblyVersion("1.0.0.0")]
[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 MasterCookingFixes
{
	[BepInPlugin("pieter.valheim.mastercookingfixes", "MasterCooking Fixes", "1.1.2")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class Plugin : BaseUnityPlugin
	{
		private struct Factors
		{
			public float Health;

			public float Stamina;

			public float Regen;

			public float Eitr;

			public float Duration;

			public bool Any
			{
				get
				{
					if (Health == 1f && Stamina == 1f && Regen == 1f && Eitr == 1f)
					{
						return Duration != 1f;
					}
					return true;
				}
			}
		}

		private sealed class Original
		{
			public float Food;

			public float Stamina;

			public float Eitr;

			public float Regen;
		}

		[HarmonyPatch(typeof(Player), "EatFood")]
		private static class EatFoodPatch
		{
			private static void Prefix(Player __instance, ItemData item, out List<SharedData> __state)
			{
				__state = new List<SharedData>();
				if ((Object)(object)__instance == (Object)(object)Player.m_localPlayer && item != null)
				{
					Boost(item.m_shared, Current(), __state);
				}
			}

			private static void Postfix(Player __instance, ItemData item, bool __result, List<Food> ___m_foods)
			{
				if (!__result || (Object)(object)__instance != (Object)(object)Player.m_localPlayer || item?.m_shared == null)
				{
					return;
				}
				float duration = Current().Duration;
				if (duration == 1f)
				{
					return;
				}
				foreach (Food ___m_food in ___m_foods)
				{
					if (___m_food.m_item?.m_shared?.m_name == item.m_shared.m_name && ___m_food.m_time > 0f)
					{
						___m_food.m_time *= duration;
					}
				}
			}

			private static Exception Finalizer(Exception __exception, List<SharedData> __state)
			{
				Restore(__state);
				return __exception;
			}
		}

		[HarmonyPatch(typeof(Player), "UpdateFood")]
		private static class UpdateFoodPatch
		{
			private static void Prefix(Player __instance, List<Food> ___m_foods, out List<SharedData> __state)
			{
				__state = new List<SharedData>();
				if ((Object)(object)__instance != (Object)(object)Player.m_localPlayer || ___m_foods == null)
				{
					return;
				}
				Factors f = Current();
				if (!f.Any)
				{
					return;
				}
				foreach (Food ___m_food in ___m_foods)
				{
					Boost(___m_food?.m_item?.m_shared, f, __state);
				}
			}

			private static Exception Finalizer(Exception __exception, List<SharedData> __state)
			{
				Restore(__state);
				return __exception;
			}
		}

		[HarmonyPatch(typeof(Player), "GetTotalFoodValue")]
		private static class TotalFoodPatch
		{
			private static void Postfix(Player __instance, List<Food> ___m_foods, ref float hp, ref float stamina, ref float eitr)
			{
				if (!_disableDecay.Value || (Object)(object)__instance != (Object)(object)Player.m_localPlayer || ___m_foods == null || ___m_foods.Count == 0)
				{
					return;
				}
				Factors factors = Current();
				foreach (Food ___m_food in ___m_foods)
				{
					SharedData val = ___m_food?.m_item?.m_shared;
					if (val != null)
					{
						hp += Boosted(OriginalFood(val), factors.Health) - ___m_food.m_health;
						stamina += Boosted(OriginalStamina(val), factors.Stamina) - ___m_food.m_stamina;
						eitr += Boosted(OriginalEitr(val), factors.Eitr) - ___m_food.m_eitr;
					}
				}
			}
		}

		[HarmonyPatch(typeof(InventoryGui), "DoCrafting")]
		private static class DoCraftingPatch
		{
			private static void Prefix(InventoryGui __instance, Player player, out int __state)
			{
				__state = -1;
				_craftingFood = false;
				_cookingRaised = false;
				ItemData val = CraftRecipe.Invoke(__instance)?.m_item?.m_itemData;
				if (!((Object)(object)player == (Object)null) && !((Object)(object)player != (Object)(object)Player.m_localPlayer) && IsFood(val?.m_shared))
				{
					_craftingFood = true;
					__state = ((Humanoid)player).GetInventory().CountItems(val.m_shared.m_name, -1, true);
				}
			}

			private static void Postfix(InventoryGui __instance, Player player, int __state)
			{
				if (_logXp.Value)
				{
					ItemData val = CraftRecipe.Invoke(__instance)?.m_item?.m_itemData;
					_log.LogInfo((object)($"[XP] DoCrafting {val?.m_shared?.m_name}: food={_craftingFood} raised={_cookingRaised} " + $"before={__state} after={((val == null || (Object)(object)player == (Object)null) ? (-1) : ((Humanoid)player).GetInventory().CountItems(val.m_shared.m_name, -1, true))} " + "lcfspExecuting=" + LcfspExecuting()));
				}
				if (_craftingFood && !_cookingRaised && __state >= 0 && !((Object)(object)player == (Object)null))
				{
					ItemData val2 = CraftRecipe.Invoke(__instance)?.m_item?.m_itemData;
					if (val2 != null && ((Humanoid)player).GetInventory().CountItems(val2.m_shared.m_name, -1, true) > __state)
					{
						((Character)player).RaiseSkill((SkillType)105, (float)((!MultiCrafting.Invoke(__instance)) ? 1 : __instance.m_multiCraftAmount));
					}
				}
			}

			private static Exception Finalizer(Exception __exception)
			{
				_craftingFood = false;
				_cookingRaised = false;
				return __exception;
			}
		}

		[HarmonyPatch(typeof(Player), "RaiseSkill")]
		private static class RaiseSkillPatch
		{
			[HarmonyPriority(800)]
			private static void Prefix(Player __instance, ref SkillType skill)
			{
				if (_craftingFood && !((Object)(object)__instance != (Object)(object)Player.m_localPlayer))
				{
					skill = (SkillType)105;
					_cookingRaised = true;
				}
			}
		}

		[HarmonyPatch(typeof(Player), "RaiseSkill")]
		private static class RaiseSkillLogPatch
		{
			[HarmonyPriority(0)]
			private static void Prefix(Player __instance, SkillType skill, float value)
			{
				//IL_000c: Unknown result type (might be due to invalid IL or missing references)
				//IL_000f: Invalid comparison between Unknown and I4
				if (_logXp.Value && (int)skill == 105 && (Object)(object)__instance == (Object)(object)Player.m_localPlayer)
				{
					_log.LogInfo((object)$"[XP] Player.RaiseSkill Cooking value={value:0.##} craftingFood={_craftingFood}");
				}
			}
		}

		[HarmonyPatch(typeof(Skills), "RaiseSkill")]
		private static class SkillsRaiseLogPatch
		{
			private static void Postfix(Skills __instance, SkillType skillType)
			{
				//IL_000c: Unknown result type (might be due to invalid IL or missing references)
				//IL_000f: Invalid comparison between Unknown and I4
				if (_logXp.Value && (int)skillType == 105)
				{
					Skill value = Traverse.Create((object)__instance).Method("GetSkill", new object[1] { (object)(SkillType)105 }).GetValue<Skill>();
					if (value != null)
					{
						_log.LogInfo((object)$"[XP] Cooking now level {value.m_level:0} + {value.m_accumulator:0.##} XP");
					}
				}
			}
		}

		[HarmonyPatch(typeof(ItemData), "GetTooltip", new Type[]
		{
			typeof(ItemData),
			typeof(int),
			typeof(bool),
			typeof(float),
			typeof(int),
			typeof(bool)
		})]
		private static class TooltipPatch
		{
			private static void Postfix(ItemData item, bool crafting, bool appending, ref string __result)
			{
				if (appending || string.IsNullOrEmpty(__result) || (Object)(object)Player.m_localPlayer == (Object)null)
				{
					return;
				}
				SharedData val = item?.m_shared;
				if (IsFood(val))
				{
					Factors factors = Current();
					if (factors.Any)
					{
						__result = Line(__result, "(\\$item_food_health: <color=#ff8080ff>)([^<]*)(</color>)", OriginalFood(val), factors.Health, crafting);
						__result = Line(__result, "(\\$item_food_stamina: <color=#ffff80ff>)([^<]*)(</color>)", OriginalStamina(val), factors.Stamina, crafting);
						__result = Line(__result, "(\\$item_food_eitr: <color=#9090ffff>)([^<]*)(</color>)", OriginalEitr(val), factors.Eitr, crafting);
						__result = RegenLine(__result, Boosted_.TryGetValue(val, out var value) ? value.Regen : val.m_foodRegen, factors.Regen, crafting);
						__result = DurationLine(__result, val.m_foodBurnTime, factors.Duration, crafting);
					}
				}
			}

			private static string Number(float v)
			{
				return v.ToString("0.#", CultureInfo.InvariantCulture);
			}

			private static string Bonus(string text)
			{
				return " <color=#80ff80ff>+" + text + "</color>";
			}

			private static string Line(string tooltip, string pattern, float value, float factor, bool crafting)
			{
				if (value <= 0f || factor == 1f)
				{
					return tooltip;
				}
				float num = Boosted(value, factor);
				string replacement = (crafting ? (Number(value) + "</color>" + Bonus(Number(num - value))) : (Number(num) + "</color>"));
				return new Regex(pattern).Replace(tooltip, (Match m) => m.Groups[1].Value + replacement, 1);
			}

			private static string RegenLine(string tooltip, float value, float factor, bool crafting)
			{
				if (value <= 0f || factor == 1f)
				{
					return tooltip;
				}
				float num = Boosted(value, factor);
				string replacement = (crafting ? (Number(value) + " hp/tick</color>" + Bonus(Number(num - value))) : (Number(num) + " hp/tick</color>"));
				return new Regex("(\\$item_food_regen: <color=orange>)([^<]*)( hp/tick</color>)").Replace(tooltip, (Match m) => m.Groups[1].Value + replacement, 1);
			}

			private static string DurationLine(string tooltip, float burnTime, float factor, bool crafting)
			{
				if (burnTime <= 0f || factor == 1f)
				{
					return tooltip;
				}
				float num = burnTime * factor;
				string replacement = (crafting ? (ItemData.GetDurationString(burnTime) + "</color>" + Bonus(ItemData.GetDurationString(num - burnTime))) : (ItemData.GetDurationString(num) + "</color>"));
				return new Regex("(\\$item_food_duration: <color=orange>)([^<]*)(</color>)").Replace(tooltip, (Match m) => m.Groups[1].Value + replacement, 1);
			}
		}

		public const string Guid = "pieter.valheim.mastercookingfixes";

		public const string Name = "MasterCooking Fixes";

		public const string Version = "1.1.2";

		private const string MasterCookingGuid = "com.ruijven.mastercooking";

		private static ConfigEntry<float> _health;

		private static ConfigEntry<float> _stamina;

		private static ConfigEntry<float> _regen;

		private static ConfigEntry<float> _eitr;

		private static ConfigEntry<float> _duration;

		private static ConfigEntry<bool> _disableDecay;

		private static ConfigEntry<bool> _logXp;

		private static ManualLogSource _log;

		private static readonly Dictionary<SharedData, Original> Boosted_ = new Dictionary<SharedData, Original>();

		private static readonly FieldRef<InventoryGui, Recipe> CraftRecipe = AccessTools.FieldRefAccess<InventoryGui, Recipe>("m_craftRecipe");

		private static readonly FieldRef<InventoryGui, bool> MultiCrafting = AccessTools.FieldRefAccess<InventoryGui, bool>("m_multiCrafting");

		private static bool _craftingFood;

		private static bool _cookingRaised;

		private void Awake()
		{
			//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00da: Expected O, but got Unknown
			Type type = AccessTools.TypeByName("MasterCooking.MasterCookingPlugin");
			_health = Entry<float>(type, "HealthFactor");
			_stamina = Entry<float>(type, "StaminaFactor");
			_regen = Entry<float>(type, "RegenFactor");
			_eitr = Entry<float>(type, "EitrFactor");
			_duration = Entry<float>(type, "DurationFactor");
			_disableDecay = Entry<bool>(type, "DisableStatDecay");
			if (_health == null || _stamina == null || _regen == null || _eitr == null || _duration == null || _disableDecay == null)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)"Disabled, MasterCooking's settings were not found.");
				return;
			}
			_log = ((BaseUnityPlugin)this).Logger;
			_logXp = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "Log Cooking XP", false, "Writes every crafted food and Cooking XP gain to the BepInEx log.");
			Harmony val = new Harmony("pieter.valheim.mastercookingfixes");
			string[] array = new string[3] { "EatFood", "UpdateFood", "GetTotalFoodValue" };
			foreach (string text in array)
			{
				val.Unpatch((MethodBase)AccessTools.Method(typeof(Player), text, (Type[])null, (Type[])null), (HarmonyPatchType)0, "com.ruijven.mastercooking");
			}
			val.PatchAll(typeof(Plugin).Assembly);
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Replaced MasterCooking's food hooks; regen and tooltips include the Cooking bonus; crafting food trains Cooking.");
		}

		private static ConfigEntry<T> Entry<T>(Type type, string field)
		{
			if (!(type == null))
			{
				return AccessTools.Field(type, field)?.GetValue(null) as ConfigEntry<T>;
			}
			return null;
		}

		private static Factors Current()
		{
			Player localPlayer = Player.m_localPlayer;
			float num = (((Object)(object)localPlayer == (Object)null) ? 0f : Mathf.Clamp01(((Character)localPlayer).GetSkillLevel((SkillType)105) / 100f));
			return new Factors
			{
				Health = 1f + num * (_health.Value - 1f),
				Stamina = 1f + num * (_stamina.Value - 1f),
				Regen = 1f + num * (_regen.Value - 1f),
				Eitr = 1f + num * (_eitr.Value - 1f),
				Duration = 1f + num * (_duration.Value - 1f)
			};
		}

		private static bool IsFood(SharedData s)
		{
			if (s != null)
			{
				if (!(s.m_food > 0f) && !(s.m_foodStamina > 0f))
				{
					return s.m_foodEitr > 0f;
				}
				return true;
			}
			return false;
		}

		private static float Boosted(float value, float factor)
		{
			return Mathf.Round(value * factor * 10f) / 10f;
		}

		private static void Boost(SharedData s, Factors f, List<SharedData> owned)
		{
			if (IsFood(s) && !Boosted_.ContainsKey(s))
			{
				Boosted_[s] = new Original
				{
					Food = s.m_food,
					Stamina = s.m_foodStamina,
					Eitr = s.m_foodEitr,
					Regen = s.m_foodRegen
				};
				s.m_food = Boosted(s.m_food, f.Health);
				s.m_foodStamina = Boosted(s.m_foodStamina, f.Stamina);
				s.m_foodEitr = Boosted(s.m_foodEitr, f.Eitr);
				s.m_foodRegen = Boosted(s.m_foodRegen, f.Regen);
				owned.Add(s);
			}
		}

		private static void Restore(List<SharedData> owned)
		{
			if (owned == null)
			{
				return;
			}
			foreach (SharedData item in owned)
			{
				if (Boosted_.TryGetValue(item, out var value))
				{
					item.m_food = value.Food;
					item.m_foodStamina = value.Stamina;
					item.m_foodEitr = value.Eitr;
					item.m_foodRegen = value.Regen;
					Boosted_.Remove(item);
				}
			}
		}

		private static float OriginalFood(SharedData s)
		{
			if (!Boosted_.TryGetValue(s, out var value))
			{
				return s.m_food;
			}
			return value.Food;
		}

		private static float OriginalStamina(SharedData s)
		{
			if (!Boosted_.TryGetValue(s, out var value))
			{
				return s.m_foodStamina;
			}
			return value.Stamina;
		}

		private static float OriginalEitr(SharedData s)
		{
			if (!Boosted_.TryGetValue(s, out var value))
			{
				return s.m_foodEitr;
			}
			return value.Eitr;
		}

		private static string LcfspExecuting()
		{
			Type type = AccessTools.TypeByName("LocalCraftFromStoragePrime.PaymentAction");
			FieldInfo fieldInfo = ((type == null) ? null : AccessTools.Field(type, "Executing"));
			if (!(fieldInfo == null))
			{
				return (fieldInfo.GetValue(null) != null).ToString();
			}
			return "n/a";
		}
	}
}