Decompiled source of Pickup Notification Adjustment v0.1.2

plugins/PickupNotificationAdjustment/PickupNotificationAdjustment.dll

Decompiled 2 days ago
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.CompilerServices;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using UnityEngine;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: AssemblyFileVersion("0.1.2.0")]
[assembly: AssemblyVersion("0.1.2.0")]
namespace PickupNotificationAdjustment;

internal sealed class ReceiptLedger
{
	private sealed class Credit
	{
		public int Key;

		public int Amount;

		public bool Actual;

		public double Time;
	}

	private readonly List<Credit> credits = new List<Credit>();

	public int Add(int key, int amount, bool actual, double now)
	{
		credits.RemoveAll((Credit c) => now - c.Time > 2.0);
		if (amount <= 0)
		{
			return 0;
		}
		int num = amount;
		foreach (Credit credit in credits)
		{
			if (credit.Key == key && credit.Actual != actual && credit.Amount != 0)
			{
				int num2 = Math.Min(num, credit.Amount);
				num -= num2;
				credit.Amount -= num2;
				if (num == 0)
				{
					break;
				}
			}
		}
		credits.RemoveAll((Credit c) => c.Amount == 0);
		if (num > 0)
		{
			credits.Add(new Credit
			{
				Key = key,
				Amount = num,
				Actual = actual,
				Time = now
			});
		}
		return num;
	}

	public void Clear()
	{
		credits.Clear();
	}
}
internal sealed class DisplayQueue<T>
{
	internal sealed class Visible
	{
		public T Value;

		public double Remaining;
	}

	private readonly Queue<T> pending = new Queue<T>();

	public readonly List<Visible> Active = new List<Visible>();

	public int PendingCount => pending.Count;

	public void Add(T value)
	{
		pending.Enqueue(value);
	}

	public void AddOrMerge(T value, Func<T, T, bool> sameItem, Action<T, T> merge, double lifetime)
	{
		foreach (Visible item in Active)
		{
			if (sameItem(item.Value, value))
			{
				merge.Invoke(item.Value, value);
				item.Remaining = Math.Max(1.0, lifetime);
				return;
			}
		}
		foreach (T item2 in pending)
		{
			if (sameItem(item2, value))
			{
				merge.Invoke(item2, value);
				return;
			}
		}
		pending.Enqueue(value);
	}

	public void Advance(double delta, int limit, double lifetime)
	{
		for (int num = Active.Count - 1; num >= 0; num--)
		{
			Active[num].Remaining -= Math.Max(0.0, delta);
			if (Active[num].Remaining <= 0.0)
			{
				Active.RemoveAt(num);
			}
		}
		while (Active.Count < Math.Max(1, limit) && pending.Count > 0)
		{
			Active.Add(new Visible
			{
				Value = pending.Dequeue(),
				Remaining = Math.Max(1.0, lifetime)
			});
		}
	}

	public void Clear()
	{
		pending.Clear();
		Active.Clear();
	}
}
public sealed class FeedView : MonoBehaviour
{
	private sealed class Entry
	{
		public int ItemID;

		public string Name;

		public Sprite Icon;

		public long Amount;
	}

	private sealed class Row
	{
		public RectTransform Rect;

		public Text Label;

		public Image Icon;

		public CanvasGroup Group;
	}

	private readonly DisplayQueue<Entry> queue = new DisplayQueue<Entry>();

	private readonly ReceiptLedger ledger = new ReceiptLedger();

	private readonly List<Row> rows = new List<Row>();

	private readonly Vector3[] corners = (Vector3[])(object)new Vector3[4];

	private CharacterUI ui;

	private Character character;

	private RectTransform root;

	private RectTransform parent;

	private CanvasGroup group;

	private Font font;

	private QuickSlotPanel[] hotbars;

	private float nextLayoutCheck;

	private float hotbarTop = 150f;

	private bool watchingSilver;

	private int previousSilver;

	private float silverDeadline;

	private Ammunition watchedAmmo;

	private int ammoBefore;

	private int ammoExpected;

	private float ammoDeadline;

	internal void WatchAmmo(Ammunition ammo, int incoming)
	{
		CheckAmmo();
		if ((Object)(object)watchedAmmo != (Object)(object)ammo)
		{
			ammoExpected = 0;
		}
		watchedAmmo = ammo;
		ammoBefore = ((Item)ammo).RemainingAmount;
		ammoExpected += Math.Min(incoming, Math.Max(0, ((Item)ammo).MaxStackAmount - ammoBefore));
		ammoDeadline = Time.unscaledTime + 10f;
	}

	private void CheckAmmo()
	{
		if (Object.op_Implicit((Object)(object)watchedAmmo))
		{
			int remainingAmount = ((Item)watchedAmmo).RemainingAmount;
			int num = Math.Min(ammoExpected, remainingAmount - ammoBefore);
			ammoBefore = remainingAmount;
			if (num > 0)
			{
				Receive(((Item)watchedAmmo).ItemID, ((Item)watchedAmmo).Name, ((Item)watchedAmmo).ItemIcon, num, actual: true);
				ammoExpected -= num;
			}
			if (ammoExpected <= 0 || Time.unscaledTime >= ammoDeadline)
			{
				watchedAmmo = null;
			}
		}
	}

	internal void WatchSilver(Character c)
	{
		CheckSilver();
		previousSilver = c.Inventory.ContainedSilver;
		watchingSilver = true;
		silverDeadline = Time.unscaledTime + 10f;
	}

	private void CheckSilver()
	{
		if (watchingSilver && Object.op_Implicit((Object)(object)character))
		{
			int containedSilver = character.Inventory.ContainedSilver;
			int num = containedSilver - previousSilver;
			previousSilver = containedSilver;
			if (num > 0)
			{
				Currency silverPrefab = Currency.SilverPrefab;
				Receive(((Item)silverPrefab).ItemID, ((Item)silverPrefab).Name, ((Item)silverPrefab).ItemIcon, num, actual: true);
				watchingSilver = false;
			}
			if (Time.unscaledTime >= silverDeadline)
			{
				watchingSilver = false;
			}
		}
	}

	public void Initialize(CharacterUI owner)
	{
		//IL_0038: Unknown result type (might be due to invalid IL or missing references)
		//IL_0042: Expected O, but got Unknown
		//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f1: Expected O, but got Unknown
		//IL_0125: Unknown result type (might be due to invalid IL or missing references)
		//IL_012a: Unknown result type (might be due to invalid IL or missing references)
		//IL_012b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0131: Unknown result type (might be due to invalid IL or missing references)
		//IL_0147: Unknown result type (might be due to invalid IL or missing references)
		ui = owner;
		character = ui.TargetCharacter;
		parent = (RectTransform)AccessTools.Field(typeof(CharacterUI), "m_gameplayPanelsHolder").GetValue(ui);
		if (!Object.op_Implicit((Object)(object)parent))
		{
			ref RectTransform reference = ref parent;
			Transform transform = ((Component)ui).transform;
			reference = (RectTransform)(object)((transform is RectTransform) ? transform : null);
		}
		if (!Object.op_Implicit((Object)(object)parent))
		{
			throw new InvalidOperationException("Player UI has no RectTransform.");
		}
		Text val = (Object.op_Implicit((Object)(object)ui.SmallNotificationPanel) ? ((Component)ui.SmallNotificationPanel).GetComponentInChildren<Text>(true) : null);
		font = (Object.op_Implicit((Object)(object)val) ? val.font : Resources.GetBuiltinResource<Font>("Arial.ttf"));
		GameObject val2 = new GameObject("Pickup Notification Adjustment", new Type[2]
		{
			typeof(RectTransform),
			typeof(CanvasGroup)
		});
		root = val2.GetComponent<RectTransform>();
		((Transform)root).SetParent((Transform)(object)parent, false);
		RectTransform obj = root;
		Vector2 anchorMin = (root.anchorMax = new Vector2(1f, 0f));
		obj.anchorMin = anchorMin;
		root.pivot = new Vector2(1f, 0f);
		group = val2.GetComponent<CanvasGroup>();
		group.interactable = false;
		group.blocksRaycasts = false;
		hotbars = ((Component)ui).GetComponentsInChildren<QuickSlotPanel>(true);
		((Transform)root).SetAsLastSibling();
	}

	internal void Receive(int id, string name, Sprite icon, int amount, bool actual)
	{
		int num = ledger.Add(id, amount, actual, Time.unscaledTime);
		if (num <= 0)
		{
			return;
		}
		queue.AddOrMerge(new Entry
		{
			ItemID = id,
			Name = (name ?? "Item"),
			Icon = icon,
			Amount = num
		}, (Entry a, Entry b) => a.ItemID == b.ItemID, delegate(Entry a, Entry b)
		{
			a.Amount += b.Amount;
			if (!Object.op_Implicit((Object)(object)a.Icon) && Object.op_Implicit((Object)(object)b.Icon))
			{
				a.Icon = b.Icon;
			}
		}, Plugin.Duration.Value);
	}

	private void LateUpdate()
	{
		//IL_01c7: Unknown result type (might be due to invalid IL or missing references)
		//IL_01cc: Unknown result type (might be due to invalid IL or missing references)
		//IL_01dc: Unknown result type (might be due to invalid IL or missing references)
		//IL_0209: Unknown result type (might be due to invalid IL or missing references)
		if (!Object.op_Implicit((Object)(object)ui) || !Object.op_Implicit((Object)(object)root))
		{
			return;
		}
		try
		{
			if (!Plugin.GameplayReady || (Object)(object)ui.TargetCharacter != (Object)(object)character)
			{
				queue.Clear();
				ledger.Clear();
				group.alpha = 0f;
				watchingSilver = false;
				watchedAmmo = null;
				character = ui.TargetCharacter;
				return;
			}
			CheckSilver();
			CheckAmmo();
			bool flag = ((Component)parent).gameObject.activeInHierarchy;
			CanvasGroup[] componentsInParent = ((Component)parent).GetComponentsInParent<CanvasGroup>();
			foreach (CanvasGroup val in componentsInParent)
			{
				if (val.alpha < 0.01f)
				{
					flag = false;
				}
			}
			group.alpha = (flag ? 1 : 0);
			if (!flag)
			{
				return;
			}
			queue.Advance(Time.unscaledDeltaTime, Plugin.Rows.Value, Plugin.Duration.Value);
			UpdateLayout();
			while (rows.Count < queue.Active.Count)
			{
				rows.Add(CreateRow());
			}
			float num = Math.Max(40, Plugin.FontSize.Value + 18);
			for (int j = 0; j < rows.Count; j++)
			{
				Row row = rows[j];
				bool flag2 = j < queue.Active.Count;
				((Component)row.Rect).gameObject.SetActive(flag2);
				if (flag2)
				{
					DisplayQueue<Entry>.Visible visible = queue.Active[j];
					RectTransform rect = row.Rect;
					Rect rect2 = root.rect;
					rect.sizeDelta = new Vector2(((Rect)(ref rect2)).width, num - 3f);
					row.Rect.anchoredPosition = new Vector2(0f, (float)(queue.Active.Count - 1 - j) * num);
					row.Label.fontSize = Plugin.FontSize.Value;
					row.Label.resizeTextMaxSize = Plugin.FontSize.Value;
					row.Label.text = "+" + visible.Value.Amount + "  " + visible.Value.Name;
					row.Icon.sprite = visible.Value.Icon;
					((Behaviour)row.Icon).enabled = Object.op_Implicit((Object)(object)visible.Value.Icon);
					row.Group.alpha = Mathf.Clamp01((float)visible.Remaining / 0.5f);
				}
			}
		}
		catch (Exception e)
		{
			Plugin.Report(e);
		}
	}

	private void UpdateLayout()
	{
		//IL_0129: Unknown result type (might be due to invalid IL or missing references)
		//IL_012e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0154: Unknown result type (might be due to invalid IL or missing references)
		//IL_0159: Unknown result type (might be due to invalid IL or missing references)
		//IL_0177: Unknown result type (might be due to invalid IL or missing references)
		//IL_017c: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ba: Unknown result type (might be due to invalid IL or missing references)
		//IL_0204: Unknown result type (might be due to invalid IL or missing references)
		//IL_009f: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b9: 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_00d8: Unknown result type (might be due to invalid IL or missing references)
		if (Time.unscaledTime >= nextLayoutCheck)
		{
			nextLayoutCheck = Time.unscaledTime + 0.5f;
			hotbarTop = 150f;
			bool flag = false;
			if (hotbars != null)
			{
				QuickSlotPanel[] array = hotbars;
				foreach (QuickSlotPanel val in array)
				{
					if (!Object.op_Implicit((Object)(object)val) || !((Component)val).gameObject.activeInHierarchy)
					{
						continue;
					}
					Transform transform = ((Component)val).transform;
					RectTransform val2 = (RectTransform)(object)((transform is RectTransform) ? transform : null);
					if (!Object.op_Implicit((Object)(object)val2))
					{
						continue;
					}
					val2.GetWorldCorners(corners);
					float y = ((Transform)parent).InverseTransformPoint(corners[1]).y;
					Rect rect = parent.rect;
					float num = y - ((Rect)(ref rect)).yMin;
					if (!(num <= 0f))
					{
						Rect rect2 = parent.rect;
						if (!(num > ((Rect)(ref rect2)).height * 0.55f))
						{
							hotbarTop = (flag ? Math.Max(hotbarTop, num) : num);
							flag = true;
						}
					}
				}
			}
		}
		float value = Plugin.Width.Value;
		Rect rect3 = parent.rect;
		float num2 = Mathf.Min(value, Mathf.Max(160f, ((Rect)(ref rect3)).width - 24f));
		Rect rect4 = parent.rect;
		float num3 = Mathf.Max(0f, ((Rect)(ref rect4)).width - num2);
		Rect rect5 = parent.rect;
		float num4 = Mathf.Max(0f, ((Rect)(ref rect5)).height - (float)(Math.Max(40, Plugin.FontSize.Value + 18) * Plugin.Rows.Value));
		root.sizeDelta = new Vector2(num2, 0f);
		root.anchoredPosition = new Vector2(0f - Mathf.Clamp(Plugin.RightMargin.Value, 0f, num3), Mathf.Clamp(hotbarTop + 4f + Plugin.HeightOffset.Value, 0f, num4));
	}

	private Row CreateRow()
	{
		//IL_0039: Unknown result type (might be due to invalid IL or missing references)
		//IL_003f: Expected O, but got Unknown
		//IL_005f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0064: Unknown result type (might be due to invalid IL or missing references)
		//IL_0065: Unknown result type (might be due to invalid IL or missing references)
		//IL_006c: 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)
		//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e6: Expected O, but got Unknown
		//IL_0105: Unknown result type (might be due to invalid IL or missing references)
		//IL_010a: Unknown result type (might be due to invalid IL or missing references)
		//IL_010b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0112: Unknown result type (might be due to invalid IL or missing references)
		//IL_0125: Unknown result type (might be due to invalid IL or missing references)
		//IL_013b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0151: Unknown result type (might be due to invalid IL or missing references)
		//IL_019e: Unknown result type (might be due to invalid IL or missing references)
		//IL_01a5: Expected O, but got Unknown
		//IL_01b9: Unknown result type (might be due to invalid IL or missing references)
		//IL_01c5: Unknown result type (might be due to invalid IL or missing references)
		//IL_01db: Unknown result type (might be due to invalid IL or missing references)
		//IL_01f1: Unknown result type (might be due to invalid IL or missing references)
		//IL_0222: Unknown result type (might be due to invalid IL or missing references)
		GameObject val = new GameObject("Pickup", new Type[3]
		{
			typeof(RectTransform),
			typeof(CanvasGroup),
			typeof(Image)
		});
		RectTransform component = val.GetComponent<RectTransform>();
		((Transform)component).SetParent((Transform)(object)root, false);
		Vector2 anchorMin = (component.anchorMax = new Vector2(1f, 0f));
		component.anchorMin = anchorMin;
		component.pivot = new Vector2(1f, 0f);
		Image component2 = val.GetComponent<Image>();
		((Graphic)component2).color = new Color(0.035f, 0.045f, 0.055f, 0.8f);
		((Graphic)component2).raycastTarget = false;
		GameObject val3 = new GameObject("Item icon", new Type[2]
		{
			typeof(RectTransform),
			typeof(Image)
		});
		RectTransform component3 = val3.GetComponent<RectTransform>();
		((Transform)component3).SetParent((Transform)(object)component, false);
		Vector2 anchorMin2 = (component3.anchorMax = new Vector2(0f, 0.5f));
		component3.anchorMin = anchorMin2;
		component3.pivot = new Vector2(0f, 0.5f);
		component3.anchoredPosition = new Vector2(5f, 0f);
		component3.sizeDelta = new Vector2(32f, 32f);
		Image component4 = val3.GetComponent<Image>();
		((Graphic)component4).raycastTarget = false;
		component4.preserveAspect = true;
		GameObject val5 = new GameObject("Item name", new Type[2]
		{
			typeof(RectTransform),
			typeof(Text)
		});
		RectTransform component5 = val5.GetComponent<RectTransform>();
		((Transform)component5).SetParent((Transform)(object)component, false);
		component5.anchorMin = Vector2.zero;
		component5.anchorMax = Vector2.one;
		component5.offsetMin = new Vector2(44f, 3f);
		component5.offsetMax = new Vector2(-9f, -3f);
		Text component6 = val5.GetComponent<Text>();
		component6.font = font;
		((Graphic)component6).color = new Color(0.95f, 0.93f, 0.86f);
		component6.alignment = (TextAnchor)3;
		component6.supportRichText = false;
		((Graphic)component6).raycastTarget = false;
		component6.resizeTextForBestFit = true;
		component6.resizeTextMinSize = 12;
		component6.resizeTextMaxSize = Plugin.FontSize.Value;
		component6.horizontalOverflow = (HorizontalWrapMode)0;
		component6.verticalOverflow = (VerticalWrapMode)0;
		Row row = new Row();
		row.Rect = component;
		row.Label = component6;
		row.Icon = component4;
		row.Group = val.GetComponent<CanvasGroup>();
		return row;
	}

	private void OnDestroy()
	{
		if (Object.op_Implicit((Object)(object)root))
		{
			Object.Destroy((Object)(object)((Component)root).gameObject);
		}
	}
}
[BepInPlugin("Outwardz.PickupNotificationAdjustment", "Pickup Notification Adjustment", "0.1.2")]
public sealed class Plugin : BaseUnityPlugin
{
	public const string Id = "Outwardz.PickupNotificationAdjustment";

	internal static Plugin Instance;

	internal static ConfigEntry<bool> Enabled;

	internal static ConfigEntry<int> Rows;

	internal static ConfigEntry<int> FontSize;

	internal static ConfigEntry<float> Duration;

	internal static ConfigEntry<float> RightMargin;

	internal static ConfigEntry<float> HeightOffset;

	internal static ConfigEntry<float> Width;

	private readonly Dictionary<CharacterUI, FeedView> feeds = new Dictionary<CharacterUI, FeedView>();

	private Harmony harmony;

	private bool loggedError;

	internal static readonly FieldInfo InventoryOwner = AccessTools.Field(typeof(CharacterInventory), "m_character");

	internal static bool GameplayReady
	{
		get
		{
			if (Object.op_Implicit((Object)(object)Instance) && Enabled.Value && Object.op_Implicit((Object)(object)NetworkLevelLoader.Instance) && NetworkLevelLoader.Instance.IsOverallLoadingDone)
			{
				return !NetworkLevelLoader.Instance.IsGameplayLoading;
			}
			return false;
		}
	}

	private void Awake()
	{
		//IL_0055: Unknown result type (might be due to invalid IL or missing references)
		//IL_005f: Expected O, but got Unknown
		//IL_0088: Unknown result type (might be due to invalid IL or missing references)
		//IL_0092: Expected O, but got Unknown
		//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c7: Expected O, but got Unknown
		//IL_00fb: Unknown result type (might be due to invalid IL or missing references)
		//IL_0105: Expected O, but got Unknown
		//IL_0139: Unknown result type (might be due to invalid IL or missing references)
		//IL_0143: Expected O, but got Unknown
		//IL_0177: Unknown result type (might be due to invalid IL or missing references)
		//IL_0181: Expected O, but got Unknown
		//IL_018c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0196: Expected O, but got Unknown
		Instance = this;
		Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Show item pickups above the hotbar.");
		Duration = ((BaseUnityPlugin)this).Config.Bind<float>("Display", "Display seconds", 5f, new ConfigDescription("How long each pickup stays visible.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 20f), new object[0]));
		Rows = ((BaseUnityPlugin)this).Config.Bind<int>("Display", "Visible items", 6, new ConfigDescription("Extra pickups wait their turn; they are not dropped.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 10), new object[0]));
		FontSize = ((BaseUnityPlugin)this).Config.Bind<int>("Display", "Text size", 18, new ConfigDescription("Item-name text size.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(12, 30), new object[0]));
		Width = ((BaseUnityPlugin)this).Config.Bind<float>("Position", "Panel width", 350f, new ConfigDescription("Width in UI units.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(220f, 650f), new object[0]));
		RightMargin = ((BaseUnityPlugin)this).Config.Bind<float>("Position", "Right margin", 24f, new ConfigDescription("Distance from the right edge of your player screen.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 500f), new object[0]));
		HeightOffset = ((BaseUnityPlugin)this).Config.Bind<float>("Position", "Vertical offset", -66.197f, new ConfigDescription("Adjust the position above the hotbar. Negative moves down; positive moves up.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(-100f, 500f), new object[0]));
		try
		{
			harmony = new Harmony("Outwardz.PickupNotificationAdjustment");
			harmony.PatchAll(typeof(Plugin).Assembly);
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Pickup feed 0.1.2 ready. Grouped quantities; bottom-right layout; local-player receipts only.");
		}
		catch (Exception ex)
		{
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
			Enabled.Value = false;
			((BaseUnityPlugin)this).Logger.LogError((object)("Pickup feed could not start; original notifications remain enabled. " + ex));
		}
	}

	internal static bool Local(Character c)
	{
		if (Object.op_Implicit((Object)(object)c) && c.IsLocalPlayer)
		{
			return Object.op_Implicit((Object)(object)c.CharacterUI);
		}
		return false;
	}

	internal static Character Owner(CharacterInventory inv)
	{
		//IL_000b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0011: Expected O, but got Unknown
		return (Character)InventoryOwner.GetValue(inv);
	}

	internal static bool Carried(Character c, ItemContainer box)
	{
		if (Local(c) && Object.op_Implicit((Object)(object)box))
		{
			if (!((Object)(object)box == (Object)(object)c.Inventory.Pouch))
			{
				if (c.Inventory.HasABag)
				{
					return (Object)(object)box == (Object)(object)c.Inventory.EquippedBag.Container;
				}
				return false;
			}
			return true;
		}
		return false;
	}

	internal static int Quantity(Item item)
	{
		if (!item.IsStackable)
		{
			return 1;
		}
		return Math.Max(1, item.RemainingAmount);
	}

	internal static void Report(Exception e)
	{
		if (Object.op_Implicit((Object)(object)Instance) && !Instance.loggedError)
		{
			Instance.loggedError = true;
			((BaseUnityPlugin)Instance).Logger.LogWarning((object)("Pickup display problem (gameplay unchanged): " + e));
		}
	}

	internal static bool Receipt(Character c, Item item, int amount, bool actual)
	{
		if (!GameplayReady || !Local(c) || !Object.op_Implicit((Object)(object)item) || item is Skill || item is ItemContainer || amount <= 0)
		{
			return false;
		}
		try
		{
			FeedView feed = Instance.GetFeed(c);
			feed.Receive(item.ItemID, item.Name, item.ItemIcon, amount, actual);
			return true;
		}
		catch (Exception e)
		{
			Report(e);
			return false;
		}
	}

	internal FeedView GetFeed(Character c)
	{
		if (!feeds.TryGetValue(c.CharacterUI, out var value) || !Object.op_Implicit((Object)(object)value))
		{
			value = ((Component)c.CharacterUI).gameObject.AddComponent<FeedView>();
			try
			{
				value.Initialize(c.CharacterUI);
			}
			catch
			{
				Object.Destroy((Object)(object)value);
				throw;
			}
			feeds[c.CharacterUI] = value;
		}
		return value;
	}

	internal static void WatchCurrency(Character c)
	{
		if (!GameplayReady || !Local(c))
		{
			return;
		}
		try
		{
			Instance.GetFeed(c).WatchSilver(c);
		}
		catch (Exception e)
		{
			Report(e);
		}
	}

	private void LateUpdate()
	{
		List<CharacterUI> list = new List<CharacterUI>();
		foreach (KeyValuePair<CharacterUI, FeedView> feed in feeds)
		{
			if (!Object.op_Implicit((Object)(object)feed.Key) || !Object.op_Implicit((Object)(object)feed.Value))
			{
				list.Add(feed.Key);
			}
		}
		foreach (CharacterUI item in list)
		{
			feeds.Remove(item);
		}
	}

	private void OnDestroy()
	{
		if (harmony != null)
		{
			harmony.UnpatchSelf();
		}
		foreach (FeedView value in feeds.Values)
		{
			if (Object.op_Implicit((Object)(object)value))
			{
				Object.Destroy((Object)(object)value);
			}
		}
		feeds.Clear();
		Instance = null;
	}
}
[HarmonyPatch(typeof(Item), "ChangeOwner", new Type[] { typeof(Character) })]
internal static class OwnershipHook
{
	internal sealed class Capture
	{
		public Character Owner;

		public int Amount;
	}

	private static void Prefix(Item __instance, Character __0, out Capture __state)
	{
		__state = null;
		try
		{
			if (Plugin.GameplayReady && Plugin.Local(__0) && Object.op_Implicit((Object)(object)__instance) && !((Object)(object)((EffectSynchronizer)__instance).OwnerCharacter == (Object)(object)__0) && !(__instance is Skill) && !(__instance is ItemContainer))
			{
				__state = new Capture
				{
					Owner = __0,
					Amount = Plugin.Quantity(__instance)
				};
			}
		}
		catch (Exception e)
		{
			Plugin.Report(e);
		}
	}

	private static void Postfix(Item __instance, Capture __state)
	{
		try
		{
			if (__state != null && Object.op_Implicit((Object)(object)__instance) && (Object)(object)((EffectSynchronizer)__instance).OwnerCharacter == (Object)(object)__state.Owner)
			{
				Plugin.Receipt(__state.Owner, __instance, __state.Amount, actual: true);
			}
		}
		catch (Exception e)
		{
			Plugin.Report(e);
		}
	}
}
[HarmonyPatch(typeof(CharacterInventory), "NotifyItemTake", new Type[]
{
	typeof(Item),
	typeof(int)
})]
internal static class NotificationHook
{
	private static bool Prefix(CharacterInventory __instance, Item __0, int __1)
	{
		try
		{
			return !Plugin.Receipt(Plugin.Owner(__instance), __0, __1, actual: false);
		}
		catch (Exception e)
		{
			Plugin.Report(e);
			return true;
		}
	}
}
[HarmonyPatch(typeof(CharacterInventory), "TakeItem", new Type[]
{
	typeof(Item),
	typeof(bool)
})]
internal static class AmmoHook
{
	private static void Prefix(CharacterInventory __instance, Item __0, bool __1)
	{
		try
		{
			Character val = Plugin.Owner(__instance);
			if (__0 is Currency)
			{
				Plugin.WatchCurrency(val);
			}
			Ammunition equippedAmmunition = __instance.GetEquippedAmmunition();
			if (Plugin.GameplayReady && Plugin.Local(val) && __1 && Object.op_Implicit((Object)(object)__0) && Object.op_Implicit((Object)(object)equippedAmmunition) && !((Object)(object)__0 == (Object)(object)equippedAmmunition) && __0.ItemID == ((Item)equippedAmmunition).ItemID && !((Object)(object)((EffectSynchronizer)__0).OwnerCharacter == (Object)(object)val))
			{
				Plugin.Instance.GetFeed(val).WatchAmmo(equippedAmmunition, Plugin.Quantity(__0));
			}
		}
		catch (Exception e)
		{
			Plugin.Report(e);
		}
	}
}
[HarmonyPatch(typeof(CharacterInventory), "AddMoney", new Type[]
{
	typeof(int),
	typeof(int),
	typeof(bool)
})]
internal static class SilverHook
{
	private static void Prefix(CharacterInventory __instance, int __1)
	{
		try
		{
			if (__1 > 0)
			{
				Plugin.WatchCurrency(Plugin.Owner(__instance));
			}
		}
		catch (Exception e)
		{
			Plugin.Report(e);
		}
	}
}
[HarmonyPatch(typeof(CharacterInventory), "TakeAllContent", new Type[]
{
	typeof(ItemContainer),
	typeof(bool)
})]
internal static class TakeAllSilverHook
{
	private static void Prefix(CharacterInventory __instance, ItemContainer __0)
	{
		try
		{
			if (Object.op_Implicit((Object)(object)__0) && __0.ContainedSilver > 0)
			{
				Plugin.WatchCurrency(Plugin.Owner(__instance));
			}
		}
		catch (Exception e)
		{
			Plugin.Report(e);
		}
	}
}