Decompiled source of Simple Item Upgrade Preview v1.0.0

BepInEx/plugins/SimpleItemUpgradePreview/SimpleItemUpgradePreview.dll

Decompiled 2 weeks ago
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text.RegularExpressions;
using BepInEx;
using HarmonyLib;
using TMPro;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: AssemblyVersion("0.0.0.0")]
namespace SimpleUpgradePreview;

[BepInPlugin("local.che.simpleupgradepreview", "Simple Item Upgrade Preview", "1.0.0")]
public sealed class Plugin : BaseUnityPlugin
{
	public const string Id = "local.che.simpleupgradepreview";

	private Harmony harmony;

	internal static Plugin Instance;

	internal static bool Failed;

	private void Awake()
	{
		//IL_0011: Unknown result type (might be due to invalid IL or missing references)
		//IL_001b: Expected O, but got Unknown
		Instance = this;
		try
		{
			PreviewPatch.ValidateApi();
			harmony = new Harmony("local.che.simpleupgradepreview");
			harmony.PatchAll(typeof(Plugin).Assembly);
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Simple Item Upgrade Preview ready: current -> next stats in the Upgrade panel.");
		}
		catch (Exception error)
		{
			StopPreview(error);
		}
	}

	internal static void StopPreview(Exception error)
	{
		if (!Failed)
		{
			Failed = true;
			((BaseUnityPlugin)Instance).Logger.LogWarning((object)("Upgrade preview disabled; normal crafting remains available. " + error));
		}
	}

	private void OnDestroy()
	{
		if (harmony != null)
		{
			harmony.UnpatchSelf();
		}
		Instance = null;
	}
}
[HarmonyPatch(typeof(InventoryGui), "UpdateRecipe")]
internal static class PreviewPatch
{
	internal struct Context
	{
		public ItemData Item;

		public int Quality;
	}

	private static readonly FieldInfo Selected = AccessTools.Field(typeof(InventoryGui), "m_selectedRecipe");

	private static MethodInfo getItem;

	private static MethodInfo getRecipe;

	private static ItemData cachedItem;

	private static int cachedQuality;

	private static string cachedInput;

	private static string cachedOutput;

	private static float refreshAt;

	private static readonly string[] Tokens = new string[16]
	{
		"$item_durability", "$item_armor", "$item_blockarmor", "$item_blockforce", "$item_parrybonus", "$item_parryadrenaline", "$item_knockback", "$inventory_damage", "$inventory_blunt", "$inventory_slash",
		"$inventory_pierce", "$inventory_fire", "$inventory_frost", "$inventory_lightning", "$inventory_poison", "$inventory_spirit"
	};

	internal static void ValidateApi()
	{
		if (Selected == null)
		{
			throw new MissingFieldException("InventoryGui.m_selectedRecipe");
		}
		getItem = AccessTools.PropertyGetter(Selected.FieldType, "ItemData");
		getRecipe = AccessTools.PropertyGetter(Selected.FieldType, "Recipe");
		if (getItem == null || getRecipe == null)
		{
			throw new MissingMemberException("RecipeDataPair accessors");
		}
	}

	private static void Prefix(InventoryGui __instance, out Context __state)
	{
		__state = default(Context);
		if (Plugin.Failed)
		{
			return;
		}
		try
		{
			object value = Selected.GetValue(__instance);
			object? obj = getItem.Invoke(value, null);
			ItemData val = (ItemData)((obj is ItemData) ? obj : null);
			object? obj2 = getRecipe.Invoke(value, null);
			Recipe val2 = (Recipe)((obj2 is Recipe) ? obj2 : null);
			if (val == null || (Object)(object)val2 == (Object)null || val.m_shared == null)
			{
				return;
			}
			Player localPlayer = Player.m_localPlayer;
			if (!((Object)(object)localPlayer == (Object)null))
			{
				CraftingStation currentCraftingStation = localPlayer.GetCurrentCraftingStation();
				if (val.m_quality < val.m_shared.m_maxQuality || (!((Object)(object)currentCraftingStation == (Object)null) && currentCraftingStation.m_upgrader))
				{
					__state.Item = val;
					__state.Quality = val.m_quality;
				}
			}
		}
		catch (Exception error)
		{
			Plugin.StopPreview(error);
		}
	}

	[HarmonyPriority(0)]
	[HarmonyAfter(new string[] { "randyknapp.mods.epicloot" })]
	private static void Postfix(InventoryGui __instance, Context __state)
	{
		if (Plugin.Failed || __state.Item == null)
		{
			return;
		}
		try
		{
			if (__state.Item.m_quality != __state.Quality)
			{
				return;
			}
			object value = Selected.GetValue(__instance);
			if (!object.ReferenceEquals(getItem.Invoke(value, null), __state.Item))
			{
				return;
			}
			TMP_Text recipeDecription = __instance.m_recipeDecription;
			if ((Object)(object)recipeDecription == (Object)null || !((Behaviour)recipeDecription).enabled)
			{
				return;
			}
			string text = recipeDecription.text;
			if (object.ReferenceEquals(cachedItem, __state.Item) && cachedQuality == __state.Quality && Time.unscaledTime < refreshAt && (text == cachedInput || text == cachedOutput))
			{
				recipeDecription.text = cachedOutput;
				return;
			}
			HashSet<string> hashSet = new HashSet<string>();
			string[] tokens = Tokens;
			foreach (string text2 in tokens)
			{
				hashSet.Add(Localization.instance.Localize(text2));
			}
			string current = Localization.instance.Localize(ItemData.GetTooltip(__state.Item, __state.Quality, true, (float)Game.m_worldLevel, -1, false));
			string upgraded = Localization.instance.Localize(ItemData.GetTooltip(__state.Item, __state.Quality + 1, true, (float)Game.m_worldLevel, -1, false));
			string text3 = ComparisonText.Apply(text, current, upgraded, hashSet);
			cachedItem = __state.Item;
			cachedQuality = __state.Quality;
			cachedInput = text;
			cachedOutput = text3;
			refreshAt = Time.unscaledTime + 0.5f;
			recipeDecription.text = text3;
		}
		catch (Exception error)
		{
			Plugin.StopPreview(error);
		}
	}
}
public static class ComparisonText
{
	private static readonly Regex Tags = new Regex("<[^>]+>");

	private static readonly Regex Number = new Regex("^[+-]?\\d+(?:[.,]\\d+)?%?");

	private static Dictionary<string, string> ReadLines(string text, ISet<string> labels)
	{
		Dictionary<string, string> dictionary = new Dictionary<string, string>();
		string[] array = text.Split('\n');
		foreach (string input in array)
		{
			string text2 = Tags.Replace(input, "").Trim();
			int num = text2.IndexOf(':');
			if (num < 0)
			{
				continue;
			}
			string text3 = text2.Substring(0, num).Trim();
			if (labels.Contains(text3))
			{
				string value = text2.Substring(num + 1).Trim();
				if (dictionary.ContainsKey(text3))
				{
					dictionary[text3] = null;
				}
				else
				{
					dictionary.Add(text3, value);
				}
			}
		}
		return dictionary;
	}

	public static string Apply(string displayed, string current, string upgraded, ISet<string> labels)
	{
		if (string.IsNullOrEmpty(displayed) || string.IsNullOrEmpty(current) || string.IsNullOrEmpty(upgraded))
		{
			return displayed;
		}
		Dictionary<string, string> dictionary = ReadLines(current, labels);
		Dictionary<string, string> dictionary2 = ReadLines(upgraded, labels);
		Dictionary<string, string> dictionary3 = ReadLines(displayed, labels);
		string[] array = displayed.Split('\n');
		for (int i = 0; i < array.Length; i++)
		{
			string text = Tags.Replace(array[i], "").Trim();
			int num = text.IndexOf(':');
			if (num < 0)
			{
				continue;
			}
			string text2 = text.Substring(0, num).Trim();
			if (dictionary.TryGetValue(text2, out var value) && value != null && dictionary2.TryGetValue(text2, out var value2) && value2 != null && dictionary3.TryGetValue(text2, out var value3) && value3 != null)
			{
				Match match = Number.Match(value);
				Match match2 = Number.Match(value2);
				if (match.Success && match2.Success && !(value == value2))
				{
					string text3 = value2.Substring(match2.Length);
					array[i] = text2 + ": <color=#BBBBBB>" + match.Value + "</color> → <color=#FFD966>" + match2.Value + "</color>" + text3;
				}
			}
		}
		return string.Join("\n", array);
	}
}