Decompiled source of SulfurRecipeBook v2.0.2

plugins/net.mizle.SulfurRecipeBook.dll

Decompiled a month 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 PerfectRandom.Sulfur.Core;
using PerfectRandom.Sulfur.Core.Items;
using PerfectRandom.Sulfur.Core.Stats;
using PerfectRandom.Sulfur.Core.UI.ItemDescription;
using TMPro;
using UnityEngine;
using UnityEngine.UI;

[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: AssemblyFileVersion("2.0.0.0")]
[assembly: AssemblyInformationalVersion("2.0.0")]
[assembly: AssemblyProduct("SulfurRecipeBook")]
[assembly: AssemblyTitle("net.mizle.SulfurRecipeBook")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("2.0.0.0")]
[module: UnverifiableCode]
namespace SulfurRecipeBook;

[BepInPlugin("net.mizle.SulfurRecipeBook", "SulfurRecipeBook", "2.0.0")]
public class Plugin : BaseUnityPlugin
{
	internal static ManualLogSource Logger;

	public static ConfigEntry<bool> ConfigShowHealPerSlot;

	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");
		Harmony.CreateAndPatchAll(typeof(RecipeBook), (string)null);
		Harmony.CreateAndPatchAll(typeof(ReversePatch), (string)null);
	}
}
internal class IngredientRange
{
	public int Min { get; set; } = int.MaxValue;


	public int Max { get; set; }
}
public static class RecipeAnalyzer
{
	public static Dictionary<string, string> AnalyzeRecipes(List<CraftingRecipe> recipes)
	{
		Dictionary<string, List<(Dictionary<string, int>, string, int, CraftingRecipe)>> dictionary = new Dictionary<string, List<(Dictionary<string, int>, string, int, CraftingRecipe)>>();
		foreach (CraftingRecipe recipe in recipes)
		{
			IOrderedEnumerable<string> values = from i in recipe.itemsNeeded
				select i.item.identifier into name
				orderby name
				select name;
			string key = string.Join("+", values);
			Dictionary<string, int> item = recipe.itemsNeeded.ToDictionary((IngredientDefinition ing) => ing.item.identifier, (IngredientDefinition ing) => ing.quantity);
			if (!dictionary.ContainsKey(key))
			{
				dictionary[key] = new List<(Dictionary<string, int>, string, int, CraftingRecipe)>();
			}
			dictionary[key].Add((item, recipe.createsItem.identifier, recipe.quantityCreated, recipe));
		}
		Dictionary<string, string> dictionary2 = new Dictionary<string, string>();
		foreach (KeyValuePair<string, List<(Dictionary<string, int>, string, int, CraftingRecipe)>> item5 in dictionary)
		{
			item5.Deconstruct(out var key2, out var value);
			string key3 = key2;
			List<(Dictionary<string, int> Ingredients, string Result, int Quantity, CraftingRecipe Recipe)> recipesList = value;
			Dictionary<string, IngredientRange> dictionary3 = new Dictionary<string, IngredientRange>();
			Dictionary<string, (IngredientRange, List<CraftingRecipe>)> dictionary4 = new Dictionary<string, (IngredientRange, List<CraftingRecipe>)>();
			foreach (var (dictionary5, key4, val, item2) in recipesList)
			{
				foreach (KeyValuePair<string, int> item6 in dictionary5)
				{
					item6.Deconstruct(out key2, out var value2);
					string key5 = key2;
					int val2 = value2;
					if (!dictionary3.ContainsKey(key5))
					{
						dictionary3[key5] = new IngredientRange();
					}
					dictionary3[key5].Min = Math.Min(dictionary3[key5].Min, val2);
					dictionary3[key5].Max = Math.Max(dictionary3[key5].Max, val2);
				}
				if (!dictionary4.ContainsKey(key4))
				{
					dictionary4[key4] = (new IngredientRange(), new List<CraftingRecipe>());
				}
				dictionary4[key4].Item1.Min = Math.Min(dictionary4[key4].Item1.Min, val);
				dictionary4[key4].Item1.Max = Math.Max(dictionary4[key4].Item1.Max, val);
				dictionary4[key4].Item2.Add(item2);
			}
			List<string> values2 = dictionary3.Select((KeyValuePair<string, IngredientRange> kvp) => FormatRange(GetDisplayName(recipesList[0].Recipe, kvp.Key), kvp.Value)).ToList();
			foreach (KeyValuePair<string, (IngredientRange, List<CraftingRecipe>)> item7 in dictionary4)
			{
				item7.Deconstruct(out key2, out var value3);
				(IngredientRange, List<CraftingRecipe>) tuple2 = value3;
				string identifier = key2;
				IngredientRange item3 = tuple2.Item1;
				List<CraftingRecipe> item4 = tuple2.Item2;
				float num = CalculateHealPerSlot(item4[0].createsItem);
				string text = FormatRange(GetDisplayName(item4[0], identifier), item3);
				if (Plugin.ConfigShowHealPerSlot.Value && num > 0f)
				{
					text += $"<size=-1> [{num:F2}]</size>";
				}
				string value4 = string.Join("<size=-1><color=#6D8791> + </color></size>", values2) + "<size=-1><color=#6D8791> = </color></size>" + text;
				dictionary2[key3] = value4;
			}
		}
		return dictionary2;
	}

	private static string GetDisplayName(CraftingRecipe recipe, string identifier)
	{
		//IL_0043: Unknown result type (might be due to invalid IL or missing references)
		if (identifier == recipe.createsItem.identifier)
		{
			return recipe.createsItem.LocalizedDisplayName;
		}
		return ((IEnumerable<IngredientDefinition>)recipe.itemsNeeded).FirstOrDefault((Func<IngredientDefinition, bool>)((IngredientDefinition i) => i.item.identifier == identifier)).item.LocalizedDisplayName ?? identifier;
	}

	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.attributeNew.id == 54);
		if (obj == null)
		{
			return 0f;
		}
		return obj.totalValueOverride / (float)num;
	}

	private static string FormatRange(string itemName, IngredientRange range)
	{
		if (range.Min == range.Max)
		{
			return $"{itemName}<size=-1><color=#6D8791>x{range.Min}</color></size>";
		}
		return $"{itemName}<size=-1><color=#6D8791>x({range.Min}~{range.Max})</color></size>";
	}
}
[HarmonyPatch]
internal class ReversePatch
{
	[HarmonyReversePatch(/*Could not decode attribute arguments.*/)]
	[HarmonyPatch(typeof(ItemDescription), "AddAttribute", new Type[] { typeof(string) })]
	public static void AddTextLineAsAttribute(object instance, string key)
	{
		throw new NotImplementedException("It's a stub");
	}

	[HarmonyReversePatch(/*Could not decode attribute arguments.*/)]
	[HarmonyPatch(typeof(ItemDescription), "AddDescriptionText", new Type[] { typeof(string) })]
	public static void AddTextLineAsDescription(object instance, string descriptionText)
	{
		throw new NotImplementedException("It's a stub");
	}
}
[HarmonyPatch]
internal class RecipeBook
{
	private static readonly List<CraftingRecipe> AllRecipe = ((Func<List<CraftingRecipe>>)delegate
	{
		CraftingManager instance = StaticInstance<CraftingManager>.Instance;
		return (from r in instance.genericRecipes.Concat(instance.cookingRecipes)
			where r.canBeCrafted
			select r).ToList();
	})();

	private static bool _fontAtlasPopulationModeUpdated;

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

	[HarmonyPostfix]
	[HarmonyPatch(typeof(ItemDescription), "Setup")]
	private static void DescriptionSetupPostfix(ItemDescription __instance, InventoryItem inventoryItem)
	{
		//IL_0046: Unknown result type (might be due to invalid IL or missing references)
		//IL_0050: Expected O, but got Unknown
		//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
		List<CraftingRecipe> list = FindRecipe(inventoryItem);
		if (list.Count == 0)
		{
			return;
		}
		ReversePatch.AddTextLineAsAttribute(__instance, "Recipes:");
		Dictionary<string, string> dictionary = RecipeAnalyzer.AnalyzeRecipes(list);
		foreach (string value in dictionary.Values)
		{
			ReversePatch.AddTextLineAsDescription(__instance, value);
			UpdateLatestHorizontalLayoutGroupPadding(__instance, new RectOffset(8, 2, 2, 0));
		}
		if (!_fontAtlasPopulationModeUpdated)
		{
			UpdateLatestDescriptionFontAtlasPopulationMode(__instance);
			_fontAtlasPopulationModeUpdated = true;
		}
		int num = ((dictionary.Count > 20) ? 500 : 350);
		((Component)__instance).gameObject.GetComponent<RectTransform>().sizeDelta = new Vector2((float)num, 100f);
	}

	[HarmonyPostfix]
	[HarmonyPatch(typeof(ItemDescription), "AddDescriptionText")]
	private static void AddDescriptionTextPostfix(ItemDescription __instance, string descriptionText)
	{
		//IL_0079: Unknown result type (might be due to invalid IL or missing references)
		//IL_0080: Invalid comparison between Unknown and I4
		if (!Plugin.ConfigShowHealPerSlot.Value)
		{
			return;
		}
		InventoryItem value = Traverse.Create((object)__instance).Field("inventoryRepresentation").GetValue<InventoryItem>();
		if (value.itemDefinition.buffsOnConsume.Find((BuffDefinition b) => (int)b.attributeNew.id == 54) == null)
		{
			return;
		}
		foreach (BuffDefinition item in value.itemDefinition.buffsOnConsume)
		{
			if ((int)item.attributeNew.id != 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(value.itemDefinition);
			string newDescription = $"{descriptionText} [Heal per Slot: {num}]";
			UpdateLatestDescription(__instance, newDescription);
		}
	}

	private static void UpdateLatestHorizontalLayoutGroupPadding(ItemDescription instance, RectOffset padding)
	{
		Transform child = ((Component)instance).transform.GetChild(0);
		HorizontalLayoutGroup componentInChildren = ((Component)child.GetChild(child.childCount - 1)).GetComponentInChildren<HorizontalLayoutGroup>();
		if ((Object)(object)componentInChildren == (Object)null)
		{
			Plugin.Logger.LogInfo((object)"No HorizontalLayoutGroup found.");
		}
		else
		{
			((LayoutGroup)componentInChildren).padding = padding;
		}
	}

	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;
		}
	}

	private static void UpdateLatestDescriptionFontAtlasPopulationMode(ItemDescription instance)
	{
		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.");
			return;
		}
		Plugin.Logger.LogInfo((object)("Updating font settings: " + ((Object)componentInChildren).name));
		((TMP_Text)componentInChildren).font.atlasPopulationMode = (AtlasPopulationMode)0;
	}
}
public static class MyPluginInfo
{
	public const string PLUGIN_GUID = "net.mizle.SulfurRecipeBook";

	public const string PLUGIN_NAME = "SulfurRecipeBook";

	public const string PLUGIN_VERSION = "2.0.0";
}