Decompiled source of BuyDvergrExtractorNeedle v1.1.0

BuyExtractorNeedle.dll

Decompiled 2 weeks ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using ConditionalConfigSync;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: AssemblyTitle("Buy Dvergr Extractor Needle")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Buy Dvergr Extractor Needle")]
[assembly: AssemblyCopyright("Copyright © 2024-2026")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("1bd1a4b7-cfc5-48c2-b443-1c91bd448dba")]
[assembly: AssemblyFileVersion("1.1.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.1.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 BuyExtractorNeedle
{
	public class BuyAndDestroy : MonoBehaviour, Hoverable, Interactable
	{
		internal sealed class PaymentItem
		{
			internal readonly string PrefabName;

			internal readonly string SharedName;

			internal int Amount;

			internal PaymentItem(string prefabName, string sharedName, int amount)
			{
				PrefabName = prefabName;
				SharedName = sharedName;
				Amount = amount;
			}

			internal PaymentItem Clone(int amount)
			{
				return new PaymentItem(PrefabName, SharedName, amount);
			}
		}

		internal sealed class PurchaseData
		{
			internal readonly string BuyerName;

			internal readonly string PurchasedPrefabName;

			internal readonly List<PaymentItem> Payment;

			internal PurchaseData(string buyerName, string purchasedPrefabName, List<PaymentItem> payment)
			{
				BuyerName = buyerName;
				PurchasedPrefabName = purchasedPrefabName;
				Payment = payment ?? new List<PaymentItem>();
			}
		}

		internal const float DvergrSearchRadius = 30f;

		private const string PurchaseRpc = "BEN_BuyAndRemove";

		private static readonly int PurchaseProcessedZdoKey = StringExtensionMethods.GetStableHashCode("shudnal.BuyExtractorNeedle.PurchaseProcessed");

		public string m_text = "";

		public Destructible m_destructible;

		public WearNTear m_wnt;

		public ZNetView m_nview;

		public string m_requirements;

		public string m_description;

		public int m_myIndex = -1;

		public static readonly List<BuyAndDestroy> s_allInstances = new List<BuyAndDestroy>();

		public static readonly List<string> s_randomBuy = new List<string> { "$npc_haldor_random_buy1", "$npc_haldor_random_buy3", "$npc_haldor_random_buy4", "$npc_haldor_random_buy5", "$npc_haldor_random_sell1", "$npc_haldor_random_sell2", "$npc_haldor_random_greet4", "$npc_haldor_random_sell4" };

		public static EffectList s_randomSellFX;

		public void Awake()
		{
			EnsureSellEffects();
			IDestructible component = ((Component)this).GetComponent<IDestructible>();
			m_destructible = (Destructible)(object)((component is Destructible) ? component : null);
			m_wnt = (WearNTear)(object)((component is WearNTear) ? component : null);
			s_allInstances.Add(this);
			m_myIndex = s_allInstances.Count - 1;
			m_requirements = BuyExtractorNeedle.requirements.Value;
			m_nview = (((Object)(object)m_destructible != (Object)null) ? m_destructible.m_nview : (((Object)(object)m_wnt != (Object)null) ? m_wnt.m_nview : null));
			if ((Object)(object)m_nview != (Object)null && m_nview.GetZDO() != null)
			{
				m_nview.Register<ZPackage>("BEN_BuyAndRemove", (Action<long, ZPackage>)RPC_BuyAndRemove);
			}
		}

		public void OnDestroy()
		{
			if (m_myIndex >= 0 && m_myIndex < s_allInstances.Count)
			{
				int index = s_allInstances.Count - 1;
				s_allInstances[m_myIndex] = s_allInstances[index];
				s_allInstances[m_myIndex].m_myIndex = m_myIndex;
				s_allInstances.RemoveAt(index);
				m_myIndex = -1;
			}
		}

		public string GetHoverText()
		{
			if ((Object)(object)m_destructible == (Object)null && (Object)(object)m_wnt == (Object)null)
			{
				return GetHoverName();
			}
			if (string.IsNullOrWhiteSpace(m_description) && !string.IsNullOrWhiteSpace(m_requirements))
			{
				m_description = GetDescription(m_requirements);
			}
			string text = (string.IsNullOrWhiteSpace(m_description) ? "$piece_itemstand_take" : ("$store_buy " + m_description));
			return Localization.instance.Localize(m_text + "\n[<color=yellow><b>$KEY_Use</b></color>] " + text);
		}

		public string GetHoverName()
		{
			return Localization.instance.Localize(m_text);
		}

		public bool Interact(Humanoid user, bool hold, bool alt)
		{
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0170: Unknown result type (might be due to invalid IL or missing references)
			//IL_0175: Unknown result type (might be due to invalid IL or missing references)
			if (hold || alt || ((Object)(object)m_destructible == (Object)null && (Object)(object)m_wnt == (Object)null) || (Object)(object)user == (Object)null)
			{
				return false;
			}
			if ((Object)(object)m_nview == (Object)null || !m_nview.IsValid())
			{
				return false;
			}
			string buyerName = GetBuyerName(user);
			List<PaymentItem> requirements = GetRequirements(m_requirements);
			if (!HasNearbyDvergr(((Component)this).transform.position))
			{
				Remove(Array.Empty<PaymentItem>(), buyerName);
				return true;
			}
			Inventory inventory = user.GetInventory();
			if (requirements.Count > 0 && requirements.Any((PaymentItem item) => inventory.CountItems(item.SharedName, -1, true) < item.Amount))
			{
				PrivateArea.OnObjectDamaged(((Component)this).transform.position, (Character)(object)user, false);
				return true;
			}
			foreach (PaymentItem item in requirements)
			{
				inventory.RemoveItem(item.SharedName, item.Amount, -1, true);
			}
			if (requirements.Count > 0 && (Object)(object)StoreGui.instance != (Object)null)
			{
				StoreGui.instance.m_sellEffects.Create(((Component)this).transform.position, Quaternion.identity, (Transform)null, 1f, -1);
			}
			Remove(requirements, buyerName);
			return true;
		}

		public bool UseItem(Humanoid user, ItemData item)
		{
			return false;
		}

		private void Remove(IEnumerable<PaymentItem> payment, string buyerName)
		{
			if (!((Object)(object)m_nview == (Object)null) && m_nview.IsValid())
			{
				string prefabName = m_nview.GetPrefabName();
				m_nview.InvokeRPC("BEN_BuyAndRemove", new object[1] { CreatePurchasePackage(buyerName, prefabName, payment) });
			}
		}

		private void RPC_BuyAndRemove(long sender, ZPackage package)
		{
			//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)m_nview == (Object)null || !m_nview.IsValid() || !m_nview.IsOwner())
			{
				return;
			}
			PurchaseData purchaseData = ReadPurchasePackage(package);
			List<PaymentItem> payment = purchaseData.Payment;
			ZDO zDO = m_nview.GetZDO();
			if (zDO == null)
			{
				return;
			}
			if (zDO.GetBool(PurchaseProcessedZdoKey, false))
			{
				RefundPayment(payment);
				if ((Object)(object)BuyExtractorNeedle.instance != (Object)null)
				{
					BuyExtractorNeedle.LogWarning("A repeated purchase of '" + purchaseData.PurchasedPrefabName + "' by player '" + purchaseData.BuyerName + "' was rejected and its payment was refunded: " + FormatPayment(payment) + ".");
				}
				return;
			}
			zDO.Set(PurchaseProcessedZdoKey, true);
			DvergrPaidItemStorage dvergrPaidItemStorage = DvergrPaidItemStorage.FindClosest(((Component)this).transform.position, 30f);
			if ((Object)(object)dvergrPaidItemStorage == (Object)null)
			{
				RefundPayment(payment);
				if ((Object)(object)BuyExtractorNeedle.instance != (Object)null)
				{
					BuyExtractorNeedle.LogInfo("Player '" + purchaseData.BuyerName + "' obtained '" + purchaseData.PurchasedPrefabName + "' for free because no Dvergr was available to accept payment.");
				}
				DestroyPurchasedObject();
			}
			else if (!dvergrPaidItemStorage.AcceptPurchase(purchaseData))
			{
				RefundPayment(payment);
				if ((Object)(object)BuyExtractorNeedle.instance != (Object)null)
				{
					BuyExtractorNeedle.LogWarning("Purchase of '" + purchaseData.PurchasedPrefabName + "' by player '" + purchaseData.BuyerName + "' could not be assigned to " + dvergrPaidItemStorage.GetLogDescription() + ". Refunded payment: " + FormatPayment(payment) + ".");
				}
				DestroyPurchasedObject();
			}
			else
			{
				if ((Object)(object)BuyExtractorNeedle.instance != (Object)null)
				{
					BuyExtractorNeedle.LogInfo("Player '" + purchaseData.BuyerName + "' bought '" + purchaseData.PurchasedPrefabName + "' for " + FormatPayment(payment) + ". Primary payment recipient: " + dvergrPaidItemStorage.GetLogDescription() + ".");
				}
				DestroyPurchasedObject();
			}
		}

		private void DestroyPurchasedObject()
		{
			if ((Object)(object)m_destructible != (Object)null)
			{
				m_destructible.Destroy((HitData)null);
			}
			else if ((Object)(object)m_wnt != (Object)null)
			{
				m_wnt.Destroy((HitData)null, false);
			}
		}

		private void RefundPayment(IEnumerable<PaymentItem> payment)
		{
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			foreach (PaymentItem item in payment)
			{
				GameObject val = (((Object)(object)ObjectDB.instance != (Object)null) ? ObjectDB.instance.GetItemPrefab(item.PrefabName) : null);
				ItemDrop val2 = (((Object)(object)val != (Object)null) ? val.GetComponent<ItemDrop>() : null);
				if (!((Object)(object)val2 == (Object)null) && val2.m_itemData != null && val2.m_itemData.m_shared != null)
				{
					int val3 = Math.Max(1, val2.m_itemData.m_shared.m_maxStackSize);
					int num = item.Amount;
					while (num > 0)
					{
						int num2 = Math.Min(val3, num);
						DropItemStack(val, val2, num2, ((Component)this).transform.position + Vector3.up, Quaternion.identity);
						num -= num2;
					}
				}
			}
		}

		internal static ItemDrop DropItemStack(GameObject prefab, ItemDrop itemDrop, int amount, Vector3 position, Quaternion rotation)
		{
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)prefab == (Object)null || (Object)(object)itemDrop == (Object)null || itemDrop.m_itemData == null || amount <= 0)
			{
				return null;
			}
			ItemData val = itemDrop.m_itemData.Clone();
			val.m_dropPrefab = prefab;
			return ItemDrop.DropItem(val, amount, position, rotation);
		}

		internal static void PlaySaleResponse(NpcTalk npcTalk, int lineIndex)
		{
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)npcTalk == (Object)null) && s_randomBuy.Count != 0)
			{
				EnsureSellEffects();
				s_randomSellFX.Create(((Component)npcTalk).transform.position, Quaternion.identity, (Transform)null, 1f, -1);
				int index = Mathf.Clamp(lineIndex, 0, s_randomBuy.Count - 1);
				npcTalk.Say(s_randomBuy[index], "Talk");
			}
		}

		private static void EnsureSellEffects()
		{
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			if (s_randomSellFX == null)
			{
				object obj;
				if (!((Object)(object)ZNetScene.instance != (Object)null))
				{
					obj = null;
				}
				else
				{
					GameObject prefab = ZNetScene.instance.GetPrefab("Haldor");
					obj = ((prefab != null) ? prefab.GetComponent<Trader>() : null);
				}
				Trader val = (Trader)obj;
				s_randomSellFX = (EffectList)(((Object)(object)val != (Object)null) ? ((object)val.m_randomSellFX) : ((object)new EffectList()));
			}
		}

		private static bool HasNearbyDvergr(Vector3 position)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			return (Object)(object)DvergrPaidItemStorage.FindClosest(position, 30f) != (Object)null;
		}

		private static string GetBuyerName(Humanoid user)
		{
			Player val = (Player)(object)((user is Player) ? user : null);
			if ((Object)(object)val != (Object)null)
			{
				string playerName = val.GetPlayerName();
				if (!string.IsNullOrWhiteSpace(playerName))
				{
					return playerName;
				}
			}
			return (!string.IsNullOrWhiteSpace(((Object)user).name)) ? ((Object)user).name : "Unknown player";
		}

		private static string GetDescription(string requirements)
		{
			return string.Join(", ", (from item in GetRequirements(requirements)
				select item.SharedName + " x" + item.Amount).ToArray());
		}

		private static List<PaymentItem> GetRequirements(string requirements)
		{
			List<PaymentItem> list = new List<PaymentItem>();
			Dictionary<string, PaymentItem> dictionary = new Dictionary<string, PaymentItem>(StringComparer.Ordinal);
			if (string.IsNullOrWhiteSpace(requirements) || (Object)(object)ObjectDB.instance == (Object)null)
			{
				return list;
			}
			string[] array = requirements.Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries);
			foreach (string text in array)
			{
				string[] array2 = text.Split(new char[1] { ':' }, StringSplitOptions.RemoveEmptyEntries);
				if (array2.Length != 2 || !int.TryParse(array2[1].Trim(), out var result) || result <= 0)
				{
					continue;
				}
				string text2 = array2[0].Trim();
				GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(text2);
				ItemDrop val = (((Object)(object)itemPrefab != (Object)null) ? itemPrefab.GetComponent<ItemDrop>() : null);
				string text3 = (((Object)(object)val != (Object)null && val.m_itemData != null && val.m_itemData.m_shared != null) ? val.m_itemData.m_shared.m_name : null);
				if (!string.IsNullOrEmpty(text3))
				{
					if (dictionary.TryGetValue(text2, out var value))
					{
						value.Amount = (int)Math.Min(2147483647L, (long)value.Amount + (long)result);
						continue;
					}
					PaymentItem paymentItem = new PaymentItem(text2, text3, result);
					dictionary.Add(text2, paymentItem);
					list.Add(paymentItem);
				}
			}
			return list;
		}

		internal static ZPackage CreatePurchasePackage(string buyerName, string purchasedPrefabName, IEnumerable<PaymentItem> payment)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Expected O, but got Unknown
			ZPackage val = new ZPackage();
			val.Write(buyerName ?? "Unknown player");
			val.Write(purchasedPrefabName ?? "Unknown prefab");
			WritePayment(val, payment);
			return val;
		}

		internal static PurchaseData ReadPurchasePackage(ZPackage package)
		{
			if (package == null)
			{
				return new PurchaseData("Unknown player", "Unknown prefab", new List<PaymentItem>());
			}
			try
			{
				string buyerName = package.ReadString();
				string purchasedPrefabName = package.ReadString();
				return new PurchaseData(buyerName, purchasedPrefabName, ReadPayment(package));
			}
			catch (Exception ex)
			{
				if ((Object)(object)BuyExtractorNeedle.instance != (Object)null)
				{
					BuyExtractorNeedle.LogWarning("Failed to read purchase package: " + ex.Message);
				}
				return new PurchaseData("Unknown player", "Unknown prefab", new List<PaymentItem>());
			}
		}

		internal static string FormatPayment(IEnumerable<PaymentItem> payment)
		{
			if (payment == null)
			{
				return "nothing";
			}
			string[] array = (from item in payment
				where item != null && item.Amount > 0
				select item.PrefabName + " x" + item.Amount).ToArray();
			return (array.Length != 0) ? string.Join(", ", array) : "nothing";
		}

		private static void WritePayment(ZPackage package, IEnumerable<PaymentItem> payment)
		{
			List<PaymentItem> list = ((payment != null) ? payment.Where((PaymentItem item) => item != null && item.Amount > 0).ToList() : new List<PaymentItem>());
			package.Write(list.Count);
			foreach (PaymentItem item in list)
			{
				package.Write(item.PrefabName);
				package.Write(item.Amount);
			}
		}

		private static List<PaymentItem> ReadPayment(ZPackage package)
		{
			List<PaymentItem> list = new List<PaymentItem>();
			int num = Mathf.Clamp(package.ReadInt(), 0, 64);
			for (int i = 0; i < num; i++)
			{
				string text = package.ReadString();
				int num2 = package.ReadInt();
				GameObject val = (((Object)(object)ObjectDB.instance != (Object)null) ? ObjectDB.instance.GetItemPrefab(text) : null);
				ItemDrop val2 = (((Object)(object)val != (Object)null) ? val.GetComponent<ItemDrop>() : null);
				string text2 = (((Object)(object)val2 != (Object)null && val2.m_itemData != null && val2.m_itemData.m_shared != null) ? val2.m_itemData.m_shared.m_name : null);
				if (!string.IsNullOrEmpty(text) && !string.IsNullOrEmpty(text2) && num2 > 0)
				{
					list.Add(new PaymentItem(text, text2, num2));
				}
			}
			return list;
		}

		public static List<BuyAndDestroy> GetAllInstances()
		{
			return s_allInstances;
		}

		public static void UpdateDescription()
		{
			foreach (BuyAndDestroy s_allInstance in s_allInstances)
			{
				s_allInstance.m_requirements = BuyExtractorNeedle.requirements.Value;
				s_allInstance.m_description = "";
			}
		}
	}
	[BepInPlugin("shudnal.BuyExtractorNeedle", "Buy Dvergr Extractor Needle", "1.1.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class BuyExtractorNeedle : BaseUnityPlugin
	{
		[HarmonyPatch(typeof(ZNetScene), "Awake")]
		private static class ZNetSceneAwakePatch
		{
			private static void Postfix(ZNetScene __instance)
			{
				ApplyConfiguredPrefabs(__instance);
			}
		}

		public const string pluginID = "shudnal.BuyExtractorNeedle";

		public const string pluginName = "Buy Dvergr Extractor Needle";

		public const string pluginVersion = "1.1.0";

		private readonly Harmony harmony = new Harmony("shudnal.BuyExtractorNeedle");

		internal static readonly ConfigSync configSync = new ConfigSync("shudnal.BuyExtractorNeedle")
		{
			DisplayName = "Buy Dvergr Extractor Needle",
			CurrentVersion = "1.1.0",
			MinimumRequiredVersion = "1.1.0",
			ModRequired = true
		};

		public static BuyExtractorNeedle instance;

		public static ConfigEntry<bool> configLocked;

		public static ConfigEntry<string> requirements;

		public static ConfigEntry<string> targetPrefabs;

		public static ConfigEntry<bool> loggingEnabled;

		private static readonly Dictionary<string, string> managedPrefabTexts = new Dictionary<string, string>(StringComparer.Ordinal);

		private void Awake()
		{
			instance = this;
			ConfigInit();
			((ConditionalConfigSync)configSync).AddLockingConfigEntry<bool>(configLocked);
			harmony.PatchAll();
			Game.isModded = true;
		}

		private void ConfigInit()
		{
			configLocked = BindConfig("General", "Lock Configuration", defaultValue: true, "Configuration is locked and can be changed by server admins only.");
			requirements = BindConfig("General", "Required items", "Coins:4000", "Items required to buy a configured crate. Use prefab names separated by commas, for example Coins:4000 or Coins:2000,BlackCore:1.");
			targetPrefabs = BindConfig("General", "Target prefabs", "dvergrprops_crate_long", "Prefab names that should become purchasable, separated by commas.");
			loggingEnabled = BindConfig("General", "Enable logging", defaultValue: false, "Enable logging for interaction events.", synchronizedSetting: false);
			requirements.SettingChanged += delegate
			{
				BuyAndDestroy.UpdateDescription();
			};
			targetPrefabs.SettingChanged += delegate
			{
				ApplyConfiguredPrefabs(ZNetScene.instance);
			};
		}

		private void OnDestroy()
		{
			((BaseUnityPlugin)this).Config.Save();
			instance = null;
			harmony.UnpatchSelf();
		}

		public static void LogInfo(object data)
		{
			if (loggingEnabled.Value)
			{
				((BaseUnityPlugin)instance).Logger.LogInfo(data);
			}
		}

		public static void LogWarning(object data)
		{
			((BaseUnityPlugin)instance).Logger.LogWarning(data);
		}

		private ConfigEntry<T> BindConfig<T>(string group, string name, T defaultValue, ConfigDescription description, bool synchronizedSetting = true)
		{
			ConfigEntry<T> val = ((BaseUnityPlugin)this).Config.Bind<T>(group, name, defaultValue, description);
			SyncedConfigEntry<T> val2 = ((ConditionalConfigSync)configSync).AddConfigEntry<T>(val);
			((OwnConfigEntryBase)val2).SynchronizedConfig = synchronizedSetting;
			return val;
		}

		private ConfigEntry<T> BindConfig<T>(string group, string name, T defaultValue, string description, bool synchronizedSetting = true)
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Expected O, but got Unknown
			return BindConfig(group, name, defaultValue, new ConfigDescription(description, (AcceptableValueBase)null, Array.Empty<object>()), synchronizedSetting);
		}

		private static IEnumerable<string> GetTargetPrefabNames()
		{
			HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal);
			string text = ((targetPrefabs != null) ? targetPrefabs.Value : "dvergrprops_crate_long");
			string[] array = text.Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries);
			foreach (string text2 in array)
			{
				string text3 = text2.Trim();
				if (!string.IsNullOrEmpty(text3))
				{
					hashSet.Add(text3);
				}
			}
			return hashSet;
		}

		internal static void ApplyConfiguredPrefabs(ZNetScene scene)
		{
			if ((Object)(object)scene == (Object)null)
			{
				return;
			}
			HashSet<string> hashSet = new HashSet<string>(GetTargetPrefabNames(), StringComparer.Ordinal);
			foreach (string item in new List<string>(managedPrefabTexts.Keys))
			{
				if (!hashSet.Contains(item))
				{
					GameObject prefab = scene.GetPrefab(item);
					BuyAndDestroy buyAndDestroy = (((Object)(object)prefab != (Object)null) ? prefab.GetComponent<BuyAndDestroy>() : null);
					if ((Object)(object)buyAndDestroy != (Object)null)
					{
						HoverText val = prefab.GetComponent<HoverText>() ?? prefab.AddComponent<HoverText>();
						val.m_text = managedPrefabTexts[item];
						Object.Destroy((Object)(object)buyAndDestroy);
					}
					managedPrefabTexts.Remove(item);
				}
			}
			foreach (string item2 in hashSet)
			{
				GameObject prefab2 = scene.GetPrefab(item2);
				if ((Object)(object)prefab2 == (Object)null)
				{
					if ((Object)(object)instance != (Object)null)
					{
						LogWarning("Target prefab was not found: " + item2);
					}
					continue;
				}
				BuyAndDestroy component = prefab2.GetComponent<BuyAndDestroy>();
				if ((Object)(object)component != (Object)null)
				{
					managedPrefabTexts[item2] = component.m_text;
					continue;
				}
				HoverText component2 = prefab2.GetComponent<HoverText>();
				if ((Object)(object)component2 == (Object)null)
				{
					if ((Object)(object)instance != (Object)null)
					{
						LogWarning("Target prefab does not have HoverText and cannot be made purchasable: " + item2);
					}
				}
				else
				{
					managedPrefabTexts[item2] = component2.m_text;
					BuyAndDestroy buyAndDestroy2 = prefab2.AddComponent<BuyAndDestroy>();
					buyAndDestroy2.m_text = component2.m_text;
					Object.Destroy((Object)(object)component2);
				}
			}
		}
	}
	internal sealed class DvergrPaidItemStorage : MonoBehaviour
	{
		[HarmonyPatch(typeof(Character), "Awake")]
		private static class CharacterAwakePatch
		{
			private static void Postfix(Character __instance)
			{
				if (IsDvergr(__instance) && (Object)(object)((Component)__instance).GetComponent<DvergrPaidItemStorage>() == (Object)null)
				{
					((Component)__instance).gameObject.AddComponent<DvergrPaidItemStorage>();
				}
			}
		}

		[HarmonyPatch(typeof(CharacterDrop), "GenerateDropList")]
		private static class CharacterDropGenerateDropListPatch
		{
			private static void Postfix(CharacterDrop __instance)
			{
				DropPaidItems(__instance);
			}
		}

		private static readonly int PaidItemsZdoKey = StringExtensionMethods.GetStableHashCode("shudnal.BuyExtractorNeedle.PaidItems");

		private const string AcceptPurchaseRpc = "BEN_AcceptPurchase";

		private const string StorePaymentRpc = "BEN_StorePayment";

		private const string PlayResponseRpc = "BEN_PlayResponse";

		private NpcTalk npcTalk;

		internal ZNetView nview;

		private bool initialized;

		private void Awake()
		{
			Initialize();
		}

		private bool Initialize()
		{
			if (initialized)
			{
				return true;
			}
			Character component = ((Component)this).GetComponent<Character>();
			npcTalk = ((Component)this).GetComponent<NpcTalk>();
			nview = ((Component)this).GetComponent<ZNetView>();
			if ((Object)(object)component == (Object)null || (Object)(object)npcTalk == (Object)null || (Object)(object)nview == (Object)null || nview.GetZDO() == null)
			{
				return false;
			}
			nview.Register<ZPackage>("BEN_AcceptPurchase", (Action<long, ZPackage>)RPC_AcceptPurchase);
			nview.Register<ZPackage>("BEN_StorePayment", (Action<long, ZPackage>)RPC_StorePayment);
			nview.Register<int>("BEN_PlayResponse", (Action<long, int>)RPC_PlayResponse);
			initialized = true;
			return true;
		}

		internal bool AcceptPurchase(BuyAndDestroy.PurchaseData purchase)
		{
			if (!Initialize() || !nview.IsValid() || purchase == null)
			{
				return false;
			}
			nview.InvokeRPC("BEN_AcceptPurchase", new object[1] { BuyAndDestroy.CreatePurchasePackage(purchase.BuyerName, purchase.PurchasedPrefabName, purchase.Payment) });
			return true;
		}

		private bool StorePayment(BuyAndDestroy.PurchaseData purchase)
		{
			if (!Initialize() || !nview.IsValid() || purchase == null)
			{
				return false;
			}
			nview.InvokeRPC("BEN_StorePayment", new object[1] { BuyAndDestroy.CreatePurchasePackage(purchase.BuyerName, purchase.PurchasedPrefabName, purchase.Payment) });
			return true;
		}

		private void RPC_AcceptPurchase(long sender, ZPackage package)
		{
			if (!((Object)(object)nview == (Object)null) && nview.IsValid() && nview.IsOwner())
			{
				BuyAndDestroy.PurchaseData purchase = BuyAndDestroy.ReadPurchasePackage(package);
				DistributeAndStorePayment(purchase, 30f);
				int num = Random.Range(0, BuyAndDestroy.s_randomBuy.Count);
				nview.InvokeRPC(ZRoutedRpc.Everybody, "BEN_PlayResponse", new object[1] { num });
			}
		}

		private void RPC_StorePayment(long sender, ZPackage package)
		{
			if (!((Object)(object)nview == (Object)null) && nview.IsValid() && nview.IsOwner())
			{
				BuyAndDestroy.PurchaseData purchaseData = BuyAndDestroy.ReadPurchasePackage(package);
				StoreItems(purchaseData.Payment, purchaseData);
			}
		}

		private void RPC_PlayResponse(long sender, int lineIndex)
		{
			BuyAndDestroy.PlaySaleResponse(npcTalk, lineIndex);
		}

		private void DistributeAndStorePayment(BuyAndDestroy.PurchaseData purchase, float radius)
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			List<BuyAndDestroy.PaymentItem> list = ClonePayment(purchase.Payment);
			List<DvergrPaidItemStorage> list2 = FindNearby(((Component)this).transform.position, radius, this);
			List<string> list3 = new List<string>();
			int num = 0;
			foreach (BuyAndDestroy.PaymentItem item in list)
			{
				int maxStackSize = GetMaxStackSize(item.PrefabName);
				while (item.Amount > maxStackSize && num < list2.Count)
				{
					DvergrPaidItemStorage dvergrPaidItemStorage = list2[num++];
					List<BuyAndDestroy.PaymentItem> payment = new List<BuyAndDestroy.PaymentItem> { item.Clone(maxStackSize) };
					BuyAndDestroy.PurchaseData purchase2 = new BuyAndDestroy.PurchaseData(purchase.BuyerName, purchase.PurchasedPrefabName, payment);
					if (dvergrPaidItemStorage.StorePayment(purchase2))
					{
						item.Amount -= maxStackSize;
						list3.Add(dvergrPaidItemStorage.GetLogDescription() + " <- " + BuyAndDestroy.FormatPayment(payment));
					}
					else if ((Object)(object)BuyExtractorNeedle.instance != (Object)null)
					{
						BuyExtractorNeedle.LogWarning("Could not assign " + BuyAndDestroy.FormatPayment(payment) + " from player '" + purchase.BuyerName + "' to " + dvergrPaidItemStorage.GetLogDescription() + ". The main Dvergr will keep it instead.");
					}
				}
			}
			StoreItems(list, purchase);
			list3.Insert(0, GetLogDescription() + " <- " + BuyAndDestroy.FormatPayment(list));
			if ((Object)(object)BuyExtractorNeedle.instance != (Object)null)
			{
				BuyExtractorNeedle.LogInfo("Payment distribution for player '" + purchase.BuyerName + "' buying '" + purchase.PurchasedPrefabName + "': " + string.Join("; ", list3.ToArray()) + ".");
			}
		}

		private void StoreItems(IEnumerable<BuyAndDestroy.PaymentItem> payment, BuyAndDestroy.PurchaseData purchase)
		{
			ZDO val = (((Object)(object)nview != (Object)null) ? nview.GetZDO() : null);
			if (val == null)
			{
				if ((Object)(object)BuyExtractorNeedle.instance != (Object)null)
				{
					BuyExtractorNeedle.LogWarning("Could not store payment on " + GetLogDescription() + " because its ZDO is unavailable.");
				}
				return;
			}
			List<BuyAndDestroy.PaymentItem> list = ClonePayment(payment);
			Dictionary<string, int> dictionary = ReadStoredItems(val);
			foreach (BuyAndDestroy.PaymentItem item in list)
			{
				dictionary.TryGetValue(item.PrefabName, out var value);
				dictionary[item.PrefabName] = (int)Math.Min(2147483647L, (long)value + (long)item.Amount);
			}
			WriteStoredItems(val, dictionary);
			if ((Object)(object)BuyExtractorNeedle.instance != (Object)null)
			{
				BuyExtractorNeedle.LogInfo("Stored payment from player '" + purchase.BuyerName + "' for '" + purchase.PurchasedPrefabName + "' on " + GetLogDescription() + ": added " + BuyAndDestroy.FormatPayment(list) + "; total stored " + FormatStoredItems(dictionary) + ".");
			}
		}

		private static List<BuyAndDestroy.PaymentItem> ClonePayment(IEnumerable<BuyAndDestroy.PaymentItem> payment)
		{
			List<BuyAndDestroy.PaymentItem> list = new List<BuyAndDestroy.PaymentItem>();
			if (payment == null)
			{
				return list;
			}
			foreach (BuyAndDestroy.PaymentItem item in payment)
			{
				if (item != null && item.Amount > 0)
				{
					list.Add(item.Clone(item.Amount));
				}
			}
			return list;
		}

		private static int GetMaxStackSize(string prefabName)
		{
			GameObject val = (((Object)(object)ObjectDB.instance != (Object)null) ? ObjectDB.instance.GetItemPrefab(prefabName) : null);
			ItemDrop val2 = (((Object)(object)val != (Object)null) ? val.GetComponent<ItemDrop>() : null);
			return ((Object)(object)val2 != (Object)null && val2.m_itemData != null && val2.m_itemData.m_shared != null) ? Math.Max(1, val2.m_itemData.m_shared.m_maxStackSize) : int.MaxValue;
		}

		internal static DvergrPaidItemStorage FindClosest(Vector3 position, float radius)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			List<DvergrPaidItemStorage> list = FindNearby(position, radius, null);
			return (list.Count > 0) ? list[0] : null;
		}

		private static List<DvergrPaidItemStorage> FindNearby(Vector3 position, float radius, DvergrPaidItemStorage excluded)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			List<Character> list = new List<Character>();
			Character.GetCharactersInRange(position, radius, list);
			List<DvergrPaidItemStorage> list2 = new List<DvergrPaidItemStorage>();
			foreach (Character item in list)
			{
				if (IsDvergr(item))
				{
					DvergrPaidItemStorage dvergrPaidItemStorage = ((Component)item).GetComponent<DvergrPaidItemStorage>();
					if ((Object)(object)dvergrPaidItemStorage == (Object)null)
					{
						dvergrPaidItemStorage = ((Component)item).gameObject.AddComponent<DvergrPaidItemStorage>();
					}
					if (!((Object)(object)dvergrPaidItemStorage == (Object)(object)excluded) && dvergrPaidItemStorage.Initialize() && dvergrPaidItemStorage.nview.IsValid())
					{
						list2.Add(dvergrPaidItemStorage);
					}
				}
			}
			list2.Sort(delegate(DvergrPaidItemStorage left, DvergrPaidItemStorage right)
			{
				//IL_0007: Unknown result type (might be due to invalid IL or missing references)
				//IL_000d: 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)
				//IL_0017: Unknown result type (might be due to invalid IL or missing references)
				//IL_0026: Unknown result type (might be due to invalid IL or missing references)
				//IL_002c: Unknown result type (might be due to invalid IL or missing references)
				//IL_0031: Unknown result type (might be due to invalid IL or missing references)
				//IL_0036: Unknown result type (might be due to invalid IL or missing references)
				Vector3 val = ((Component)left).transform.position - position;
				float sqrMagnitude = ((Vector3)(ref val)).sqrMagnitude;
				val = ((Component)right).transform.position - position;
				float sqrMagnitude2 = ((Vector3)(ref val)).sqrMagnitude;
				return sqrMagnitude.CompareTo(sqrMagnitude2);
			});
			return list2;
		}

		internal static bool IsDvergr(Character candidate)
		{
			if ((Object)(object)candidate == (Object)null || (Object)(object)((Component)candidate).GetComponent<NpcTalk>() == (Object)null)
			{
				return false;
			}
			ZNetView component = ((Component)candidate).GetComponent<ZNetView>();
			if ((Object)(object)component == (Object)null || component.GetZDO() == null)
			{
				return false;
			}
			string prefabName = component.GetPrefabName();
			return !string.IsNullOrEmpty(prefabName) && prefabName.StartsWith("Dverger", StringComparison.Ordinal);
		}

		internal string GetLogDescription()
		{
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			string text = (((Object)(object)nview != (Object)null) ? nview.GetPrefabName() : ((Object)((Component)this).gameObject).name);
			if (string.IsNullOrEmpty(text))
			{
				text = ((Object)((Component)this).gameObject).name;
			}
			string text2 = text;
			Vector3 position = ((Component)this).transform.position;
			return "'" + text2 + "' at " + ((Vector3)(ref position)).ToString("F1");
		}

		private static string GetLogDescription(Character character, ZNetView characterView)
		{
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			string text = (((Object)(object)characterView != (Object)null) ? characterView.GetPrefabName() : (((Object)(object)character != (Object)null) ? ((Object)((Component)character).gameObject).name : "Unknown Dvergr"));
			if (string.IsNullOrEmpty(text) && (Object)(object)character != (Object)null)
			{
				text = ((Object)((Component)character).gameObject).name;
			}
			Vector3 val = (((Object)(object)character != (Object)null) ? ((Component)character).transform.position : Vector3.zero);
			return "'" + text + "' at " + ((Vector3)(ref val)).ToString("F1");
		}

		private static Dictionary<string, int> ReadStoredItems(ZDO zdo)
		{
			Dictionary<string, int> dictionary = new Dictionary<string, int>(StringComparer.Ordinal);
			if (zdo == null)
			{
				return dictionary;
			}
			string text = zdo.GetString(PaidItemsZdoKey, "");
			if (string.IsNullOrEmpty(text))
			{
				return dictionary;
			}
			string[] array = text.Split(new char[1] { ';' }, StringSplitOptions.RemoveEmptyEntries);
			foreach (string text2 in array)
			{
				int num = text2.LastIndexOf(':');
				if (num > 0 && num < text2.Length - 1)
				{
					string key = text2.Substring(0, num);
					if (int.TryParse(text2.Substring(num + 1), out var result) && result > 0)
					{
						dictionary.TryGetValue(key, out var value);
						dictionary[key] = (int)Math.Min(2147483647L, (long)value + (long)result);
					}
				}
			}
			return dictionary;
		}

		private static void WriteStoredItems(ZDO zdo, Dictionary<string, int> items)
		{
			if (zdo == null)
			{
				return;
			}
			List<string> list = new List<string>();
			foreach (KeyValuePair<string, int> item in items)
			{
				if (!string.IsNullOrEmpty(item.Key) && item.Value > 0)
				{
					list.Add(item.Key + ":" + item.Value);
				}
			}
			list.Sort(StringComparer.Ordinal);
			zdo.Set(PaidItemsZdoKey, string.Join(";", list.ToArray()));
		}

		private static string FormatStoredItems(IDictionary<string, int> items)
		{
			if (items == null || items.Count == 0)
			{
				return "nothing";
			}
			return string.Join(", ", (from item in items.Where((KeyValuePair<string, int> item) => !string.IsNullOrEmpty(item.Key) && item.Value > 0).OrderBy<KeyValuePair<string, int>, string>((KeyValuePair<string, int> item) => item.Key, StringComparer.Ordinal)
				select item.Key + " x" + item.Value).ToArray());
		}

		private static void DropPaidItems(CharacterDrop characterDrop)
		{
			//IL_031a: Unknown result type (might be due to invalid IL or missing references)
			//IL_031f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0324: Unknown result type (might be due to invalid IL or missing references)
			//IL_0329: Unknown result type (might be due to invalid IL or missing references)
			//IL_0333: Unknown result type (might be due to invalid IL or missing references)
			//IL_0338: Unknown result type (might be due to invalid IL or missing references)
			//IL_033d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0346: Unknown result type (might be due to invalid IL or missing references)
			//IL_0348: Unknown result type (might be due to invalid IL or missing references)
			Character val = (((Object)(object)characterDrop != (Object)null) ? ((Component)characterDrop).GetComponent<Character>() : null);
			if ((Object)(object)val == (Object)null && (Object)(object)characterDrop != (Object)null)
			{
				val = ((Component)characterDrop).GetComponentInParent<Character>();
			}
			if ((Object)(object)val == (Object)null)
			{
				return;
			}
			ZNetView component = ((Component)val).GetComponent<ZNetView>();
			ZDO val2 = (((Object)(object)component != (Object)null) ? component.GetZDO() : null);
			string text = ((val2 != null) ? val2.GetString(PaidItemsZdoKey, "") : "");
			if (string.IsNullOrEmpty(text))
			{
				return;
			}
			string logDescription = GetLogDescription(val, component);
			if (!IsDvergr(val))
			{
				if ((Object)(object)BuyExtractorNeedle.instance != (Object)null)
				{
					BuyExtractorNeedle.LogWarning(logDescription + " has stored payment but was not recognized as a Dvergr at death. Raw stored value: " + text + ".");
				}
				return;
			}
			if ((Object)(object)component == (Object)null || !component.IsValid())
			{
				if ((Object)(object)BuyExtractorNeedle.instance != (Object)null)
				{
					BuyExtractorNeedle.LogWarning(logDescription + " died with stored payment, but its ZNetView was not valid. Raw stored value: " + text + ".");
				}
				return;
			}
			if (!component.IsOwner())
			{
				if ((Object)(object)BuyExtractorNeedle.instance != (Object)null)
				{
					BuyExtractorNeedle.LogInfo("Stored payment drop for " + logDescription + " was skipped on this peer because it is not the ZDO owner. Raw stored value: " + text + ".");
				}
				return;
			}
			Dictionary<string, int> dictionary = ReadStoredItems(val2);
			if (dictionary.Count == 0)
			{
				if ((Object)(object)BuyExtractorNeedle.instance != (Object)null)
				{
					BuyExtractorNeedle.LogWarning(logDescription + " died with an unreadable stored payment value: " + text + ".");
				}
				return;
			}
			if ((Object)(object)BuyExtractorNeedle.instance != (Object)null)
			{
				BuyExtractorNeedle.LogInfo(logDescription + " died with stored payment: " + FormatStoredItems(dictionary) + ".");
			}
			Dictionary<string, int> dictionary2 = new Dictionary<string, int>(StringComparer.Ordinal);
			int num = 0;
			foreach (KeyValuePair<string, int> item in dictionary)
			{
				GameObject val3 = (((Object)(object)ObjectDB.instance != (Object)null) ? ObjectDB.instance.GetItemPrefab(item.Key) : null);
				ItemDrop val4 = (((Object)(object)val3 != (Object)null) ? val3.GetComponent<ItemDrop>() : null);
				if ((Object)(object)val4 == (Object)null || val4.m_itemData == null || val4.m_itemData.m_shared == null)
				{
					if ((Object)(object)BuyExtractorNeedle.instance != (Object)null)
					{
						BuyExtractorNeedle.LogWarning("Cannot drop stored payment from " + logDescription + " because item prefab was not found: " + item.Key + " x" + item.Value + ".");
					}
					continue;
				}
				int val5 = Math.Max(1, val4.m_itemData.m_shared.m_maxStackSize);
				int num2 = item.Value;
				while (num2 > 0)
				{
					int num3 = Math.Min(val5, num2);
					Vector3 position = ((Component)val).transform.position + Vector3.up + Random.insideUnitSphere * 0.4f;
					ItemDrop val6;
					try
					{
						val6 = BuyAndDestroy.DropItemStack(val3, val4, num3, position, Random.rotation);
					}
					catch (Exception ex)
					{
						if ((Object)(object)BuyExtractorNeedle.instance != (Object)null)
						{
							BuyExtractorNeedle.LogWarning("Failed to drop " + item.Key + " x" + num3 + " from " + logDescription + ": " + ex?.ToString() + ".");
						}
						break;
					}
					if ((Object)(object)val6 == (Object)null)
					{
						if ((Object)(object)BuyExtractorNeedle.instance != (Object)null)
						{
							BuyExtractorNeedle.LogWarning("Failed to create dropped item " + item.Key + " x" + num3 + " for " + logDescription + ".");
						}
						break;
					}
					dictionary2.TryGetValue(item.Key, out var value);
					dictionary2[item.Key] = value + num3;
					num++;
					num2 -= num3;
				}
			}
			val2.Set(PaidItemsZdoKey, "");
			if (!((Object)(object)BuyExtractorNeedle.instance == (Object)null))
			{
				int num4 = dictionary.Sum((KeyValuePair<string, int> item) => item.Value);
				int num5 = dictionary2.Sum((KeyValuePair<string, int> item) => item.Value);
				if (num5 == num4)
				{
					BuyExtractorNeedle.LogInfo(logDescription + " dropped stored payment: " + FormatStoredItems(dictionary2) + " in " + num + " stack(s).");
				}
				else
				{
					BuyExtractorNeedle.LogWarning(logDescription + " dropped only " + FormatStoredItems(dictionary2) + " in " + num + " stack(s); expected " + FormatStoredItems(dictionary) + ".");
				}
			}
		}
	}
}