Decompiled source of CantTouchThis v0.0.1

plugins\CantTouchThis.dll

Decompiled 20 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using Jotunn;
using Jotunn.Entities;
using Jotunn.Managers;
using Jotunn.Utils;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("CantTouchThis")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("CantTouchThis")]
[assembly: AssemblyCopyright("Copyright ©  2021")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("e3243d22-4307-4008-ba36-9f326008cde5")]
[assembly: AssemblyFileVersion("0.0.1")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.0.1.0")]
namespace CantTouchThis;

[BepInPlugin("de.sirskunkalot.CantTouchThis", "CantTouchThis", "0.0.1")]
[BepInDependency(/*Could not decode attribute arguments.*/)]
[NetworkCompatibility(/*Could not decode attribute arguments.*/)]
internal class CantTouchThis : BaseUnityPlugin
{
	[CompilerGenerated]
	private sealed class <DelayedMessage>d__33 : IEnumerator<object>, IDisposable, IEnumerator
	{
		private int <>1__state;

		private object <>2__current;

		public string message;

		object IEnumerator<object>.Current
		{
			[DebuggerHidden]
			get
			{
				return <>2__current;
			}
		}

		object IEnumerator.Current
		{
			[DebuggerHidden]
			get
			{
				return <>2__current;
			}
		}

		[DebuggerHidden]
		public <DelayedMessage>d__33(int <>1__state)
		{
			this.<>1__state = <>1__state;
		}

		[DebuggerHidden]
		void IDisposable.Dispose()
		{
			<>1__state = -2;
		}

		private bool MoveNext()
		{
			switch (<>1__state)
			{
			default:
				return false;
			case 0:
				<>1__state = -1;
				<>2__current = null;
				<>1__state = 1;
				return true;
			case 1:
			{
				<>1__state = -1;
				Player localPlayer = Player.m_localPlayer;
				if (localPlayer != null)
				{
					((Character)localPlayer).Message((MessageType)2, message, 0, (Sprite)null);
				}
				return false;
			}
			}
		}

		bool IEnumerator.MoveNext()
		{
			//ILSpy generated this explicit interface implementation from .override directive in MoveNext
			return this.MoveNext();
		}

		[DebuggerHidden]
		void IEnumerator.Reset()
		{
			throw new NotSupportedException();
		}
	}

	public const string PluginGUID = "de.sirskunkalot.CantTouchThis";

	public const string PluginName = "CantTouchThis";

	public const string PluginVersion = "0.0.1";

	private static CantTouchThis _instance;

	private static readonly CustomLocalization Localization = LocalizationManager.Instance.GetLocalization();

	private static readonly string[] MessageKeys = (from i in Enumerable.Range(1, 14)
		select $"message{i}").ToArray();

	private static ConfigEntry<string> _forbiddenItemsConfig;

	private static ConfigEntry<bool> _removeRecipesFromUIConfig;

	private static HashSet<string> _forbiddenItemsSet;

	private static HashSet<string> _forbiddenItemsCache;

	private static HashSet<string> _allowedItemsCache;

	private void Awake()
	{
		//IL_011c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0121: Unknown result type (might be due to invalid IL or missing references)
		//IL_0129: Expected O, but got Unknown
		//IL_0129: Unknown result type (might be due to invalid IL or missing references)
		//IL_0133: Expected O, but got Unknown
		//IL_0185: 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)
		//IL_0192: Expected O, but got Unknown
		//IL_0192: Unknown result type (might be due to invalid IL or missing references)
		//IL_019c: Expected O, but got Unknown
		_instance = this;
		CustomLocalization localization = Localization;
		string text = "English";
		localization.AddTranslation(ref text, new Dictionary<string, string>
		{
			{ "message1", "Odin forbids {item_name}" },
			{ "message2", "Even Loki wouldn't touch {item_name}" },
			{ "message3", "The gods frown upon your desire for {item_name}" },
			{ "message4", "{item_name}? Not in this realm" },
			{ "message5", "Valhalla has {item_name}. You do not" },
			{ "message6", "A raven whispers: {item_name} is not for you" },
			{ "message7", "The Allfather says no to {item_name}" },
			{ "message8", "You stare at {item_name}. It stares back. You walk away" },
			{ "message9", "You can look at {item_name} but you can't touch" },
			{ "message10", "Can't touch this" },
			{ "message11", "Stop! {item_name} time. Oh wait..." },
			{ "message12", "You shall not {item_name}" },
			{ "message13", "Imagine using {item_name}" },
			{ "message14", "{item_name}? In this economy?" }
		});
		_forbiddenItemsConfig = ((BaseUnityPlugin)this).Config.Bind<string>("Config", "Forbidden Items", (string)null, new ConfigDescription("Comma separated list of forbidden items. Syncs from server. Evaluated at runtime when changed.", (AcceptableValueBase)null, new object[1] { (object)new ConfigurationManagerAttributes
		{
			IsAdminOnly = true
		} }));
		_forbiddenItemsConfig.SettingChanged += delegate
		{
			ResetForbiddenItems();
		};
		ResetForbiddenItems();
		_removeRecipesFromUIConfig = ((BaseUnityPlugin)this).Config.Bind<bool>("Config", "Hide Forbidden Items in UI", false, new ConfigDescription("Forbidden items will not show up in the crafting and building menu when enabled. Client side only.", (AcceptableValueBase)null, new object[1] { (object)new ConfigurationManagerAttributes
		{
			IsAdminOnly = false
		} }));
		Harmony.CreateAndPatchAll(typeof(CantTouchThis), "de.sirskunkalot.CantTouchThis");
	}

	[HarmonyPatch(typeof(Game), "Start")]
	[HarmonyPostfix]
	private static void PostfixGameStart()
	{
		CraftingDataCache.Build();
	}

	[HarmonyPatch(typeof(Game), "OnDestroy")]
	[HarmonyPostfix]
	private static void PostfixGameDestroy()
	{
		CraftingDataCache.Invalidate();
	}

	[HarmonyPatch(typeof(Player), "CanConsumeItem")]
	[HarmonyPrefix]
	private static bool PrefixPlayerCanConsumeItem(Player __instance, ItemData item)
	{
		return CheckConsumption(__instance, item);
	}

	[HarmonyPatch(typeof(Player), "ConsumeItem")]
	[HarmonyPrefix]
	private static bool PrefixPlayerConsumeItem(Player __instance, ItemData item)
	{
		return CheckConsumption(__instance, item);
	}

	private static bool CheckConsumption(Player player, ItemData item)
	{
		//IL_0021: Unknown result type (might be due to invalid IL or missing references)
		//IL_0027: Invalid comparison between Unknown and I4
		if (!Object.op_Implicit((Object)(object)Player.m_localPlayer) || (Object)(object)Player.m_localPlayer != (Object)(object)player)
		{
			return true;
		}
		if ((int)item.m_shared.m_itemType != 2)
		{
			return true;
		}
		if (IsForbidden(item.m_shared.m_name))
		{
			Message(item.m_shared.m_name);
			return false;
		}
		return true;
	}

	[HarmonyPatch(typeof(InventoryGui), "OnCraftPressed")]
	[HarmonyPrefix]
	private static bool PrefixOnCraftPressed(InventoryGui __instance)
	{
		Recipe recipe = ((RecipeDataPair)(ref __instance.m_selectedRecipe)).Recipe;
		if ((Object)(object)recipe?.m_item == (Object)null)
		{
			return true;
		}
		string name = recipe.m_item.m_itemData.m_shared.m_name;
		if (IsForbidden(name))
		{
			Message(name);
			return false;
		}
		return true;
	}

	[HarmonyPatch(typeof(Player), "GetAvailableRecipes")]
	[HarmonyPostfix]
	private static void PostfixGetAvailableRecipes(ref List<Recipe> available)
	{
		if (_removeRecipesFromUIConfig.Value)
		{
			available.RemoveAll((Recipe recipe) => !((Object)(object)recipe?.m_item == (Object)null) && IsForbidden(recipe.m_item.m_itemData.m_shared.m_name));
		}
	}

	[HarmonyPatch(typeof(Player), "TryPlacePiece")]
	[HarmonyPrefix]
	private static bool PrefixTryPlacePiece(Player __instance, Piece piece)
	{
		if (!Object.op_Implicit((Object)(object)Player.m_localPlayer) || (Object)(object)Player.m_localPlayer != (Object)(object)__instance)
		{
			return true;
		}
		if ((Object)(object)piece == (Object)null)
		{
			return true;
		}
		if (IsPieceForbidden(piece))
		{
			Message(piece.m_name);
			return false;
		}
		return true;
	}

	[HarmonyPatch(typeof(PieceTable), "UpdateAvailable")]
	[HarmonyPostfix]
	private static void PostfixUpdateAvailable(PieceTable __instance)
	{
		if (!_removeRecipesFromUIConfig.Value)
		{
			return;
		}
		foreach (List<Piece> availablePiece in __instance.m_availablePieces)
		{
			availablePiece.RemoveAll((Piece piece) => IsPieceForbidden(piece));
		}
	}

	private static bool IsPieceForbidden(Piece piece)
	{
		if ((Object)(object)piece == (Object)null)
		{
			return false;
		}
		if (IsForbidden(piece.m_name))
		{
			return true;
		}
		foreach (string pieceRequirement in CraftingDataCache.Get().GetPieceRequirements(piece.m_name))
		{
			if (IsForbidden(pieceRequirement))
			{
				return true;
			}
		}
		return false;
	}

	[HarmonyPatch(typeof(CookingStation), "OnUseItem")]
	[HarmonyPrefix]
	private static bool PrefixCookingStationUseItem(Humanoid user, ItemData item)
	{
		return CheckStationInput((Player)(object)((user is Player) ? user : null), item);
	}

	[HarmonyPatch(typeof(CookingStation), "OnAddFuelSwitch")]
	[HarmonyPrefix]
	private static bool PrefixCookingStationAddFuel(CookingStation __instance, Humanoid user)
	{
		Player val = (Player)(object)((user is Player) ? user : null);
		if (val == null)
		{
			return true;
		}
		if (!Object.op_Implicit((Object)(object)Player.m_localPlayer) || (Object)(object)Player.m_localPlayer != (Object)(object)val)
		{
			return true;
		}
		if ((Object)(object)__instance.m_fuelItem == (Object)null)
		{
			return true;
		}
		if (!IsForbidden(__instance.m_fuelItem.m_itemData.m_shared.m_name))
		{
			return true;
		}
		Message(__instance.m_fuelItem.m_itemData.m_shared.m_name, delayed: true);
		return false;
	}

	[HarmonyPatch(typeof(Fermenter), "AddItem")]
	[HarmonyPrefix]
	private static bool PrefixFermenterAddItem(Humanoid user, ItemData item)
	{
		return CheckStationInput((Player)(object)((user is Player) ? user : null), item);
	}

	[HarmonyPatch(typeof(Smelter), "OnAddOre")]
	[HarmonyPrefix]
	private static bool PrefixSmelterAddOre(Humanoid user, ItemData item)
	{
		if (item == null)
		{
			return true;
		}
		return CheckStationInput((Player)(object)((user is Player) ? user : null), item);
	}

	[HarmonyPatch(typeof(Smelter), "FindCookableItem")]
	[HarmonyPostfix]
	private static void PostfixFindCookableItem(Smelter __instance, Inventory inventory, ref ItemData __result)
	{
		if (__result == null || !IsForbidden(__result.m_shared.m_name))
		{
			return;
		}
		string name = __result.m_shared.m_name;
		foreach (ItemData allItem in inventory.GetAllItems())
		{
			if (allItem != __result && !(allItem.m_shared.m_name == __result.m_shared.m_name) && __instance.IsItemAllowed(((Object)allItem.m_dropPrefab).name) && !IsForbidden(allItem.m_shared.m_name))
			{
				__result = allItem;
				return;
			}
		}
		__result = null;
		Message(name, delayed: true);
	}

	[HarmonyPatch(typeof(Smelter), "OnAddFuel")]
	[HarmonyPrefix]
	private static bool PrefixSmelterAddFuel(Smelter __instance, Humanoid user)
	{
		Player val = (Player)(object)((user is Player) ? user : null);
		if (val == null)
		{
			return true;
		}
		if (!Object.op_Implicit((Object)(object)Player.m_localPlayer) || (Object)(object)Player.m_localPlayer != (Object)(object)val)
		{
			return true;
		}
		if ((Object)(object)__instance.m_fuelItem == (Object)null)
		{
			return true;
		}
		if (!IsForbidden(__instance.m_fuelItem.m_itemData.m_shared.m_name))
		{
			return true;
		}
		Message(__instance.m_fuelItem.m_itemData.m_shared.m_name, delayed: true);
		return false;
	}

	private static bool CheckStationInput(Player player, ItemData item)
	{
		if ((Object)(object)player == (Object)null || !Object.op_Implicit((Object)(object)Player.m_localPlayer) || (Object)(object)Player.m_localPlayer != (Object)(object)player)
		{
			return true;
		}
		if (item == null)
		{
			return true;
		}
		if (IsForbidden(item.m_shared.m_name))
		{
			Message(item.m_shared.m_name);
			return false;
		}
		return true;
	}

	private static void ResetForbiddenItems()
	{
		_forbiddenItemsCache = new HashSet<string>();
		_allowedItemsCache = new HashSet<string>();
		if (string.IsNullOrEmpty(_forbiddenItemsConfig.Value))
		{
			_forbiddenItemsSet = new HashSet<string>();
		}
		else
		{
			_forbiddenItemsSet = (from s in _forbiddenItemsConfig.Value.Split(new char[1] { ',' })
				select s.Trim() into s
				where !string.IsNullOrEmpty(s)
				select s).ToHashSet<string>(StringComparer.Ordinal);
		}
		Logger.LogDebug((object)$"Forbidden items: {_forbiddenItemsSet.Count}");
	}

	private static bool IsForbidden(string itemName)
	{
		if (_forbiddenItemsCache.Contains(itemName))
		{
			return true;
		}
		if (_allowedItemsCache.Contains(itemName))
		{
			return false;
		}
		bool num = ResolveItemForbidden(itemName, new HashSet<string>());
		if (num)
		{
			_forbiddenItemsCache.Add(itemName);
			return num;
		}
		_allowedItemsCache.Add(itemName);
		return num;
	}

	private static bool ResolveItemForbidden(string itemName, HashSet<string> visited)
	{
		if (!visited.Add(itemName))
		{
			return false;
		}
		if (_forbiddenItemsSet.Contains(itemName))
		{
			return true;
		}
		CraftingDataCache craftingDataCache = CraftingDataCache.Get();
		foreach (string recipeIngredient in craftingDataCache.GetRecipeIngredients(itemName))
		{
			if (ResolveItemForbidden(recipeIngredient, visited))
			{
				return true;
			}
		}
		foreach (string conversionSource in craftingDataCache.GetConversionSources(itemName))
		{
			if (ResolveItemForbidden(conversionSource, visited))
			{
				return true;
			}
		}
		return false;
	}

	private static void Message(string itemName, bool delayed = false)
	{
		Player localPlayer = Player.m_localPlayer;
		if (!((Object)(object)localPlayer == (Object)null))
		{
			string message = GetMessage(itemName);
			if (delayed)
			{
				((MonoBehaviour)_instance).StartCoroutine(DelayedMessage(message));
			}
			else
			{
				((Character)localPlayer).Message((MessageType)2, message, 0, (Sprite)null);
			}
		}
	}

	[IteratorStateMachine(typeof(<DelayedMessage>d__33))]
	private static IEnumerator DelayedMessage(string message)
	{
		//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
		return new <DelayedMessage>d__33(0)
		{
			message = message
		};
	}

	private static string GetMessage(string itemName)
	{
		string text = MessageKeys[Random.Range(0, MessageKeys.Length)];
		return Localization.TryTranslate("$" + text).Replace("{item_name}", itemName);
	}
}
internal class CraftingDataCache
{
	private static CraftingDataCache _instance;

	private readonly Dictionary<string, List<string>> _recipeIngredients;

	private readonly Dictionary<string, List<string>> _conversionSources;

	private readonly Dictionary<string, List<string>> _pieceRequirements;

	private CraftingDataCache()
	{
		_recipeIngredients = BuildRecipeCache();
		_conversionSources = BuildConversionCache();
		_pieceRequirements = BuildPieceCache();
	}

	public static void Build()
	{
		_instance = new CraftingDataCache();
	}

	public static CraftingDataCache Get()
	{
		if (_instance == null)
		{
			_instance = new CraftingDataCache();
		}
		return _instance;
	}

	public static void Invalidate()
	{
		_instance = null;
	}

	public IEnumerable<string> GetRecipeIngredients(string outputItemName)
	{
		if (_recipeIngredients.TryGetValue(outputItemName, out var value))
		{
			return value;
		}
		return Enumerable.Empty<string>();
	}

	public IEnumerable<string> GetConversionSources(string outputItemName)
	{
		if (_conversionSources.TryGetValue(outputItemName, out var value))
		{
			return value;
		}
		return Enumerable.Empty<string>();
	}

	public IEnumerable<string> GetPieceRequirements(string pieceName)
	{
		if (_pieceRequirements.TryGetValue(pieceName, out var value))
		{
			return value;
		}
		return Enumerable.Empty<string>();
	}

	private static Dictionary<string, List<string>> BuildRecipeCache()
	{
		Logger.LogDebug((object)"Creating RecipeCache");
		Dictionary<string, List<string>> dictionary = new Dictionary<string, List<string>>(StringComparer.Ordinal);
		if ((Object)(object)ObjectDB.instance == (Object)null)
		{
			return dictionary;
		}
		foreach (Recipe recipe in ObjectDB.instance.m_recipes)
		{
			if ((Object)(object)recipe.m_item == (Object)null)
			{
				continue;
			}
			string name = recipe.m_item.m_itemData.m_shared.m_name;
			Requirement[] resources = recipe.m_resources;
			foreach (Requirement val in resources)
			{
				if (!((Object)(object)val.m_resItem == (Object)null))
				{
					string name2 = val.m_resItem.m_itemData.m_shared.m_name;
					AddEntry(dictionary, name, name2);
				}
			}
		}
		return dictionary;
	}

	private static Dictionary<string, List<string>> BuildConversionCache()
	{
		Logger.LogDebug((object)"Creating ConversionDataCache");
		Dictionary<string, List<string>> dictionary = new Dictionary<string, List<string>>(StringComparer.Ordinal);
		if ((Object)(object)ZNetScene.instance == (Object)null)
		{
			return dictionary;
		}
		CookingStation val = default(CookingStation);
		Fermenter val2 = default(Fermenter);
		Smelter val3 = default(Smelter);
		foreach (GameObject value in ZNetScene.instance.m_namedPrefabs.Values)
		{
			if (!Object.op_Implicit((Object)(object)value))
			{
				continue;
			}
			if (value.TryGetComponent<CookingStation>(ref val))
			{
				foreach (ItemConversion item in val.m_conversion)
				{
					if (!((Object)(object)item.m_from == (Object)null) && !((Object)(object)item.m_to == (Object)null))
					{
						AddEntry(dictionary, item.m_to.m_itemData.m_shared.m_name, item.m_from.m_itemData.m_shared.m_name);
					}
				}
			}
			if (value.TryGetComponent<Fermenter>(ref val2))
			{
				foreach (ItemConversion item2 in val2.m_conversion)
				{
					if (!((Object)(object)item2.m_from == (Object)null) && !((Object)(object)item2.m_to == (Object)null))
					{
						AddEntry(dictionary, item2.m_to.m_itemData.m_shared.m_name, item2.m_from.m_itemData.m_shared.m_name);
					}
				}
			}
			if (!value.TryGetComponent<Smelter>(ref val3))
			{
				continue;
			}
			foreach (ItemConversion item3 in val3.m_conversion)
			{
				if (!((Object)(object)item3.m_from == (Object)null) && !((Object)(object)item3.m_to == (Object)null))
				{
					AddEntry(dictionary, item3.m_to.m_itemData.m_shared.m_name, item3.m_from.m_itemData.m_shared.m_name);
				}
			}
		}
		return dictionary;
	}

	private static Dictionary<string, List<string>> BuildPieceCache()
	{
		Logger.LogDebug((object)"Creating PieceCache");
		Dictionary<string, List<string>> dictionary = new Dictionary<string, List<string>>(StringComparer.Ordinal);
		if ((Object)(object)ZNetScene.instance == (Object)null)
		{
			return dictionary;
		}
		Piece val = default(Piece);
		foreach (GameObject value in ZNetScene.instance.m_namedPrefabs.Values)
		{
			if (!Object.op_Implicit((Object)(object)value) || !value.TryGetComponent<Piece>(ref val) || val.m_resources == null || val.m_resources.Length == 0)
			{
				continue;
			}
			Requirement[] resources = val.m_resources;
			foreach (Requirement val2 in resources)
			{
				if (!((Object)(object)val2.m_resItem == (Object)null))
				{
					AddEntry(dictionary, val.m_name, val2.m_resItem.m_itemData.m_shared.m_name);
				}
			}
		}
		return dictionary;
	}

	private static void AddEntry(Dictionary<string, List<string>> dict, string key, string value)
	{
		Logger.LogDebug((object)("Adding " + key + ": " + value));
		if (!dict.TryGetValue(key, out var value2))
		{
			value2 = (dict[key] = new List<string>());
		}
		value2.Add(value);
	}
}