Decompiled source of UtilityBelt v1.0.0

plugins/UtilityBelt.dll

Decompiled 18 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Jotunn.Configs;
using Jotunn.Entities;
using Jotunn.Managers;
using Jotunn.Utils;
using Microsoft.CodeAnalysis;
using UnityEngine;
using ValheimMods.Shared;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: IgnoresAccessChecksTo("assembly_valheim")]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("UtilityBelt")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("A Haldor-bought utility belt that grows with you: upgrade it with a swamp key, wishbone, wolf cape, wisplight and feather cape to wear them all at once. Server-synced costs and tiers. Needs Jotunn.")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("UtilityBelt")]
[assembly: AssemblyTitle("UtilityBelt")]
[assembly: InternalsVisibleTo("ValheimMods.Tests")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
[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 UtilityBelt
{
	internal static class Belt
	{
		public const string PrefabName = "UtilityBelt";

		private const string Megingjord = "BeltStrength";

		private const string SharedName = "$item_utilitybelt";

		public const float CarryWeight = 150f;

		private static readonly Color s_tint = new Color(0.55f, 0.78f, 1f);

		private const float IconHueShift = 0.5f;

		private static StatusEffect s_baseEffect;

		private static StatusEffect s_extraCarryEffect;

		private static Recipe s_recipe;

		private static string s_ownedText;

		private static int s_ownedQuality;

		private static HashSet<Tier> s_owned;

		public static ItemDrop Prefab { get; private set; }

		public static void Register()
		{
			CustomLocalization localization = LocalizationManager.Instance.GetLocalization();
			string text = "English";
			localization.AddTranslation(ref text, new Dictionary<string, string>
			{
				["item_utilitybelt"] = "Utility Belt",
				["item_utilitybelt_description"] = "A sturdy belt with a pouch for everything. Upgrade it at a workbench to carry the gear you'd otherwise have to swap between.",
				["se_utilitybelt"] = "Utility Belt"
			});
			PrefabManager.OnVanillaPrefabsAvailable += Create;
		}

		private static void Create()
		{
			//IL_0020: 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_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Expected O, but got Unknown
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Expected O, but got Unknown
			//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ca: Expected O, but got Unknown
			//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ef: Expected O, but got Unknown
			//IL_0139: Unknown result type (might be due to invalid IL or missing references)
			//IL_013f: Invalid comparison between Unknown and I4
			PrefabManager.OnVanillaPrefabsAvailable -= Create;
			try
			{
				ItemConfig val = new ItemConfig
				{
					Name = "$item_utilitybelt",
					Description = "$item_utilitybelt_description",
					CraftingStation = CraftingStations.Workbench,
					MinStationLevel = 1
				};
				val.AddRequirement(new RequirementConfig("Coins", 0, 1, false));
				foreach (string item in from i in Tiers.All.Select(Tiers.UpgradeItem)
					where i != null
					select i)
				{
					val.AddRequirement(new RequirementConfig(item, 0, 1, false));
				}
				CustomItem val2 = new CustomItem("UtilityBelt", "BeltStrength", val);
				SharedData shared = val2.ItemDrop.m_itemData.m_shared;
				shared.m_maxQuality = Tiers.MaxQuality;
				if (val2.Recipe == null)
				{
					throw new InvalidOperationException("Jotunn made no upgrade recipe for the belt");
				}
				s_recipe = val2.Recipe.Recipe;
				s_recipe.m_noCraftOnlyUpgrade = true;
				if ((int)SystemInfo.graphicsDeviceType != 4)
				{
					Tint(val2.ItemPrefab, shared);
				}
				s_baseEffect = (StatusEffect)(object)CarryEffect(shared.m_equipStatusEffect, "SE_UtilityBelt", shared.m_icons.FirstOrDefault());
				s_extraCarryEffect = (StatusEffect)(object)CarryEffect(shared.m_equipStatusEffect, "SE_UtilityBeltReinforced", null);
				shared.m_equipStatusEffect = s_baseEffect;
				ItemManager.Instance.AddItem(val2);
				Prefab = val2.ItemDrop;
			}
			catch (Exception arg)
			{
				ModInfo.Log.LogError((object)$"Couldn't create the utility belt: {arg}");
				PlayerNotice.Show(ModInfo.Name + ": couldn't create the belt item. Details are in BepInEx/LogOutput.log.");
			}
		}

		private static SE_Stats CarryEffect(StatusEffect megingjord, string name, Sprite icon)
		{
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Expected O, but got Unknown
			SE_Stats val = (SE_Stats)(object)((megingjord is SE_Stats) ? megingjord : null);
			SE_Stats val2 = ((val != null) ? Object.Instantiate<SE_Stats>(val) : ScriptableObject.CreateInstance<SE_Stats>());
			((Object)val2).name = name;
			((StatusEffect)val2).m_name = "$se_utilitybelt";
			((StatusEffect)val2).m_tooltip = "";
			((StatusEffect)val2).m_icon = icon;
			val2.m_addMaxCarryWeight = 150f;
			ItemManager.Instance.AddStatusEffect(new CustomStatusEffect((StatusEffect)(object)val2, false));
			return val2;
		}

		private static void Tint(GameObject prefab, SharedData shared)
		{
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Expected O, but got Unknown
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			Dictionary<Material, Material> dictionary = new Dictionary<Material, Material>();
			Renderer[] componentsInChildren = prefab.GetComponentsInChildren<Renderer>(true);
			foreach (Renderer val in componentsInChildren)
			{
				Material[] sharedMaterials = val.sharedMaterials;
				for (int j = 0; j < sharedMaterials.Length; j++)
				{
					Material val2 = sharedMaterials[j];
					if (Object.op_Implicit((Object)(object)val2) && val2.HasProperty("_Color"))
					{
						if (!dictionary.TryGetValue(val2, out var value))
						{
							value = new Material(val2)
							{
								name = ((Object)val2).name + "_UtilityBelt"
							};
							value.color = val2.color * s_tint;
							dictionary[val2] = value;
						}
						sharedMaterials[j] = value;
					}
				}
				val.sharedMaterials = sharedMaterials;
			}
			shared.m_icons = shared.m_icons.Select(TintSprite).ToArray();
		}

		private static Sprite TintSprite(Sprite sprite)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Expected O, but got Unknown
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0101: Unknown result type (might be due to invalid IL or missing references)
			//IL_0110: Unknown result type (might be due to invalid IL or missing references)
			if (!Object.op_Implicit((Object)(object)sprite))
			{
				return sprite;
			}
			Rect textureRect = sprite.textureRect;
			Texture2D texture = sprite.texture;
			RenderTexture temporary = RenderTexture.GetTemporary(((Texture)texture).width, ((Texture)texture).height, 0, (RenderTextureFormat)0, (RenderTextureReadWrite)2);
			RenderTexture active = RenderTexture.active;
			try
			{
				Graphics.Blit((Texture)(object)texture, temporary);
				RenderTexture.active = temporary;
				Texture2D val = new Texture2D((int)((Rect)(ref textureRect)).width, (int)((Rect)(ref textureRect)).height, (TextureFormat)4, false)
				{
					name = "UtilityBeltIcon"
				};
				val.ReadPixels(textureRect, 0, 0);
				Color[] pixels = val.GetPixels();
				float num = default(float);
				float num2 = default(float);
				float num3 = default(float);
				for (int i = 0; i < pixels.Length; i++)
				{
					Color.RGBToHSV(pixels[i], ref num, ref num2, ref num3);
					Color val2 = Color.HSVToRGB((num + 0.5f) % 1f, num2, num3);
					val2.a = pixels[i].a;
					pixels[i] = val2;
				}
				val.SetPixels(pixels);
				val.Apply();
				return Sprite.Create(val, new Rect(0f, 0f, ((Rect)(ref textureRect)).width, ((Rect)(ref textureRect)).height), new Vector2(0.5f, 0.5f), sprite.pixelsPerUnit);
			}
			finally
			{
				RenderTexture.active = active;
				RenderTexture.ReleaseTemporary(temporary);
			}
		}

		public static bool IsBelt(ItemData item)
		{
			if (item != null)
			{
				return item.m_shared.m_name == "$item_utilitybelt";
			}
			return false;
		}

		public static bool IsBeltRecipe(Recipe recipe)
		{
			if (Object.op_Implicit((Object)(object)recipe))
			{
				return (Object)(object)recipe == (Object)(object)s_recipe;
			}
			return false;
		}

		public static bool IsBeltRequirement(Requirement requirement)
		{
			if (Object.op_Implicit((Object)(object)s_recipe))
			{
				return Array.IndexOf(s_recipe.m_resources, requirement) >= 0;
			}
			return false;
		}

		public static HashSet<Tier> Owned(ItemData item)
		{
			item.m_customData.TryGetValue("s992.UtilityBelt.upgrades", out var value);
			if (s_owned == null || value != s_ownedText || item.m_quality != s_ownedQuality)
			{
				s_owned = Tiers.Owned(item.m_customData, item.m_quality);
				s_ownedText = value;
				s_ownedQuality = item.m_quality;
			}
			return s_owned;
		}

		public static bool HasActive(Humanoid character, Tier tier)
		{
			ItemData item = (Object.op_Implicit((Object)(object)character) ? character.m_utilityItem : null);
			if (IsBelt(item))
			{
				return Settings.Rules.IsActive(Owned(item), tier);
			}
			return false;
		}

		public static StatusEffect EffectFor(Tier tier)
		{
			return (StatusEffect)(tier switch
			{
				Tier.Wishbone => EquipEffectOf("Wishbone"), 
				Tier.Wisplight => EquipEffectOf("Demister"), 
				Tier.FeatherFall => EquipEffectOf("CapeFeather"), 
				Tier.ExtraCarry => s_extraCarryEffect, 
				_ => null, 
			});
		}

		public static SharedData SharedOf(string prefabName)
		{
			GameObject val = (Object.op_Implicit((Object)(object)ObjectDB.instance) ? ObjectDB.instance.GetItemPrefab(prefabName) : null);
			ItemDrop val2 = (Object.op_Implicit((Object)(object)val) ? val.GetComponent<ItemDrop>() : null);
			if (!Object.op_Implicit((Object)(object)val2))
			{
				return null;
			}
			return val2.m_itemData.m_shared;
		}

		private static StatusEffect EquipEffectOf(string prefabName)
		{
			return SharedOf(prefabName)?.m_equipStatusEffect;
		}
	}
	internal static class UpgradeContext
	{
		public static ItemData Item;

		public static ItemData Resolve(int targetQuality)
		{
			if (Belt.IsBelt(Item) && Item.m_quality == targetQuality - 1)
			{
				return Item;
			}
			Player localPlayer = Player.m_localPlayer;
			if (!Object.op_Implicit((Object)(object)localPlayer))
			{
				return null;
			}
			return ((IEnumerable<ItemData>)((Humanoid)localPlayer).GetInventory().GetAllItems()).FirstOrDefault((Func<ItemData, bool>)((ItemData item) => Belt.IsBelt(item) && item.m_quality == targetQuality - 1));
		}

		public static Tier? NextTier(ItemData belt)
		{
			if (belt != null)
			{
				return Settings.Rules.Next(Belt.Owned(belt));
			}
			return null;
		}
	}
	[HarmonyPatch(typeof(Requirement), "GetAmount")]
	internal static class RequirementAmount
	{
		private static bool Prefix(Requirement __instance, int qualityLevel, ref int __result)
		{
			if (qualityLevel <= 1 || !Belt.IsBeltRequirement(__instance))
			{
				return true;
			}
			Tier? tier = UpgradeContext.NextTier(UpgradeContext.Resolve(qualityLevel));
			__result = ((tier.HasValue && Object.op_Implicit((Object)(object)__instance.m_resItem)) ? Settings.Rules.Amount(tier.Value, ((Object)((Component)__instance.m_resItem).gameObject).name) : 0);
			return false;
		}
	}
	[HarmonyPatch(typeof(Player), "HaveRequirements", new Type[]
	{
		typeof(Recipe),
		typeof(bool),
		typeof(int),
		typeof(int)
	})]
	internal static class UpgradeAllowed
	{
		private static bool Prefix(Recipe recipe, bool discover, int qualityLevel, ref bool __result)
		{
			if (discover || !Belt.IsBeltRecipe(recipe))
			{
				return true;
			}
			if (qualityLevel > 1 && UpgradeContext.NextTier(UpgradeContext.Resolve(qualityLevel)).HasValue)
			{
				return true;
			}
			__result = false;
			return false;
		}
	}
	[HarmonyPatch(typeof(Recipe), "GetRequiredStationLevel")]
	internal static class StationLevel
	{
		private static bool Prefix(Recipe __instance, ref int __result)
		{
			if (!Belt.IsBeltRecipe(__instance))
			{
				return true;
			}
			__result = Math.Max(1, __instance.m_minStationLevel);
			return false;
		}
	}
	[HarmonyPatch(typeof(InventoryGui), "AddRecipeToList")]
	internal static class UpgradeList
	{
		private static bool Prefix(Player player, Recipe recipe, ItemData item, ref bool canCraft)
		{
			if (item == null || !Belt.IsBeltRecipe(recipe))
			{
				return true;
			}
			if (!UpgradeContext.NextTier(item).HasValue)
			{
				return false;
			}
			UpgradeContext.Item = item;
			try
			{
				canCraft = player.HaveRequirements(recipe, false, item.m_quality + 1, 1) || ZoneSystem.instance.GetGlobalKey((GlobalKeys)25);
			}
			finally
			{
				UpgradeContext.Item = null;
			}
			return true;
		}

		private static Exception Finalizer(Exception __exception)
		{
			return PatchGuard.Contain(__exception, "the belt's upgrade list");
		}
	}
	[HarmonyPatch(typeof(InventoryGui), "UpdateRecipe")]
	internal static class UpgradePanel
	{
		private static void Prefix(InventoryGui __instance)
		{
			UpgradeContext.Item = ((RecipeDataPair)(ref __instance.m_selectedRecipe)).ItemData;
		}

		private static Exception Finalizer(Exception __exception)
		{
			UpgradeContext.Item = null;
			return PatchGuard.Contain(__exception, "the belt's upgrade panel");
		}
	}
	[HarmonyPatch(typeof(InventoryGui), "DoCrafting")]
	internal static class Upgrade
	{
		internal sealed class Pending
		{
			public ItemData Old;

			public Vector2i Position;

			public int Quality;

			public Dictionary<string, string> Data;

			public bool IsUpgraded(ItemData item)
			{
				if (Belt.IsBelt(item) && item.m_quality == Quality)
				{
					return item != Old;
				}
				return false;
			}

			public bool HasOtherRecord(ItemData item)
			{
				if (item.m_customData.TryGetValue("s992.UtilityBelt.upgrades", out var value))
				{
					if (Old.m_customData.TryGetValue("s992.UtilityBelt.upgrades", out var value2))
					{
						return !(value == value2);
					}
					return true;
				}
				return false;
			}
		}

		private static bool Prefix(InventoryGui __instance, out Pending __state)
		{
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			__state = null;
			ItemData craftUpgradeItem = __instance.m_craftUpgradeItem;
			if (craftUpgradeItem == null || !Belt.IsBeltRecipe(__instance.m_craftRecipe))
			{
				return true;
			}
			UpgradeContext.Item = craftUpgradeItem;
			HashSet<Tier> hashSet = new HashSet<Tier>(Belt.Owned(craftUpgradeItem));
			Tier? tier = Settings.Rules.Next(hashSet);
			if (!tier.HasValue)
			{
				return false;
			}
			hashSet.Add(tier.Value);
			__state = new Pending
			{
				Old = craftUpgradeItem,
				Position = craftUpgradeItem.m_gridPos,
				Quality = craftUpgradeItem.m_quality + 1,
				Data = new Dictionary<string, string>(craftUpgradeItem.m_customData) { ["s992.UtilityBelt.upgrades"] = Tiers.Format(hashSet) }
			};
			return true;
		}

		private static void Postfix(Player player, Pending __state)
		{
			if (__state == null)
			{
				return;
			}
			Inventory inventory = ((Humanoid)player).GetInventory();
			ItemData val = inventory.GetItemAt(__state.Position.x, __state.Position.y);
			if (!__state.IsUpgraded(val))
			{
				List<ItemData> source = inventory.GetAllItems().Where(__state.IsUpgraded).ToList();
				val = ((IEnumerable<ItemData>)source).FirstOrDefault((Func<ItemData, bool>)((ItemData item) => !__state.HasOtherRecord(item))) ?? source.FirstOrDefault();
			}
			if (val != null)
			{
				val.m_customData = __state.Data;
				if (val.m_equipped)
				{
					((Humanoid)player).UpdateEquipmentStatusEffects();
				}
			}
		}

		private static Exception Finalizer(Exception __exception)
		{
			UpgradeContext.Item = null;
			return __exception;
		}
	}
	[HarmonyPatch(typeof(Humanoid), "UpdateEquipmentStatusEffects")]
	internal static class EquipmentEffects
	{
		private static readonly ConditionalWeakTable<Humanoid, HashSet<int>> s_added = new ConditionalWeakTable<Humanoid, HashSet<int>>();

		private static void Postfix(Humanoid __instance)
		{
			HashSet<int> orCreateValue = s_added.GetOrCreateValue(__instance);
			List<StatusEffect> list = new List<StatusEffect>();
			ItemData utilityItem = __instance.m_utilityItem;
			if (Belt.IsBelt(utilityItem))
			{
				HashSet<Tier> owned = Belt.Owned(utilityItem);
				Tier[] all = Tiers.All;
				foreach (Tier tier in all)
				{
					if (Settings.Rules.IsActive(owned, tier) && Settings.Wanted(tier))
					{
						StatusEffect val = Belt.EffectFor(tier);
						if (Object.op_Implicit((Object)(object)val))
						{
							list.Add(val);
						}
					}
				}
			}
			HashSet<int> wantedHashes = new HashSet<int>(list.Select((StatusEffect e) => e.NameHash()));
			foreach (int hash in orCreateValue.Where((int h) => !wantedHashes.Contains(h)).ToList())
			{
				orCreateValue.Remove(hash);
				if (!__instance.m_equipmentStatusEffects.Any((StatusEffect e) => e.NameHash() == hash))
				{
					((Character)__instance).m_seman.RemoveStatusEffect(hash, false);
				}
			}
			foreach (StatusEffect item in list)
			{
				orCreateValue.Add(item.NameHash());
				if (!((Character)__instance).m_seman.HaveStatusEffect(item.NameHash()))
				{
					((Character)__instance).m_seman.AddStatusEffect(item, false, 0, 0f, (short)(-1));
				}
			}
		}

		private static Exception Finalizer(Exception __exception)
		{
			return PatchGuard.Contain(__exception, "the belt's effects");
		}
	}
	[HarmonyPatch(typeof(Player), "ApplyArmorDamageMods")]
	internal static class FrostResistance
	{
		private static void Postfix(Player __instance, ref DamageModifiers mods)
		{
			if (Belt.HasActive((Humanoid)(object)__instance, Tier.FrostResist))
			{
				SharedData val = Belt.SharedOf(Tiers.UpgradeItem(Tier.FrostResist));
				if (val != null)
				{
					((DamageModifiers)(ref mods)).Apply(val.m_damageModifiers);
				}
			}
		}

		private static Exception Finalizer(Exception __exception)
		{
			return PatchGuard.Contain(__exception, "the belt's frost resistance");
		}
	}
	[HarmonyPatch(typeof(Door), "HaveKey")]
	internal static class SwampKey
	{
		private static void Postfix(Door __instance, Humanoid player, ref bool __result)
		{
			if (!__result && Object.op_Implicit((Object)(object)__instance.m_keyItem) && !(((Object)((Component)__instance.m_keyItem).gameObject).name != Tiers.UpgradeItem(Tier.SwampKey)) && Belt.HasActive(player, Tier.SwampKey))
			{
				__result = true;
			}
		}
	}
	[HarmonyPatch(typeof(Trader), "GetAvailableItems")]
	internal static class HaldorStock
	{
		private const string Megingjord = "BeltStrength";

		private static TradeItem s_belt;

		private static void Postfix(List<TradeItem> __result)
		{
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: Expected O, but got Unknown
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			int num = __result.FindIndex((TradeItem t) => Object.op_Implicit((Object)(object)t.m_prefab) && ((Object)t.m_prefab).name == "BeltStrength");
			if (num >= 0 && Object.op_Implicit((Object)(object)Belt.Prefab))
			{
				TradeItem val = __result[num];
				if (s_belt == null)
				{
					s_belt = new TradeItem
					{
						m_prefab = Belt.Prefab,
						m_stack = 1,
						m_name = "",
						m_tooltip = "",
						m_buyPlayerEffects = (EffectList)(((object)val.m_buyPlayerEffects) ?? ((object)new EffectList())),
						m_levelUpEffect = val.m_levelUpEffect
					};
				}
				s_belt.m_price = Settings.Price.Value;
				if (!__result.Contains(s_belt))
				{
					__result.Insert(num + 1, s_belt);
				}
			}
		}

		private static Exception Finalizer(Exception __exception)
		{
			return PatchGuard.Contain(__exception, "Haldor's belt listing");
		}
	}
	[HarmonyPatch(typeof(ItemData), "GetTooltip", new Type[]
	{
		typeof(ItemData),
		typeof(int),
		typeof(bool),
		typeof(float),
		typeof(int),
		typeof(bool)
	})]
	internal static class Tooltip
	{
		private static void Postfix(ItemData item, ref string __result)
		{
			if (Belt.IsBelt(item))
			{
				ItemData item2 = ((Object.op_Implicit((Object)(object)Belt.Prefab) && item == Belt.Prefab.m_itemData && Belt.IsBelt(UpgradeContext.Item)) ? UpgradeContext.Item : item);
				__result += Settings.Rules.Describe(Belt.Owned(item2), Settings.Wanted);
			}
		}

		private static Exception Finalizer(Exception __exception)
		{
			return PatchGuard.Contain(__exception, "the belt's tooltip");
		}
	}
	[BepInPlugin("s992.UtilityBelt", "UtilityBelt", "1.0.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[NetworkCompatibility(/*Could not decode attribute arguments.*/)]
	public class Plugin : BaseUnityPlugin
	{
		private void Awake()
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Expected O, but got Unknown
			Settings.Bind(((BaseUnityPlugin)this).Config);
			if (SafePatching.PatchAll(new Harmony("s992.UtilityBelt"), ((BaseUnityPlugin)this).Logger))
			{
				Belt.Register();
				Settings.Changed += RefreshEffects;
				SynchronizationManager.OnConfigurationSynchronized += delegate
				{
					Settings.Reload();
				};
				((Component)this).gameObject.AddComponent<Toggles>();
				((BaseUnityPlugin)this).Logger.LogInfo((object)(ModInfo.Name + " " + ModInfo.Version + " loaded"));
			}
		}

		private static void RefreshEffects()
		{
			if (Object.op_Implicit((Object)(object)Player.m_localPlayer))
			{
				((Humanoid)Player.m_localPlayer).UpdateEquipmentStatusEffects();
			}
		}
	}
	internal static class Settings
	{
		public static ConfigEntry<int> Price;

		private static readonly Dictionary<Tier, ConfigEntry<bool>> s_enabled = new Dictionary<Tier, ConfigEntry<bool>>();

		private static readonly Dictionary<Tier, ConfigEntry<int>> s_gold = new Dictionary<Tier, ConfigEntry<int>>();

		public static ConfigEntry<bool> Wishbone;

		public static ConfigEntry<bool> Wisplight;

		public static ConfigEntry<bool> FeatherFall;

		public static ConfigEntry<KeyboardShortcut> WishboneKey;

		public static ConfigEntry<KeyboardShortcut> WisplightKey;

		public static ConfigEntry<KeyboardShortcut> FeatherFallKey;

		private static TierRules s_rules;

		public static TierRules Rules => s_rules ?? (s_rules = new TierRules(Tiers.All.Where((Tier t) => s_enabled[t].Value), Tiers.All.ToDictionary((Tier t) => t, (Tier t) => s_gold[t].Value)));

		public static event Action Changed;

		public static void Bind(ConfigFile config)
		{
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Expected O, but got Unknown
			//IL_0179: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00db: Expected O, but got Unknown
			//IL_012d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0137: Expected O, but got Unknown
			int num = 100;
			Price = config.Bind<int>("1. Server", "Haldor price", 950, new ConfigDescription("Gold coins Haldor asks for a new belt. He sells it once he sells Megingjord. Set by the server in multiplayer.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 100000), new object[1] { Admin(num--) }));
			Tier[] all = Tiers.All;
			foreach (Tier tier in all)
			{
				string text = $"Tier {(int)tier} {Tiers.DisplayName(tier).ToLowerInvariant()}";
				string text2 = Tiers.UpgradeItemName(tier);
				string text3 = ((text2 == null) ? "gold only" : ("a " + text2 + " plus this gold"));
				s_enabled[tier] = config.Bind<bool>("1. Server", text, true, new ConfigDescription("Whether the belt has this upgrade (" + Tiers.Effect(tier) + "). When off, belts skip it when upgrading, and belts that already have it lose its effect until it's back on. Set by the server in multiplayer.", (AcceptableValueBase)null, new object[1] { Admin(num--) }));
				s_gold[tier] = config.Bind<int>("1. Server", text + " gold", TierRules.DefaultGold(tier), new ConfigDescription("Gold coins this upgrade costs at the workbench (" + text3 + "). Set by the server in multiplayer.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 100000), new object[1] { Admin(num--) }));
			}
			Wishbone = config.Bind<bool>("2. Personal", "Wishbone", true, "Use the belt's wishbone upgrade (pings near buried treasure and ores). Just for you.");
			WishboneKey = config.Bind<KeyboardShortcut>("2. Personal", "Wishbone key", new KeyboardShortcut((KeyCode)280, Array.Empty<KeyCode>()), "Turns the belt's wishbone on or off.");
			Wisplight = config.Bind<bool>("2. Personal", "Wisplight", true, "Use the belt's wisplight upgrade (the wisp that clears mist). Just for you.");
			WisplightKey = config.Bind<KeyboardShortcut>("2. Personal", "Wisplight key", KeyboardShortcut.Empty, "Turns the belt's wisplight on or off.");
			FeatherFall = config.Bind<bool>("2. Personal", "Feather fall", true, "Use the belt's feather fall upgrade (slow fall, no fall damage). Just for you.");
			FeatherFallKey = config.Bind<KeyboardShortcut>("2. Personal", "Feather fall key", KeyboardShortcut.Empty, "Turns the belt's feather fall on or off.");
			Price.SettingChanged += OnChanged;
			all = Tiers.All;
			foreach (Tier key in all)
			{
				s_enabled[key].SettingChanged += OnChanged;
				s_gold[key].SettingChanged += OnChanged;
			}
			Wishbone.SettingChanged += OnChanged;
			Wisplight.SettingChanged += OnChanged;
			FeatherFall.SettingChanged += OnChanged;
		}

		public static bool Wanted(Tier tier)
		{
			return tier switch
			{
				Tier.Wishbone => Wishbone.Value, 
				Tier.Wisplight => Wisplight.Value, 
				Tier.FeatherFall => FeatherFall.Value, 
				_ => true, 
			};
		}

		private static ConfigurationManagerAttributes Admin(int order)
		{
			//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_0019: Expected O, but got Unknown
			return new ConfigurationManagerAttributes
			{
				IsAdminOnly = true,
				Order = order
			};
		}

		private static void OnChanged(object sender, EventArgs e)
		{
			Reload();
		}

		public static void Reload()
		{
			s_rules = null;
			Settings.Changed?.Invoke();
		}
	}
	internal enum Tier
	{
		SwampKey = 1,
		Wishbone,
		FrostResist,
		Wisplight,
		FeatherFall,
		ExtraCarry
	}
	internal static class Tiers
	{
		public static readonly Tier[] All = (Tier[])Enum.GetValues(typeof(Tier));

		public static readonly int MaxQuality = 1 + All.Length;

		public const string DataKey = "s992.UtilityBelt.upgrades";

		public const string Coins = "Coins";

		public static string UpgradeItem(Tier tier)
		{
			return tier switch
			{
				Tier.SwampKey => "CryptKey", 
				Tier.Wishbone => "Wishbone", 
				Tier.FrostResist => "CapeWolf", 
				Tier.Wisplight => "Demister", 
				Tier.FeatherFall => "CapeFeather", 
				_ => null, 
			};
		}

		public static string UpgradeItemName(Tier tier)
		{
			return tier switch
			{
				Tier.SwampKey => "swamp key", 
				Tier.Wishbone => "wishbone", 
				Tier.FrostResist => "wolf fur cape", 
				Tier.Wisplight => "wisplight", 
				Tier.FeatherFall => "feather cape", 
				_ => null, 
			};
		}

		public static string DisplayName(Tier tier)
		{
			return tier switch
			{
				Tier.SwampKey => "Swamp key", 
				Tier.Wishbone => "Wishbone", 
				Tier.FrostResist => "Frost resistance", 
				Tier.Wisplight => "Wisplight", 
				Tier.FeatherFall => "Feather fall", 
				Tier.ExtraCarry => "Reinforced", 
				_ => tier.ToString(), 
			};
		}

		public static string Effect(Tier tier)
		{
			return tier switch
			{
				Tier.SwampKey => "opens sunken crypts", 
				Tier.Wishbone => "finds hidden treasure and ores", 
				Tier.FrostResist => "frost resistant, like a wolf fur cape", 
				Tier.Wisplight => "clears the mist around you", 
				Tier.FeatherFall => "slow fall, no fall damage", 
				Tier.ExtraCarry => "+150 more carry weight", 
				_ => "", 
			};
		}

		public static HashSet<Tier> Owned(IDictionary<string, string> customData, int quality)
		{
			if (customData != null && customData.TryGetValue("s992.UtilityBelt.upgrades", out var value))
			{
				return Parse(value);
			}
			return new HashSet<Tier>(All.Take(Math.Max(0, quality - 1)));
		}

		public static HashSet<Tier> Parse(string text)
		{
			HashSet<Tier> hashSet = new HashSet<Tier>();
			string[] array = (text ?? "").Split(',');
			for (int i = 0; i < array.Length; i++)
			{
				if (int.TryParse(array[i].Trim(), NumberStyles.Integer, CultureInfo.InvariantCulture, out var result) && Enum.IsDefined(typeof(Tier), result))
				{
					hashSet.Add((Tier)result);
				}
			}
			return hashSet;
		}

		public static string Format(IEnumerable<Tier> tiers)
		{
			return string.Join(",", (from t in tiers.Distinct()
				orderby t
				select t).Select(delegate(Tier t)
			{
				int num = (int)t;
				return num.ToString(CultureInfo.InvariantCulture);
			}));
		}
	}
	internal sealed class TierRules
	{
		private readonly HashSet<Tier> m_enabled;

		private readonly Dictionary<Tier, int> m_gold;

		public static TierRules Default => new TierRules(Tiers.All, Tiers.All.ToDictionary((Tier t) => t, DefaultGold));

		public TierRules(IEnumerable<Tier> enabled, IDictionary<Tier, int> goldCosts)
		{
			m_enabled = new HashSet<Tier>(enabled);
			m_gold = new Dictionary<Tier, int>(goldCosts);
		}

		public static int DefaultGold(Tier tier)
		{
			if (tier != Tier.ExtraCarry)
			{
				return 0;
			}
			return 1500;
		}

		public bool IsEnabled(Tier tier)
		{
			return m_enabled.Contains(tier);
		}

		public int Gold(Tier tier)
		{
			if (!m_gold.TryGetValue(tier, out var value))
			{
				return 0;
			}
			return Math.Max(0, value);
		}

		public bool IsActive(ICollection<Tier> owned, Tier tier)
		{
			if (owned.Contains(tier))
			{
				return IsEnabled(tier);
			}
			return false;
		}

		public Tier? Next(ICollection<Tier> owned)
		{
			Tier[] all = Tiers.All;
			foreach (Tier tier in all)
			{
				if (IsEnabled(tier) && !owned.Contains(tier))
				{
					return tier;
				}
			}
			return null;
		}

		public int Amount(Tier tier, string prefabName)
		{
			if (prefabName == "Coins")
			{
				return Gold(tier);
			}
			return (prefabName == Tiers.UpgradeItem(tier)) ? 1 : 0;
		}

		public string Cost(Tier tier)
		{
			List<string> list = new List<string>();
			string text = Tiers.UpgradeItemName(tier);
			if (text != null)
			{
				list.Add("1 " + text);
			}
			if (Gold(tier) > 0)
			{
				list.Add(Gold(tier).ToString(CultureInfo.InvariantCulture) + " gold");
			}
			if (list.Count != 0)
			{
				return string.Join(" and ", list);
			}
			return "free";
		}

		public string Describe(ICollection<Tier> owned, Func<Tier, bool> wanted)
		{
			StringBuilder stringBuilder = new StringBuilder("\n\n<color=orange>Upgrades</color>");
			foreach (Tier item in Tiers.All.Where(owned.Contains))
			{
				string value = Tiers.DisplayName(item) + ": " + Tiers.Effect(item);
				if (!IsEnabled(item))
				{
					value = "<color=#888888>" + Tiers.DisplayName(item) + ": turned off on this server</color>";
				}
				else if (!wanted(item))
				{
					value = "<color=#888888>" + Tiers.DisplayName(item) + ": off in your settings</color>";
				}
				stringBuilder.Append("\n  ").Append(value);
			}
			if (!Tiers.All.Any(owned.Contains))
			{
				stringBuilder.Append("\n  none yet");
			}
			Tier? tier = Next(owned);
			stringBuilder.Append((!tier.HasValue) ? "\nFully upgraded" : ("\nNext at a workbench: <color=orange>" + Tiers.DisplayName(tier.Value) + "</color> (" + Cost(tier.Value) + ")"));
			return stringBuilder.ToString();
		}
	}
	internal sealed class Toggles : MonoBehaviour
	{
		private FrameGuard m_guard;

		private void Awake()
		{
			m_guard = new FrameGuard((Behaviour)(object)this, "the belt's hotkeys");
		}

		private void Update()
		{
			try
			{
				if (Object.op_Implicit((Object)(object)Player.m_localPlayer) && !Hotkeys.IsTyping())
				{
					Toggle(Settings.WishboneKey, Settings.Wishbone, Tier.Wishbone);
					Toggle(Settings.WisplightKey, Settings.Wisplight, Tier.Wisplight);
					Toggle(Settings.FeatherFallKey, Settings.FeatherFall, Tier.FeatherFall);
				}
			}
			catch (Exception e)
			{
				m_guard.Report(e);
			}
		}

		private static void Toggle(ConfigEntry<KeyboardShortcut> key, ConfigEntry<bool> setting, Tier tier)
		{
			if (Hotkeys.Pressed(key))
			{
				setting.Value = !setting.Value;
				((Character)Player.m_localPlayer).Message((MessageType)1, "Utility belt: " + Tiers.DisplayName(tier).ToLowerInvariant() + " " + (setting.Value ? "on" : "off"), 0, (Sprite)null, false);
			}
		}
	}
	public static class MyPluginInfo
	{
		public const string PLUGIN_GUID = "s992.UtilityBelt";

		public const string PLUGIN_NAME = "UtilityBelt";

		public const string PLUGIN_VERSION = "1.0.0";
	}
}
namespace System.Runtime.CompilerServices
{
	[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
	internal sealed class IgnoresAccessChecksToAttribute : Attribute
	{
		public IgnoresAccessChecksToAttribute(string assemblyName)
		{
		}
	}
}
namespace ValheimMods.Shared
{
	internal sealed class FrameGuard
	{
		private const int MaxErrors = 10;

		private readonly Behaviour m_owner;

		private readonly string m_feature;

		private int m_errors;

		public FrameGuard(Behaviour owner, string feature)
		{
			m_owner = owner;
			m_feature = feature;
		}

		public void Report(Exception e)
		{
			m_errors++;
			if (m_errors == 1)
			{
				ModInfo.Log.LogError((object)$"Error in {m_feature}: {e}");
			}
			if (m_errors >= 10)
			{
				m_owner.enabled = false;
				string text = ModInfo.Name + ": " + m_feature + " kept failing and is off until the game restarts. Details are in BepInEx/LogOutput.log.";
				ModInfo.Log.LogError((object)text);
				PlayerNotice.Show(text);
			}
		}
	}
	internal static class Hotkeys
	{
		private static readonly KeyCode[] s_modifierKeys;

		public static bool Pressed(ConfigEntry<KeyboardShortcut> entry)
		{
			//IL_0007: 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_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			KeyboardShortcut value = entry.Value;
			if ((int)((KeyboardShortcut)(ref value)).MainKey == 0 || !ZInput.GetKeyDown(((KeyboardShortcut)(ref value)).MainKey, false))
			{
				return false;
			}
			List<KeyCode> modifiers = ((KeyboardShortcut)(ref value)).Modifiers.ToList();
			if (modifiers.Any((KeyCode key) => !ZInput.GetKey(key, false)))
			{
				return false;
			}
			return !s_modifierKeys.Any((KeyCode key) => !modifiers.Contains(key) && ZInput.GetKey(key, false));
		}

		public static bool Held(ConfigEntry<KeyboardShortcut> entry)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			KeyboardShortcut value = entry.Value;
			if ((int)((KeyboardShortcut)(ref value)).MainKey != 0 && ZInput.GetKey(((KeyboardShortcut)(ref value)).MainKey, false))
			{
				return ((KeyboardShortcut)(ref value)).Modifiers.All((KeyCode key) => ZInput.GetKey(key, false));
			}
			return false;
		}

		public static bool IsTyping()
		{
			if (!Console.IsVisible() && !TextInput.IsVisible() && !Menu.IsVisible() && !Minimap.InTextInput())
			{
				if (Object.op_Implicit((Object)(object)Chat.instance))
				{
					return Chat.instance.HasFocus();
				}
				return false;
			}
			return true;
		}

		static Hotkeys()
		{
			KeyCode[] array = new KeyCode[6];
			RuntimeHelpers.InitializeArray(array, (RuntimeFieldHandle)/*OpCode not supported: LdMemberToken*/);
			s_modifierKeys = (KeyCode[])(object)array;
		}
	}
	internal static class ModInfo
	{
		public static readonly string Name = typeof(ModInfo).Assembly.GetName().Name;

		public static readonly string Version = typeof(ModInfo).Assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>()?.InformationalVersion ?? typeof(ModInfo).Assembly.GetName().Version.ToString(3);

		private static ManualLogSource s_log;

		public static ManualLogSource Log => s_log ?? (s_log = Logger.CreateLogSource(Name));
	}
	internal static class PatchGuard
	{
		private static readonly HashSet<string> s_reported = new HashSet<string>();

		public static Exception Contain(Exception exception, string feature)
		{
			if (exception == null || !FromThisMod(exception))
			{
				return exception;
			}
			if (s_reported.Add(feature))
			{
				ModInfo.Log.LogError((object)$"Error in {feature} (further errors there aren't logged): {exception}");
				PlayerNotice.Show(ModInfo.Name + ": " + feature + " hit an error and may not work until the game restarts. Details are in BepInEx/LogOutput.log.");
			}
			return null;
		}

		private static bool FromThisMod(Exception exception)
		{
			Assembly assembly = typeof(PatchGuard).Assembly;
			for (Exception ex = exception; ex != null; ex = ex.InnerException)
			{
				StackFrame[] array = new StackTrace(ex, fNeedFileInfo: false).GetFrames() ?? Array.Empty<StackFrame>();
				for (int i = 0; i < array.Length; i++)
				{
					if (array[i].GetMethod()?.DeclaringType?.Assembly == assembly)
					{
						return true;
					}
				}
			}
			return false;
		}
	}
	internal sealed class PlayerNotice : MonoBehaviour
	{
		private static PlayerNotice s_instance;

		private readonly List<string> m_pending = new List<string>();

		public static void Show(string message)
		{
			//IL_001b: 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_0026: Expected O, but got Unknown
			if (!Object.op_Implicit((Object)(object)s_instance))
			{
				GameObject val = new GameObject(ModInfo.Name + ".PlayerNotice");
				Object.DontDestroyOnLoad((Object)val);
				s_instance = val.AddComponent<PlayerNotice>();
			}
			s_instance.m_pending.Add(message);
		}

		private void Update()
		{
			if (m_pending.Count == 0 || !Object.op_Implicit((Object)(object)Player.m_localPlayer) || !Object.op_Implicit((Object)(object)MessageHud.instance))
			{
				return;
			}
			try
			{
				foreach (string item in m_pending)
				{
					Send(item);
				}
			}
			catch (Exception ex)
			{
				ModInfo.Log.LogWarning((object)("Couldn't show a message in game: " + ex.Message));
			}
			m_pending.Clear();
		}

		private static void Send(string message)
		{
			((Character)Player.m_localPlayer).Message((MessageType)1, message, 0, (Sprite)null, false);
			if (Object.op_Implicit((Object)(object)Console.instance))
			{
				Console.instance.Print(message);
			}
		}
	}
	internal static class SafePatching
	{
		public static bool PatchAll(Harmony harmony, ManualLogSource log)
		{
			List<string> list = new List<string>();
			Type[] typesFromAssembly = AccessTools.GetTypesFromAssembly(typeof(SafePatching).Assembly);
			foreach (Type type in typesFromAssembly)
			{
				try
				{
					harmony.CreateClassProcessor(type).Patch();
				}
				catch (Exception arg)
				{
					list.Add(type.Name);
					log.LogError((object)$"Patch {type.Name} failed: {arg}");
				}
			}
			if (list.Count == 0)
			{
				return true;
			}
			harmony.UnpatchSelf();
			string text = ModInfo.Name + " is turned off: this game version changed something it relies on (" + string.Join(", ", list) + "). Check for an update to " + ModInfo.Name + ".";
			log.LogError((object)text);
			PlayerNotice.Show(text);
			return false;
		}

		public static void ExpectReplaced(int replaced, int expected, string what)
		{
			if (replaced != expected)
			{
				throw new InvalidOperationException($"{what}: expected {expected} change(s), made {replaced}");
			}
		}
	}
}