Decompiled source of ConfigurableHaldorSellList v1.2.1

plugins\MG-HaldorSellPatch.dll

Decompiled a day ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
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: AssemblyCompany("MG-HaldorSellPatch")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("MG-HaldorSellPatch")]
[assembly: AssemblyTitle("MG-HaldorSellPatch")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.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 MG.HaldorSellPatch
{
	[BepInPlugin("mg.haldorsellpatch", "MG Haldor Sell Patch", "1.2.1")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public sealed class HaldorSellPatchPlugin : BaseUnityPlugin
	{
		private readonly struct ItemConfig
		{
			public string PrefabName { get; }

			public int SellPrice { get; }

			public int BuyQuantity { get; }

			public int BuyPrice { get; }

			public bool BuyOnly { get; }

			public bool HasBuy => BuyQuantity > 0;

			public ItemConfig(string prefabName, int sellPrice, int buyQuantity = 0, int buyPrice = 0, bool buyOnly = false)
			{
				PrefabName = prefabName;
				SellPrice = sellPrice;
				BuyQuantity = buyQuantity;
				BuyPrice = ((buyPrice > 0) ? buyPrice : (sellPrice * buyQuantity * 2));
				BuyOnly = buyOnly;
			}
		}

		private sealed class BuyConfig
		{
			public ConfigEntry<int> Quantity;

			public int FixedQuantity;

			public ConfigEntry<int> Price;
		}

		private readonly struct BuyEntry
		{
			public string PrefabName { get; }

			public int Quantity { get; }

			public int Price { get; }

			public bool Remove { get; }

			public BuyEntry(string prefabName, int quantity, int price)
			{
				PrefabName = prefabName;
				Quantity = quantity;
				Price = price;
				Remove = false;
			}

			private BuyEntry(string prefabName)
			{
				PrefabName = prefabName;
				Quantity = 0;
				Price = 0;
				Remove = true;
			}

			public static BuyEntry Disabled(string prefabName)
			{
				return new BuyEntry(prefabName);
			}
		}

		[HarmonyPatch(typeof(ObjectDB), "Awake")]
		private static class ObjectDBAwakePatch
		{
			private static void Postfix(ObjectDB __instance)
			{
				ApplySellValues(__instance);
			}
		}

		[HarmonyPatch(typeof(StoreGui), "Show")]
		private static class StoreGuiShowPatch
		{
			private static void Prefix(Trader trader)
			{
				ApplyBuyItems(trader);
			}
		}

		public const string ModGuid = "mg.haldorsellpatch";

		public const string ModName = "MG Haldor Sell Patch";

		public const string ModVersion = "1.2.1";

		private const int TrophyItemType = 13;

		private static readonly List<ItemConfig> Items = new List<ItemConfig>();

		private static readonly string[] RemovedOriginalBuyItems = new string[2] { "Ashwood", "CharredWood" };

		private static readonly Dictionary<string, ConfigEntry<int>> SellPrices = new Dictionary<string, ConfigEntry<int>>(StringComparer.OrdinalIgnoreCase);

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

		private static ConfigEntry<bool> EnableExtraBuyItemsConfig;

		private static ConfigEntry<string> CustomBuyItemsConfig;

		private static ConfigEntry<bool> EnableExtraSellItemsConfig;

		private static ConfigEntry<string> CustomSellItemsConfig;

		private static ConfigEntry<bool> EnableTrophySellingConfig;

		private static ConfigEntry<int> DefaultTrophyPriceConfig;

		private static ConfigEntry<string> TrophyPriceOverridesConfig;

		private static ManualLogSource Log;

		private Harmony harmony;

		private void Awake()
		{
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			BuildItemList();
			BindConfig();
			PatchHaldorExpansionSellLists();
			harmony = new Harmony("mg.haldorsellpatch");
			harmony.PatchAll();
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Loaded configurable Haldor buy/sell list.");
		}

		private void OnDestroy()
		{
			Harmony obj = harmony;
			if (obj != null)
			{
				obj.UnpatchSelf();
			}
		}

		private static void BuildItemList()
		{
			Items.Clear();
			Add("Stone", 1, 50);
			Add("Wood", 1, 50);
			Add("Resin", 1, 50);
			Add("Feathers", 1, 50);
			Add("BoneFragments", 1, 50);
			Add("LeatherScraps", 1, 50);
			Add("DeerHide", 1, 50);
			Add("RoundLog", 2, 50);
			Add("Grausten", 2, 50);
			Add("GreydwarfEye", 2, 50);
			Add("Needle", 2, 50);
			Add("Flint", 2, 30);
			Add("Coal", 2, 50);
			Add("TrollHide", 2, 20);
			Add("WolfPelt", 2, 50);
			Add("FreezeGland", 2, 50);
			Add("FineWood", 3, 50);
			Add("Guck", 3, 50);
			Add("LoxPelt", 3, 50);
			Add("ElderBark", 4, 50);
			Add("BlackMarble", 4, 50);
			Add("Copper", 4, 30);
			Add("JuteRed", 4, 50);
			Add("JuteBlue", 4, 50);
			Add("YggdrasilWood", 4, 50);
			Add("Chitin", 4, 50);
			Add("Root", 4, 50);
			Add("WolfFang", 4, 50);
			Add("Carapace", 4, 50);
			Add("BloodClot", 4, 50);
			Add("Obsidian", 5, 50);
			Add("Tin", 5, 30);
			Add("Tar", 5, 50);
			Add("Blackwood", 5, 50);
			Add("Crystal", 5, 50);
			Add("Chain", 5, 50);
			Add("Mandible", 5, 50);
			Add("Bilebag", 5, 50);
			Add("Bronze", 6, 30);
			Add("SoftTissue", 6, 40);
			Add("RoyalJelly", 6, 50);
			Add("Iron", 7, 30);
			Add("Silver", 7, 30);
			Add("RefinedEitr", 7, 30);
			Add("BlackMetal", 8, 30);
			Add("Flametal", 10, 30);
			Add("SurtlingCore", 30, 10);
			Add("BlackCore", 80, 20);
			Add("MoltenCore", 130, 20);
			AddUniqueBuy("BrisingamenRing", 1, 1600);
			AddUniqueBuy("CapeWoundedBeast", 1, 2500);
			AddUniqueBuy("CapeFracturedBreath", 1, 2500);
			AddUniqueBuy("CrossbowSilentHunt", 1, 2500);
			AddUniqueBuy("Bone_crushers", 1, 2500);
			AddUniqueBuy("ArmorDelayedDoomChest", 1, 4000);
			AddUniqueBuy("ArmorPitKingChest", 1, 4000);
		}

		private static void Add(string prefabName, int sellPrice, int buyQuantity = 0)
		{
			Items.Add(new ItemConfig(prefabName, sellPrice, buyQuantity));
		}

		private static void AddUniqueBuy(string prefabName, int quantity, int price)
		{
			Items.Add(new ItemConfig(prefabName, 0, quantity, price, buyOnly: true));
		}

		private void BindConfig()
		{
			EnableExtraBuyItemsConfig = ((BaseUnityPlugin)this).Config.Bind<bool>("1 - Buy - General", "Enable Extra Buy Items", true, "If false, this add-on removes all extra buy items it controls and leaves Haldor's normal vanilla shop alone.");
			CustomBuyItemsConfig = ((BaseUnityPlugin)this).Config.Bind<string>("1 - Buy - General", "Custom Buy Items", "", "Extra items Haldor sells. Format: PrefabName:Quantity:Price. Example: Coal:50:100.");
			foreach (ItemConfig item in Items)
			{
				if (item.HasBuy)
				{
					BuyConfig buyConfig = new BuyConfig
					{
						FixedQuantity = item.BuyQuantity,
						Price = ((BaseUnityPlugin)this).Config.Bind<int>("2 - Buy - Items", item.PrefabName + " - Price", item.BuyPrice, "Coin cost for buying " + item.PrefabName + ". Set to 0 to remove this item from Haldor's shop.")
					};
					if (!item.BuyOnly)
					{
						buyConfig.Quantity = ((BaseUnityPlugin)this).Config.Bind<int>("2 - Buy - Items", item.PrefabName + " - Quantity", item.BuyQuantity, "How many " + item.PrefabName + " Haldor sells at once.");
					}
					BuyItems[item.PrefabName] = buyConfig;
				}
			}
			EnableExtraSellItemsConfig = ((BaseUnityPlugin)this).Config.Bind<bool>("3 - Sell - General", "Enable Extra Sell Items", true, "If false, this add-on disables all extra non-trophy sell items it controls.");
			CustomSellItemsConfig = ((BaseUnityPlugin)this).Config.Bind<string>("3 - Sell - General", "Custom Sell Items", "", "Extra items players can sell to Haldor. Format: PrefabName:Price,PrefabName:Price. Example: Coal:2,Flint:2. Set a price to 0 to disable selling that item.");
			foreach (ItemConfig item2 in Items)
			{
				if (!item2.BuyOnly)
				{
					SellPrices[item2.PrefabName] = ((BaseUnityPlugin)this).Config.Bind<int>("4 - Sell - Items", item2.PrefabName + " - Price", item2.SellPrice, "Coins paid for selling one " + item2.PrefabName + ". Set to 0 to disable selling this item.");
				}
			}
			EnableTrophySellingConfig = ((BaseUnityPlugin)this).Config.Bind<bool>("5 - Sell - Trophies", "Enable Trophy Selling", true, "If false, all trophy sell values are set to 0.");
			DefaultTrophyPriceConfig = ((BaseUnityPlugin)this).Config.Bind<int>("5 - Sell - Trophies", "Default Trophy Price", 10, "Sell price for trophy items not listed in Trophy Price Overrides.");
			TrophyPriceOverridesConfig = ((BaseUnityPlugin)this).Config.Bind<string>("5 - Sell - Trophies", "Trophy Price Overrides", "TrophyDeer:15,TrophyBoar:15,TrophyNeck:25,TrophyEikthyr:70,TrophyGreydwarf:15,TrophySkeleton:15,TrophyGreydwarfShaman:20,TrophyGhost:25,TrophyGreydwarfBrute:20,TrophyBjorn:50,TrophyFrostTroll:50,TrophySkeletonPoison:50,TrophySkeletonHildir:80,TrophyTheElder:100,TrophySerpent:500,TrophyBonemawSerpent:500,TrophyBlob:15,TrophyDraugr:15,TrophyLeech:15,TrophySurtling:15,TrophyDraugrElite:30,TrophyWraith:35,TrophyAbomination:75,TrophyKvastur:80,TrophyBonemass:150,TrophyWolf:15,TrophyHatchling:15,TrophyUlv:20,TrophyFenring:25,TrophyCultist:40,TrophySGolem:70,TrophyCultist_Hildir:80,TrophyDragonQueen:200,TrophyDeathsquito:15,TrophyGoblin:15,TrophyGrowth:15,TrophyBjornUndead:50,TrophyLox:35,TrophyGoblinShaman:30,TrophyGoblinBrute:70,TrophyGoblinBruteBrosShaman:80,TrophyGoblinBruteBrosBrute:80,TrophyGoblinKing:350,TrophySeeker:20,TrophyTick:20,TrophyDvergr:20,TrophyHare:20,TrophyGjall:70,TrophySeekerBrute:80,TrophySeekerQueen:500,TrophyCharredArcher:20,TrophyVolture:20,TrophyAsksvin:20,TrophyCharredMage:25,TrophyCharredMelee:25,TrophyMorgen:100,TrophyFallenValkyrie:120,TrophyFader:1000", "Specific trophy prices. Format: TrophyPrefab:Price,TrophyPrefab:Price. Set a price to 0 to disable that trophy.");
		}

		private static void PatchHaldorExpansionSellLists()
		{
			PatchDictionary("ExtraSellableItems", GetConfiguredSellItems());
			PatchDictionary("TrophyValueOverrides", ParsePriceList(TrophyPriceOverridesConfig.Value));
		}

		private static void PatchDictionary(string fieldName, Dictionary<string, int> values)
		{
			object obj = Type.GetType("HaldorExpansion.HaldorExpansionPlugin, HaldorExpansion")?.GetField(fieldName, BindingFlags.Static | BindingFlags.NonPublic)?.GetValue(null);
			PropertyInfo propertyInfo = obj?.GetType().GetProperty("Item");
			MethodInfo methodInfo = obj?.GetType().GetMethod("Remove", new Type[1] { typeof(string) });
			if (propertyInfo == null)
			{
				ManualLogSource log = Log;
				if (log != null)
				{
					log.LogWarning((object)("Could not find HaldorExpansion " + fieldName + "; direct ObjectDB patch will still run."));
				}
				return;
			}
			foreach (KeyValuePair<string, int> value in values)
			{
				if (value.Value > 0)
				{
					propertyInfo.SetValue(obj, value.Value, new object[1] { value.Key });
				}
				else
				{
					methodInfo?.Invoke(obj, new object[1] { value.Key });
				}
			}
		}

		internal static void ApplySellValues(ObjectDB db)
		{
			if ((Object)(object)db == (Object)null)
			{
				return;
			}
			PatchHaldorExpansionSellLists();
			foreach (KeyValuePair<string, int> configuredSellItem in GetConfiguredSellItems())
			{
				SetItemValue(db, configuredSellItem.Key, configuredSellItem.Value);
			}
			ApplyTrophySellValues(db);
		}

		private static Dictionary<string, int> GetConfiguredSellItems()
		{
			Dictionary<string, int> dictionary = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
			foreach (KeyValuePair<string, ConfigEntry<int>> sellPrice in SellPrices)
			{
				dictionary[sellPrice.Key] = (EnableExtraSellItemsConfig.Value ? Math.Max(0, sellPrice.Value.Value) : 0);
			}
			if (EnableExtraSellItemsConfig.Value)
			{
				foreach (KeyValuePair<string, int> item in ParsePriceList(CustomSellItemsConfig.Value))
				{
					dictionary[item.Key] = item.Value;
				}
			}
			return dictionary;
		}

		private static Dictionary<string, int> ParsePriceList(string value)
		{
			Dictionary<string, int> dictionary = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
			if (string.IsNullOrWhiteSpace(value))
			{
				return dictionary;
			}
			string[] array = value.Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries);
			for (int i = 0; i < array.Length; i++)
			{
				string text = array[i].Trim();
				string[] array2 = text.Split(new char[2] { ':', '=' }, 2);
				if (array2.Length != 2 || string.IsNullOrWhiteSpace(array2[0]) || !int.TryParse(array2[1].Trim(), NumberStyles.Integer, CultureInfo.InvariantCulture, out var result))
				{
					ManualLogSource log = Log;
					if (log != null)
					{
						log.LogWarning((object)("Ignored invalid sell config entry: " + text));
					}
				}
				else
				{
					dictionary[array2[0].Trim()] = Math.Max(0, result);
				}
			}
			return dictionary;
		}

		private static IEnumerable<BuyEntry> GetConfiguredBuyItems()
		{
			foreach (KeyValuePair<string, BuyConfig> buyItem in BuyItems)
			{
				BuyConfig value = buyItem.Value;
				int num = ((value.Quantity != null) ? value.Quantity.Value : value.FixedQuantity);
				if (!EnableExtraBuyItemsConfig.Value || value.Price.Value <= 0 || num <= 0)
				{
					yield return BuyEntry.Disabled(buyItem.Key);
				}
				else
				{
					yield return new BuyEntry(buyItem.Key, num, value.Price.Value);
				}
			}
			if (!EnableExtraBuyItemsConfig.Value)
			{
				yield break;
			}
			foreach (BuyEntry item in ParseBuyList(CustomBuyItemsConfig.Value))
			{
				yield return item;
			}
		}

		private static IEnumerable<BuyEntry> ParseBuyList(string value)
		{
			if (string.IsNullOrWhiteSpace(value))
			{
				yield break;
			}
			string[] array = value.Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries);
			for (int i = 0; i < array.Length; i++)
			{
				string text = array[i].Trim();
				string[] array2 = text.Split(new char[1] { ':' });
				if (array2.Length != 3 || string.IsNullOrWhiteSpace(array2[0]) || !int.TryParse(array2[2].Trim(), NumberStyles.Integer, CultureInfo.InvariantCulture, out var result))
				{
					ManualLogSource log = Log;
					if (log != null)
					{
						log.LogWarning((object)("Ignored invalid buy config entry: " + text));
					}
					continue;
				}
				int result2;
				if (array2[1].Trim().Equals("MaxStack", StringComparison.OrdinalIgnoreCase))
				{
					result2 = 0;
				}
				else if (!int.TryParse(array2[1].Trim(), NumberStyles.Integer, CultureInfo.InvariantCulture, out result2))
				{
					ManualLogSource log2 = Log;
					if (log2 != null)
					{
						log2.LogWarning((object)("Ignored invalid buy quantity: " + text));
					}
					continue;
				}
				yield return new BuyEntry(array2[0].Trim(), result2, Math.Max(0, result));
			}
		}

		private static void ApplyTrophySellValues(ObjectDB db)
		{
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Invalid comparison between Unknown and I4
			Dictionary<string, int> dictionary = ParsePriceList(TrophyPriceOverridesConfig.Value);
			foreach (GameObject item in db.m_items)
			{
				SharedData val = (((Object)(object)item != (Object)null) ? item.GetComponent<ItemDrop>() : null)?.m_itemData?.m_shared;
				if (val != null && (int)val.m_itemType == 13)
				{
					int val2 = 0;
					if (EnableTrophySellingConfig.Value)
					{
						val2 = (dictionary.TryGetValue(((Object)item).name, out var value) ? value : Math.Max(0, DefaultTrophyPriceConfig.Value));
					}
					val.m_value = Math.Max(0, val2);
				}
			}
		}

		private static void SetItemValue(ObjectDB db, string prefabName, int price)
		{
			GameObject itemPrefab = db.GetItemPrefab(prefabName);
			ItemDrop val = (((Object)(object)itemPrefab != (Object)null) ? itemPrefab.GetComponent<ItemDrop>() : null);
			if (val?.m_itemData?.m_shared == null)
			{
				ManualLogSource log = Log;
				if (log != null)
				{
					log.LogWarning((object)("Could not find item prefab for sell value: " + prefabName));
				}
			}
			else
			{
				val.m_itemData.m_shared.m_value = Math.Max(0, price);
			}
		}

		private static void ApplyBuyItems(Trader trader)
		{
			//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e3: Expected O, but got Unknown
			if ((Object)(object)trader == (Object)null || !IsHaldor(trader))
			{
				return;
			}
			if (trader.m_items == null)
			{
				trader.m_items = new List<TradeItem>();
			}
			foreach (BuyEntry configuredBuyItem in GetConfiguredBuyItems())
			{
				RemoveTradeItem(trader, configuredBuyItem.PrefabName);
				if (configuredBuyItem.Remove || configuredBuyItem.Price <= 0)
				{
					continue;
				}
				ItemDrop itemDrop = GetItemDrop(configuredBuyItem.PrefabName);
				if ((Object)(object)itemDrop == (Object)null)
				{
					ManualLogSource log = Log;
					if (log != null)
					{
						log.LogWarning((object)("Could not find item prefab for buy list: " + configuredBuyItem.PrefabName));
					}
					continue;
				}
				int num = ResolveQuantity(itemDrop, configuredBuyItem.Quantity);
				if (num > 0)
				{
					trader.m_items.Add(new TradeItem
					{
						m_prefab = itemDrop,
						m_stack = num,
						m_price = configuredBuyItem.Price,
						m_requiredGlobalKey = string.Empty
					});
				}
			}
			string[] removedOriginalBuyItems = RemovedOriginalBuyItems;
			foreach (string prefabName in removedOriginalBuyItems)
			{
				RemoveTradeItem(trader, prefabName);
			}
		}

		private static bool IsHaldor(Trader trader)
		{
			if ((Object)(object)trader != (Object)null && (Object)(object)((Component)trader).gameObject != (Object)null)
			{
				return ((Object)((Component)trader).gameObject).name.StartsWith("Haldor", StringComparison.Ordinal);
			}
			return false;
		}

		private static ItemDrop GetItemDrop(string prefabName)
		{
			GameObject val = (((Object)(object)ObjectDB.instance != (Object)null) ? ObjectDB.instance.GetItemPrefab(prefabName) : null);
			if ((Object)(object)val != (Object)null)
			{
				return val.GetComponent<ItemDrop>();
			}
			GameObject val2 = (((Object)(object)ZNetScene.instance != (Object)null) ? ZNetScene.instance.GetPrefab(prefabName) : null);
			if (!((Object)(object)val2 != (Object)null))
			{
				return null;
			}
			return val2.GetComponent<ItemDrop>();
		}

		private static int ResolveQuantity(ItemDrop itemDrop, int configuredQuantity)
		{
			if (configuredQuantity > 0)
			{
				return configuredQuantity;
			}
			return Math.Max(1, (itemDrop.m_itemData?.m_shared?.m_maxStackSize).GetValueOrDefault(1));
		}

		private static void RemoveTradeItem(Trader trader, string prefabName)
		{
			trader.m_items.RemoveAll((TradeItem item) => (Object)(object)item?.m_prefab != (Object)null && NormalizePrefabName(((Object)((Component)item.m_prefab).gameObject).name) == prefabName);
		}

		private static string NormalizePrefabName(string name)
		{
			if (!string.IsNullOrWhiteSpace(name))
			{
				return name.Replace("(Clone)", string.Empty).Trim();
			}
			return string.Empty;
		}
	}
}