Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of UtilityBelt v1.0.0
plugins/UtilityBelt.dll
Decompiled 18 hours agousing 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}"); } } } }