Decompiled source of SulfurRecipeBook v2.1.0

plugins/net.mizle.SulfurRecipeBook.dll

Decompiled 2 weeks 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 BepInEx.Logging;
using HarmonyLib;
using I2.Loc;
using Microsoft.CodeAnalysis;
using PerfectRandom.Sulfur.Core;
using PerfectRandom.Sulfur.Core.Items;
using PerfectRandom.Sulfur.Core.Stats;
using PerfectRandom.Sulfur.Core.UI.Inventory;
using PerfectRandom.Sulfur.Core.UI.ItemDescription;
using TMPro;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = "")]
[assembly: AssemblyCompany("net.mizle.SulfurRecipeBook")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Item tooltips show recipes that can be created with that item.")]
[assembly: AssemblyFileVersion("2.1.0.0")]
[assembly: AssemblyInformationalVersion("2.1.0")]
[assembly: AssemblyProduct("SulfurRecipeBook")]
[assembly: AssemblyTitle("net.mizle.SulfurRecipeBook")]
[assembly: AssemblyMetadata("RepositoryUrl", "https://github.com/eai04191/SulfurRecipeBook")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("2.1.0.0")]
[module: UnverifiableCode]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
}
namespace SulfurRecipeBook
{
	[BepInPlugin("net.mizle.SulfurRecipeBook", "SulfurRecipeBook", "2.1.0")]
	public class Plugin : BaseUnityPlugin
	{
		internal static ManualLogSource Logger;

		public static ConfigEntry<bool> ConfigShowHealPerSlot;

		public static ConfigEntry<KeyCode> ConfigPrevPageKey;

		public static ConfigEntry<KeyCode> ConfigNextPageKey;

		private void Awake()
		{
			Logger = ((BaseUnityPlugin)this).Logger;
			Logger.LogInfo((object)"Plugin net.mizle.SulfurRecipeBook is loaded!");
			ConfigShowHealPerSlot = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "ShowHealPerSlot", false, "Show heal per slot in item description");
			ConfigPrevPageKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Keybinds", "PrevPageKey", (KeyCode)282, "Key to go to previous recipe page");
			ConfigNextPageKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Keybinds", "NextPageKey", (KeyCode)283, "Key to go to next recipe page");
			Harmony.CreateAndPatchAll(typeof(RecipeBook), (string)null);
			Harmony.CreateAndPatchAll(typeof(ReversePatch), (string)null);
		}
	}
	internal class QuantityRange
	{
		public int Min { get; set; } = int.MaxValue;


		public int Max { get; set; }

		public void Update(int value)
		{
			Min = Math.Min(Min, value);
			Max = Math.Max(Max, value);
		}
	}
	public static class RecipeAnalyzer
	{
		private const string SeparatorColor = "<size=-1><color=#6D8791>";

		private const string ColorEnd = "</color></size>";

		public static Dictionary<string, string> AnalyzeRecipes(List<RecipeData> recipes)
		{
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_0144: Unknown result type (might be due to invalid IL or missing references)
			//IL_0149: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00df: Unknown result type (might be due to invalid IL or missing references)
			//IL_0193: Unknown result type (might be due to invalid IL or missing references)
			//IL_0198: Unknown result type (might be due to invalid IL or missing references)
			//IL_019a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0118: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ba: Unknown result type (might be due to invalid IL or missing references)
			IEnumerable<IGrouping<string, RecipeData>> enumerable = from recipe in recipes
				group recipe by string.Join("+", from i in recipe.itemsNeeded
					select i.item.value into x
					orderby x
					select x);
			Dictionary<string, string> dictionary = new Dictionary<string, string>();
			foreach (IGrouping<string, RecipeData> item3 in enumerable)
			{
				List<RecipeData> list = item3.ToList();
				Dictionary<ushort, QuantityRange> dictionary2 = new Dictionary<ushort, QuantityRange>();
				Dictionary<ushort, (QuantityRange, RecipeData)> dictionary3 = new Dictionary<ushort, (QuantityRange, RecipeData)>();
				foreach (RecipeData item4 in list)
				{
					foreach (IngredientDefinition item5 in item4.itemsNeeded)
					{
						ushort value = item5.item.value;
						if (!dictionary2.TryGetValue(value, out var value2))
						{
							value2 = (dictionary2[value] = new QuantityRange());
						}
						value2.Update(item5.quantity);
					}
					ushort value3 = item4.createsItem.value;
					if (!dictionary3.TryGetValue(value3, out var value4))
					{
						value4 = (dictionary3[value3] = (new QuantityRange(), item4));
					}
					value4.Item1.Update(item4.quantityCreated);
				}
				RecipeData firstRecipe = list[0];
				IEnumerable<string> values = dictionary2.Select((KeyValuePair<ushort, QuantityRange> kvp) => FormatRange(GetDisplayName(firstRecipe, kvp.Key), kvp.Value));
				foreach (KeyValuePair<ushort, (QuantityRange, RecipeData)> item6 in dictionary3)
				{
					item6.Deconstruct(out var key, out var value5);
					(QuantityRange, RecipeData) tuple2 = value5;
					ushort identifier = key;
					QuantityRange item = tuple2.Item1;
					RecipeData item2 = tuple2.Item2;
					string text = FormatRange(GetDisplayName(item2, identifier), item);
					if (Plugin.ConfigShowHealPerSlot.Value)
					{
						float num = CalculateHealPerSlot(AssetAccess.GetAsset(item2.createsItem));
						if (num > 0f)
						{
							text += $"<size=-1> [{num:F2}]</size>";
						}
					}
					string value6 = string.Join("<size=-1><color=#6D8791> + </color></size>", values) + "<size=-1><color=#6D8791> = </color></size>" + text;
					dictionary[item3.Key] = value6;
				}
			}
			return dictionary;
		}

		private static string GetDisplayName(RecipeData recipe, ushort identifier)
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: 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_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			if (identifier == recipe.createsItem.value)
			{
				return AssetAccess.GetAsset(recipe.createsItem).LocalizedDisplayName;
			}
			return AssetAccess.GetAsset(((IEnumerable<IngredientDefinition>)recipe.itemsNeeded).FirstOrDefault((Func<IngredientDefinition, bool>)((IngredientDefinition i) => i.item.value == identifier)).item).LocalizedDisplayName ?? identifier.ToString();
		}

		public static float CalculateHealPerSlot(ItemDefinition item)
		{
			int num = ((Vector2Int)(ref item.inventorySize)).x * ((Vector2Int)(ref item.inventorySize)).y;
			BuffDefinition? obj = item.buffsOnConsume.Find((BuffDefinition buff) => (int)buff.attributeId == 54);
			if (obj == null)
			{
				return 0f;
			}
			return obj.totalValueOverride / (float)num;
		}

		private static string FormatRange(string itemName, QuantityRange range)
		{
			string text = ((range.Min == range.Max) ? $"x{range.Min}" : $"x({range.Min}~{range.Max})");
			return itemName + "<size=-1><color=#6D8791>" + text + "</color></size>";
		}
	}
	[HarmonyPatch]
	internal class ReversePatch
	{
		[HarmonyReversePatch(/*Could not decode attribute arguments.*/)]
		[HarmonyPatch(/*Could not decode attribute arguments.*/)]
		public static void AddTextLineAsAttribute(object instance, string key, ref int childIndex, string value = "", string modifier = "", bool skipSeparator = false)
		{
			throw new NotImplementedException("It's a stub");
		}

		[HarmonyReversePatch(/*Could not decode attribute arguments.*/)]
		[HarmonyPatch(/*Could not decode attribute arguments.*/)]
		public static void AddTextLineAsDescription(object instance, string descriptionText, ref int childIndex)
		{
			throw new NotImplementedException("It's a stub");
		}
	}
	[HarmonyPatch]
	internal class RecipeBook
	{
		private static readonly List<RecipeData> AllRecipe = ((Func<List<RecipeData>>)delegate
		{
			RecipeDatabase recipeDatabase = StaticInstance<AsyncAssetLoading>.Instance.recipeDatabase;
			return (from r in recipeDatabase.enchantmentRecipes.Concat(recipeDatabase.cookingRecipes)
				where r.canBeCrafted
				select r).ToList();
		})();

		private static bool _fontAtlasPopulationModeUpdated;

		private static int _currentPage;

		private static int _totalPages;

		private const int ItemsPerPage = 20;

		private static ItemDescription? _lastItemDescription;

		private static InventoryItem? _lastInventoryItem;

		private static Vector2? _originalSizeDelta;

		private static InventoryItem GetInventoryItem(ItemDescription instance)
		{
			return Traverse.Create((object)instance).Field("inventoryRepresentation").GetValue<InventoryItem>();
		}

		private static Transform GetContentTransform(ItemDescription instance)
		{
			return Traverse.Create((object)instance).Field("contentTransform").GetValue<Transform>();
		}

		private static List<RecipeData> FindRecipe(InventoryItem targetItem)
		{
			InventoryItem targetItem2 = targetItem;
			return AllRecipe.Where((RecipeData recipe) => recipe.itemsNeeded.Any((IngredientDefinition ingredient) => ingredient.item.value == targetItem2.itemDefinition.id.value)).ToList();
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(InventoryUI), "Update")]
		private static void HandlePageNavigationInput()
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			int num = _currentPage;
			if (Input.GetKeyDown(Plugin.ConfigPrevPageKey.Value) && _currentPage > 0)
			{
				num = _currentPage - 1;
			}
			else if (Input.GetKeyDown(Plugin.ConfigNextPageKey.Value) && _currentPage < _totalPages - 1)
			{
				num = _currentPage + 1;
			}
			if (num != _currentPage)
			{
				_currentPage = num;
				RefreshItemDescription();
				Plugin.Logger.LogInfo((object)$"Switched to page {_currentPage}");
			}
		}

		private static void RefreshItemDescription()
		{
			if (_lastItemDescription != null)
			{
				InventoryItem inventoryItem = GetInventoryItem(_lastItemDescription);
				((Component)_lastItemDescription).gameObject.SetActive(false);
				((Component)_lastItemDescription).gameObject.SetActive(true);
				_lastItemDescription.Setup(inventoryItem);
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(ItemDescription), "Setup", new Type[] { typeof(InventoryItem) })]
		private static void DescriptionSetupPostfix(ItemDescription __instance, InventoryItem inventoryItem)
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: 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_002a: 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_018a: Unknown result type (might be due to invalid IL or missing references)
			RectTransform component = ((Component)__instance).gameObject.GetComponent<RectTransform>();
			Vector2 valueOrDefault = _originalSizeDelta.GetValueOrDefault();
			if (!_originalSizeDelta.HasValue)
			{
				valueOrDefault = component.sizeDelta;
				_originalSizeDelta = valueOrDefault;
			}
			component.sizeDelta = _originalSizeDelta.Value;
			if ((Object)(object)_lastInventoryItem != (Object)(object)inventoryItem)
			{
				_currentPage = 0;
			}
			_lastItemDescription = __instance;
			_lastInventoryItem = inventoryItem;
			List<RecipeData> list = FindRecipe(inventoryItem);
			if (list.Count == 0)
			{
				return;
			}
			Dictionary<string, string> dictionary = RecipeAnalyzer.AnalyzeRecipes(list);
			Plugin.Logger.LogInfo((object)$"Found {dictionary.Count} recipes for this item.");
			_totalPages = (int)Math.Ceiling((double)dictionary.Count / 20.0);
			List<KeyValuePair<string, string>> list2 = dictionary.Skip(_currentPage * 20).Take(20).ToList();
			Transform contentTransform = GetContentTransform(__instance);
			if (!_fontAtlasPopulationModeUpdated)
			{
				TextMeshProUGUI componentInChildren = ((Component)contentTransform).GetComponentInChildren<TextMeshProUGUI>();
				if (componentInChildren != null)
				{
					((TMP_Text)componentInChildren).font.atlasPopulationMode = (AtlasPopulationMode)0;
					_fontAtlasPopulationModeUpdated = true;
				}
			}
			int childIndex = contentTransform.childCount;
			ReversePatch.AddTextLineAsAttribute(__instance, $"Recipes ({_currentPage + 1} / {_totalPages}):", ref childIndex);
			foreach (KeyValuePair<string, string> item in list2)
			{
				ReversePatch.AddTextLineAsDescription(__instance, item.Value, ref childIndex);
			}
			((Component)__instance).gameObject.GetComponent<RectTransform>().sizeDelta = new Vector2(300f, 100f);
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(ItemDescription), "AddDescriptionText")]
		private static void AddDescriptionTextPostfix(ItemDescription __instance, string descriptionText)
		{
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Invalid comparison between Unknown and I4
			if (!Plugin.ConfigShowHealPerSlot.Value)
			{
				return;
			}
			InventoryItem inventoryItem = GetInventoryItem(__instance);
			if (inventoryItem.itemDefinition.buffsOnConsume.Find((BuffDefinition b) => (int)b.attributeId == 54) == null)
			{
				return;
			}
			foreach (BuffDefinition item in inventoryItem.itemDefinition.buffsOnConsume)
			{
				if ((int)item.attributeId != 54 || LocalizationManager.GetTranslation("ItemDescriptions/DynamicString_HealthConsumable", true, 0, true, false, (GameObject)null, (string)null, true).Replace("VALUE_X", item.totalValueOverride.ToString()).Replace("DURATION_X", item.duration.ToString()) != descriptionText)
				{
					break;
				}
				float num = RecipeAnalyzer.CalculateHealPerSlot(inventoryItem.itemDefinition);
				string newDescription = $"{descriptionText} [Heal per Slot: {num}]";
				UpdateLatestDescription(__instance, newDescription);
			}
		}

		private static void UpdateLatestDescription(ItemDescription instance, string newDescription)
		{
			Transform child = ((Component)instance).transform.GetChild(0);
			TextMeshProUGUI componentInChildren = ((Component)child.GetChild(child.childCount - 1)).GetComponentInChildren<TextMeshProUGUI>();
			if ((Object)(object)componentInChildren == (Object)null)
			{
				Plugin.Logger.LogInfo((object)"No TextMeshProUGUI found.");
			}
			else
			{
				((TMP_Text)componentInChildren).text = newDescription;
			}
		}
	}
	public static class MyPluginInfo
	{
		public const string PLUGIN_GUID = "net.mizle.SulfurRecipeBook";

		public const string PLUGIN_NAME = "SulfurRecipeBook";

		public const string PLUGIN_VERSION = "2.1.0";
	}
}