Decompiled source of DeepNorthUtilityItems v1.0.0

DeepNorthUtilityItems.dll

Decompiled 4 days ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Jotunn.Configs;
using Jotunn.Entities;
using Jotunn.Managers;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyVersion("0.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 DeepNorthUtilityItems
{
	[HarmonyPatch(typeof(Trader), "Start")]
	internal static class HaldorStockPatch
	{
		private static void Postfix(Trader __instance)
		{
			if (!((Object)(object)__instance == (Object)null) && string.Equals(__instance.m_name, "Haldor", StringComparison.Ordinal))
			{
				AddItem(__instance, "IceShoes", ModConfig.IceSpikesHaldorEnabled.Value, ModConfig.IceSpikesHaldorPrice.Value, ModConfig.IceSpikesHaldorStack.Value, ModConfig.IceSpikesHaldorGlobalKey.Value);
				AddItem(__instance, "IceSkates", ModConfig.IceSkatesHaldorEnabled.Value, ModConfig.IceSkatesHaldorPrice.Value, ModConfig.IceSkatesHaldorStack.Value, ModConfig.IceSkatesHaldorGlobalKey.Value);
			}
		}

		private static void AddItem(Trader trader, string prefabName, bool enabled, int price, int stack, string requiredGlobalKey)
		{
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e5: Expected O, but got Unknown
			if (!enabled || (Object)(object)ObjectDB.instance == (Object)null)
			{
				return;
			}
			foreach (TradeItem item in trader.m_items)
			{
				if ((Object)(object)item?.m_prefab != (Object)null && string.Equals(((Object)item.m_prefab).name, prefabName, StringComparison.Ordinal))
				{
					return;
				}
			}
			GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(prefabName);
			ItemDrop val = (((Object)(object)itemPrefab == (Object)null) ? null : itemPrefab.GetComponent<ItemDrop>());
			if (!((Object)(object)val == (Object)null))
			{
				trader.m_items.Add(new TradeItem
				{
					m_prefab = val,
					m_price = Math.Max(1, price),
					m_stack = Math.Max(1, stack),
					m_requiredGlobalKey = (string.IsNullOrWhiteSpace(requiredGlobalKey) ? string.Empty : requiredGlobalKey.Trim())
				});
			}
		}
	}
	[HarmonyPatch(typeof(ObjectDB), "Awake")]
	internal static class ItemIconOverrides
	{
		private sealed class IconDefinition
		{
			internal readonly string PrefabName;

			internal readonly string AssetName;

			internal IconDefinition(string prefabName, string assetName)
			{
				PrefabName = prefabName;
				AssetName = assetName;
			}
		}

		private static readonly IconDefinition[] Definitions = new IconDefinition[2]
		{
			new IconDefinition("IceShoes", "Ice Spikes.png"),
			new IconDefinition("IceSkates", "Ice Skates.png")
		};

		private static readonly Dictionary<string, Sprite> LoadedIcons = new Dictionary<string, Sprite>(StringComparer.OrdinalIgnoreCase);

		private static readonly List<Texture2D> CreatedTextures = new List<Texture2D>();

		private static readonly List<Sprite> CreatedSprites = new List<Sprite>();

		[HarmonyPostfix]
		private static void Postfix(ObjectDB __instance)
		{
			Apply(__instance);
		}

		private static void Apply(ObjectDB objectDb)
		{
			if ((Object)(object)objectDb == (Object)null || objectDb.m_items == null)
			{
				return;
			}
			foreach (GameObject prefab in objectDb.m_items)
			{
				if ((Object)(object)prefab == (Object)null)
				{
					continue;
				}
				IconDefinition iconDefinition = Array.Find(Definitions, (IconDefinition entry) => string.Equals(entry.PrefabName, ((Object)prefab).name, StringComparison.OrdinalIgnoreCase));
				if (iconDefinition == null)
				{
					continue;
				}
				SharedData val = prefab.GetComponent<ItemDrop>()?.m_itemData?.m_shared;
				if (val == null || val.m_icons == null || val.m_icons.Length == 0)
				{
					Plugin.Log.LogWarning((object)("Could not apply custom icon because " + iconDefinition.PrefabName + " has no item icon slot."));
					continue;
				}
				Sprite val2 = LoadIcon(iconDefinition, val.m_icons[0]);
				if (!((Object)(object)val2 == (Object)null))
				{
					Sprite[] array = (Sprite[])(object)new Sprite[val.m_icons.Length];
					for (int num = 0; num < array.Length; num++)
					{
						array[num] = val2;
					}
					val.m_icons = array;
					Plugin.Log.LogInfo((object)("Applied custom icon to " + iconDefinition.PrefabName + "."));
				}
			}
		}

		private static Sprite LoadIcon(IconDefinition definition, Sprite source)
		{
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Expected O, but got Unknown
			//IL_011f: Unknown result type (might be due to invalid IL or missing references)
			//IL_012e: Unknown result type (might be due to invalid IL or missing references)
			if (LoadedIcons.TryGetValue(definition.PrefabName, out var value))
			{
				return value;
			}
			string name = "DeepNorthUtilityItems.Assets." + definition.AssetName;
			try
			{
				using Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(name);
				if (stream == null)
				{
					Plugin.Log.LogWarning((object)("Embedded icon asset is missing: " + definition.AssetName + "."));
					return null;
				}
				byte[] array = new byte[(int)stream.Length];
				stream.Read(array, 0, array.Length);
				Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false, false);
				if (!LoadPng(val, array))
				{
					Object.Destroy((Object)(object)val);
					Plugin.Log.LogWarning((object)("Could not decode icon asset: " + definition.AssetName + "."));
					return null;
				}
				((Object)val).name = "DeepNorthUtilityItems_" + Path.GetFileNameWithoutExtension(definition.AssetName) + "Texture";
				((Texture)val).filterMode = (FilterMode)1;
				((Texture)val).wrapMode = (TextureWrapMode)1;
				float num = (((Object)(object)source != (Object)null) ? source.pixelsPerUnit : 100f);
				Sprite val2 = Sprite.Create(val, new Rect(0f, 0f, (float)((Texture)val).width, (float)((Texture)val).height), new Vector2(0.5f, 0.5f), num, 0u, (SpriteMeshType)0);
				((Object)val2).name = "DeepNorthUtilityItems_" + Path.GetFileNameWithoutExtension(definition.AssetName);
				LoadedIcons[definition.PrefabName] = val2;
				CreatedTextures.Add(val);
				CreatedSprites.Add(val2);
				return val2;
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Could not load icon for " + definition.PrefabName + ": " + ex.Message));
				return null;
			}
		}

		private static bool LoadPng(Texture2D texture, byte[] imageData)
		{
			Assembly assembly = Array.Find(AppDomain.CurrentDomain.GetAssemblies(), (Assembly loaded) => loaded.GetName().Name == "UnityEngine.ImageConversionModule");
			if (assembly == null)
			{
				try
				{
					assembly = Assembly.Load("UnityEngine.ImageConversionModule");
				}
				catch
				{
					return false;
				}
			}
			MethodInfo methodInfo = assembly.GetType("UnityEngine.ImageConversion")?.GetMethod("LoadImage", BindingFlags.Static | BindingFlags.Public, null, new Type[3]
			{
				typeof(Texture2D),
				typeof(byte[]),
				typeof(bool)
			}, null);
			if (methodInfo != null)
			{
				return (bool)methodInfo.Invoke(null, new object[3] { texture, imageData, true });
			}
			return false;
		}
	}
	internal static class ItemIntegration
	{
		private static bool _recipesRegistered;

		internal static void Register(ManualLogSource log)
		{
			ApplyItemType("IceShoes", log);
			ApplyItemType("IceSkates", log);
			if (!_recipesRegistered)
			{
				if (ModConfig.IceSpikesCraftEnabled.Value)
				{
					RegisterConfiguredRecipe("IceShoes", "Recipe_DeepNorthUtility_IceSpikes", ModConfig.IceSpikesStation.Value, ModConfig.IceSpikesStationLevel.Value, ModConfig.IceSpikesIngredients.Value, log);
				}
				if (ModConfig.IceSkatesCraftEnabled.Value)
				{
					RegisterConfiguredRecipe("IceSkates", "Recipe_DeepNorthUtility_IceSkates", ModConfig.IceSkatesStation.Value, ModConfig.IceSkatesStationLevel.Value, ModConfig.IceSkatesIngredients.Value, log);
				}
				_recipesRegistered = true;
			}
		}

		private static void RegisterConfiguredRecipe(string item, string recipeName, string station, int stationLevel, string ingredients, ManualLogSource log)
		{
			if (!TryParseRequirements(ingredients, out var requirements, out var error))
			{
				log.LogWarning((object)("Skipped " + recipeName + " for " + item + ": invalid Ingredients value. " + error));
			}
			else
			{
				AddRecipe(item, recipeName, station, stationLevel, requirements, log);
			}
		}

		private static bool TryParseRequirements(string ingredients, out RequirementConfig[] requirements, out string error)
		{
			//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d6: Expected O, but got Unknown
			requirements = Array.Empty<RequirementConfig>();
			error = null;
			if (string.IsNullOrWhiteSpace(ingredients))
			{
				error = "Enter at least one pair in the form PrefabID:amount.";
				return false;
			}
			List<RequirementConfig> list = new List<RequirementConfig>();
			string[] array = ingredients.Split(new char[1] { ',' });
			for (int i = 0; i < array.Length; i++)
			{
				string text = array[i].Trim();
				int num = text.LastIndexOf(':');
				if (num <= 0 || num == text.Length - 1)
				{
					error = "'" + text + "' is not in the form PrefabID:amount.";
					return false;
				}
				string text2 = text.Substring(0, num).Trim();
				if (!int.TryParse(text.Substring(num + 1).Trim(), out var result) || result <= 0)
				{
					error = "'" + text + "' must use a positive whole-number amount.";
					return false;
				}
				if (string.IsNullOrWhiteSpace(text2))
				{
					error = "Ingredient prefab IDs cannot be empty.";
					return false;
				}
				list.Add(new RequirementConfig(text2, result, 0, true));
			}
			requirements = list.ToArray();
			return requirements.Length != 0;
		}

		private static void AddRecipe(string item, string recipeName, string station, int stationLevel, RequirementConfig[] requirements, ManualLogSource log)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: 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_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Expected O, but got Unknown
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Expected O, but got Unknown
			RecipeConfig val = new RecipeConfig
			{
				Name = recipeName,
				Item = item,
				Amount = 1,
				CraftingStation = (string.IsNullOrWhiteSpace(station) ? null : station.Trim()),
				MinStationLevel = Math.Max(1, stationLevel),
				Requirements = requirements
			};
			ItemManager.Instance.AddRecipe(new CustomRecipe(val));
			log.LogInfo((object)("Registered " + recipeName + " for " + item + "."));
		}

		private static void ApplyItemType(string prefabName, ManualLogSource log)
		{
			//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
			GameObject prefab = PrefabManager.Instance.GetPrefab(prefabName);
			if ((Object)(object)prefab == (Object)null)
			{
				log.LogWarning((object)("Vanilla item " + prefabName + " was not found; it was not converted to a Utility item."));
				return;
			}
			ItemDrop component = prefab.GetComponent<ItemDrop>();
			if ((Object)(object)component == (Object)null || component.m_itemData == null || component.m_itemData.m_shared == null)
			{
				log.LogWarning((object)("Vanilla item " + prefabName + " has no usable ItemDrop data."));
				return;
			}
			if (prefabName == "IceShoes")
			{
				component.m_itemData.m_shared.m_name = "$item_iceshoes";
				component.m_itemData.m_shared.m_description = "$item_iceshoes_description";
			}
			else if (prefabName == "IceSkates")
			{
				component.m_itemData.m_shared.m_name = "$item_iceskates";
				component.m_itemData.m_shared.m_description = "$item_iceskates_description";
			}
			component.m_itemData.m_shared.m_itemType = (ItemType)18;
			component.m_itemData.m_shared.m_armor = 0f;
			component.m_itemData.m_shared.m_armorPerLevel = 0f;
			component.m_itemData.m_shared.m_useDurability = false;
			component.m_itemData.m_shared.m_canBeReparied = false;
			component.m_itemData.m_shared.m_maxDurability = 0f;
			component.m_itemData.m_shared.m_durabilityPerLevel = 0f;
			component.m_itemData.m_shared.m_maxQuality = 1;
		}
	}
	internal static class ModConfig
	{
		internal static ConfigEntry<bool> IceSpikesCraftEnabled;

		internal static ConfigEntry<bool> IceSpikesHaldorEnabled;

		internal static ConfigEntry<int> IceSpikesHaldorPrice;

		internal static ConfigEntry<int> IceSpikesHaldorStack;

		internal static ConfigEntry<string> IceSpikesHaldorGlobalKey;

		internal static ConfigEntry<string> IceSpikesStation;

		internal static ConfigEntry<int> IceSpikesStationLevel;

		internal static ConfigEntry<string> IceSpikesIngredients;

		internal static ConfigEntry<bool> IceSkatesCraftEnabled;

		internal static ConfigEntry<bool> IceSkatesHaldorEnabled;

		internal static ConfigEntry<int> IceSkatesHaldorPrice;

		internal static ConfigEntry<int> IceSkatesHaldorStack;

		internal static ConfigEntry<string> IceSkatesHaldorGlobalKey;

		internal static ConfigEntry<string> IceSkatesStation;

		internal static ConfigEntry<int> IceSkatesStationLevel;

		internal static ConfigEntry<string> IceSkatesIngredients;

		internal static void Bind(ConfigFile config)
		{
			IceSpikesCraftEnabled = config.Bind<bool>("Ice Spikes", "Craftable", true, "Add a crafting recipe for the vanilla IceShoes item.");
			IceSpikesHaldorEnabled = config.Bind<bool>("Ice Spikes", "Sold by Haldor", false, "Add IceShoes to vanilla Haldor's stock.");
			IceSpikesHaldorPrice = config.Bind<int>("Ice Spikes", "Haldor price", 1000, "Coin price when Ice Spikes are sold by Haldor.");
			IceSpikesHaldorStack = config.Bind<int>("Ice Spikes", "Haldor amount", 1, "Amount sold per purchase.");
			IceSpikesHaldorGlobalKey = config.Bind<string>("Ice Spikes", "Haldor progression key", "defeated_fader", "Global key required before Haldor offers Ice Spikes. Empty disables the progression gate.");
			IceSpikesStation = config.Bind<string>("Ice Spikes", "Crafting station", "piece_workbench", "Station prefab ID; use an empty value for hand crafting.");
			IceSpikesStationLevel = config.Bind<int>("Ice Spikes", "Crafting station level", 5, "Required station level.");
			IceSpikesIngredients = config.Bind<string>("Ice Spikes recipe", "Ingredients", "MoleClaws:6,Frostwood:6,Leatherstraps:2", "Complete recipe ingredient list as comma-separated prefab ID:amount pairs, for example MoleClaws:6,Frostwood:6,Leatherstraps:2.");
			IceSkatesCraftEnabled = config.Bind<bool>("Ice Skates", "Craftable", false, "Add a crafting recipe for the vanilla IceSkates item.");
			IceSkatesHaldorEnabled = config.Bind<bool>("Ice Skates", "Sold by Haldor", true, "Add IceSkates to vanilla Haldor's stock.");
			IceSkatesHaldorPrice = config.Bind<int>("Ice Skates", "Haldor price", 1000, "Coin price when IceSkates are sold by Haldor.");
			IceSkatesHaldorStack = config.Bind<int>("Ice Skates", "Haldor amount", 1, "Amount sold per purchase.");
			IceSkatesHaldorGlobalKey = config.Bind<string>("Ice Skates", "Haldor progression key", "defeated_fader", "Global key required before Haldor offers IceSkates. Empty disables the progression gate. Use a Valheim global key, including keys added by other mods.");
			IceSkatesStation = config.Bind<string>("Ice Skates", "Crafting station", "piece_workbench", "Station prefab ID; use an empty value for hand crafting.");
			IceSkatesStationLevel = config.Bind<int>("Ice Skates", "Crafting station level", 5, "Required station level.");
			IceSkatesIngredients = config.Bind<string>("Ice Skates recipe", "Ingredients", "SealHide:6,NornThread:4,Leatherstraps:2", "Complete recipe ingredient list as comma-separated prefab ID:amount pairs, for example SealHide:6,NornThread:4,Leatherstraps:2.");
		}
	}
	[BepInPlugin("mythica.deepnorthutilityitems", "Deep North Utility Items", "1.0.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public sealed class Plugin : BaseUnityPlugin
	{
		public const string PluginGuid = "mythica.deepnorthutilityitems";

		public const string PluginName = "Deep North Utility Items";

		public const string PluginVersion = "1.0.0";

		private Harmony _harmony;

		internal static ManualLogSource Log { get; private set; }

		private void Awake()
		{
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			ModConfig.Bind(((BaseUnityPlugin)this).Config);
			CustomLocalization localization = LocalizationManager.Instance.GetLocalization();
			string text = "item_iceshoes";
			localization.AddTranslation(ref text, "Ice Spikes");
			text = "item_iceshoes_description";
			localization.AddTranslation(ref text, "The spikes grip icy surfaces, stopping you from slipping on ice.");
			text = "item_iceskates";
			localization.AddTranslation(ref text, "Ice Skates");
			text = "item_iceskates_description";
			localization.AddTranslation(ref text, "The blades slide on icy surfaces, increasing your speed and ability to turn on ice.");
			_harmony = new Harmony("mythica.deepnorthutilityitems");
			_harmony.PatchAll();
			PrefabManager.OnVanillaPrefabsAvailable += RegisterRecipes;
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Deep North Utility Items 1.0.0 loaded.");
		}

		private void RegisterRecipes()
		{
			ItemIntegration.Register(((BaseUnityPlugin)this).Logger);
		}

		private void OnDestroy()
		{
			PrefabManager.OnVanillaPrefabsAvailable -= RegisterRecipes;
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}
	}
	[HarmonyPatch(typeof(VisEquipment), "AttachArmor", new Type[]
	{
		typeof(int),
		typeof(int),
		typeof(int)
	})]
	internal static class UtilityVisualSuppressor
	{
		private const string IceSpikesPrefab = "IceShoes";

		private const string IceSkatesPrefab = "IceSkates";

		private static readonly List<GameObject> NoVisuals = new List<GameObject>();

		[HarmonyPrefix]
		private static bool Prefix(int itemHash, ref List<GameObject> __result)
		{
			ObjectDB instance = ObjectDB.instance;
			if ((Object)(object)instance == (Object)null)
			{
				return true;
			}
			GameObject itemPrefab = instance.GetItemPrefab(itemHash);
			if ((Object)(object)itemPrefab == (Object)null || (((Object)itemPrefab).name != "IceShoes" && ((Object)itemPrefab).name != "IceSkates"))
			{
				return true;
			}
			__result = NoVisuals;
			return false;
		}
	}
}