Decompiled source of ChestSalvage v1.0.0

ChestSalvage.dll

Decompiled a day ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using TMPro;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyVersion("0.0.0.0")]
namespace ChestSalvage;

public static class SalvageMath
{
	public static int Refund(int baseCost, int[] upgradeCosts, float baseRate, float upgradeRate)
	{
		int num = 0;
		foreach (int val in upgradeCosts)
		{
			num += Math.Max(val, 0);
		}
		return Scale(Math.Max(baseCost, 0), baseRate) + Scale(num, upgradeRate);
	}

	public static bool PaidOnCraft(bool upgraderResource)
	{
		return !upgraderResource;
	}

	public static int[] PaidUpgradeLevels(int quality, int maxQuality, bool upgraderResource)
	{
		int num = (upgraderResource ? (Math.Max(maxQuality, 1) + 1) : 2);
		int num2 = (upgraderResource ? quality : Math.Min(quality, maxQuality));
		if (num2 < num)
		{
			return new int[0];
		}
		int[] array = new int[num2 - num + 1];
		for (int i = 0; i < array.Length; i++)
		{
			array[i] = num + i;
		}
		return array;
	}

	private static int Scale(int amount, float rate)
	{
		float num = Math.Max(0f, Math.Min(rate, 1f));
		return (int)Math.Ceiling(Math.Round((float)amount * num, 4));
	}
}
[BepInPlugin("com.parallel53.chestsalvage", "Chest Salvage", "1.0.0")]
public class ChestSalvagePlugin : BaseUnityPlugin
{
	private const float RefreshSeconds = 0.5f;

	private const int MaxPreviewLines = 8;

	private static readonly FieldRef<InventoryGui, Container> CurrentContainer = AccessTools.FieldRefAccess<InventoryGui, Container>("m_currentContainer");

	private static readonly HashSet<ItemType> GearTypes = new HashSet<ItemType>
	{
		(ItemType)3,
		(ItemType)14,
		(ItemType)22,
		(ItemType)4,
		(ItemType)5,
		(ItemType)6,
		(ItemType)7,
		(ItemType)11,
		(ItemType)12,
		(ItemType)17,
		(ItemType)18,
		(ItemType)19,
		(ItemType)24
	};

	private static ConfigEntry<float> baseRate;

	private static ConfigEntry<float> upgradeRate;

	private static ConfigEntry<bool> requireStation;

	private static Button button;

	private static TMP_Text label;

	private static float nextRefresh;

	private void Awake()
	{
		//IL_002e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0038: Expected O, but got Unknown
		//IL_006b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0075: Expected O, but got Unknown
		//IL_009f: Unknown result type (might be due to invalid IL or missing references)
		baseRate = ((BaseUnityPlugin)this).Config.Bind<float>("Salvage", "BaseReturnRate", 0.5f, new ConfigDescription("Share of the crafting cost returned, rounded up.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
		upgradeRate = ((BaseUnityPlugin)this).Config.Bind<float>("Salvage", "UpgradeReturnRate", 1f, new ConfigDescription("Share of the upgrade costs returned, rounded up.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
		requireStation = ((BaseUnityPlugin)this).Config.Bind<bool>("Salvage", "RequireStation", true, "Only salvage an item when the crafting station its recipe uses is in range.");
		new Harmony("com.parallel53.chestsalvage").PatchAll(typeof(ChestSalvagePlugin));
	}

	[HarmonyPostfix]
	[HarmonyPatch(typeof(InventoryGui), "Awake")]
	private static void AddButton(InventoryGui __instance)
	{
		//IL_0031: Unknown result type (might be due to invalid IL or missing references)
		//IL_003b: Expected O, but got Unknown
		//IL_004c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0056: Expected O, but got Unknown
		//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c3: Expected O, but got Unknown
		//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d3: Expected O, but got Unknown
		//IL_00da: 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_00f5: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ff: Unknown result type (might be due to invalid IL or missing references)
		//IL_0110: Unknown result type (might be due to invalid IL or missing references)
		//IL_011a: 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_0135: Unknown result type (might be due to invalid IL or missing references)
		Button takeAllButton = __instance.m_takeAllButton;
		button = Object.Instantiate<Button>(takeAllButton, ((Component)takeAllButton).transform.parent);
		((Object)button).name = "ChestSalvageButton";
		button.onClick = new ButtonClickedEvent();
		((UnityEvent)button.onClick).AddListener(new UnityAction(OnClick));
		UIGamePad[] componentsInChildren = ((Component)button).GetComponentsInChildren<UIGamePad>(true);
		for (int i = 0; i < componentsInChildren.Length; i++)
		{
			Object.DestroyImmediate((Object)(object)componentsInChildren[i]);
		}
		Localize[] componentsInChildren2 = ((Component)button).GetComponentsInChildren<Localize>(true);
		for (int i = 0; i < componentsInChildren2.Length; i++)
		{
			Object.DestroyImmediate((Object)(object)componentsInChildren2[i]);
		}
		label = ((Component)button).GetComponentInChildren<TMP_Text>(true);
		RectTransform val = (RectTransform)((Component)takeAllButton).transform;
		RectTransform val2 = (RectTransform)((Component)button).transform;
		val2.anchorMin = new Vector2(0.5f, val2.anchorMin.y);
		val2.anchorMax = new Vector2(0.5f, val2.anchorMax.y);
		val2.pivot = new Vector2(0.5f, val2.pivot.y);
		val2.anchoredPosition = new Vector2(0f, val.anchoredPosition.y);
	}

	[HarmonyPostfix]
	[HarmonyPatch(typeof(InventoryGui), "Update")]
	private static void RefreshButton(InventoryGui __instance)
	{
		if (!((Object)(object)button == (Object)null) && !(Time.unscaledTime < nextRefresh))
		{
			nextRefresh = Time.unscaledTime + 0.5f;
			Container val = CurrentContainer.Invoke(__instance);
			bool flag = (Object)(object)val != (Object)null && (Object)(object)((Component)val).GetComponent<TombStone>() == (Object)null;
			if (((Component)button).gameObject.activeSelf != flag)
			{
				((Component)button).gameObject.SetActive(flag);
			}
			if (flag)
			{
				label.text = "Salvage (" + Salvageable(val.GetInventory()).Count + ")";
			}
		}
	}

	private static List<ItemData> Salvageable(Inventory inventory)
	{
		return inventory.GetAllItems().Where(CanSalvage).ToList();
	}

	private static bool CanSalvage(ItemData item)
	{
		if (IsGear(item))
		{
			return StationInRange(item);
		}
		return false;
	}

	private static bool IsGear(ItemData item)
	{
		//IL_000b: Unknown result type (might be due to invalid IL or missing references)
		if (!GearTypes.Contains(item.m_shared.m_itemType) || item.m_shared.m_questItem)
		{
			return false;
		}
		Recipe recipe = ObjectDB.instance.GetRecipe(item);
		if ((Object)(object)recipe != (Object)null && recipe.m_enabled && recipe.m_amount == 1)
		{
			return !recipe.m_requireOnlyOneIngredient;
		}
		return false;
	}

	private static CraftingStation RequiredStation(ItemData item)
	{
		return ObjectDB.instance.GetRecipe(item).GetRequiredStation(1);
	}

	private static bool StationInRange(ItemData item)
	{
		//IL_002c: Unknown result type (might be due to invalid IL or missing references)
		CraftingStation val = RequiredStation(item);
		if (requireStation.Value && !((Object)(object)val == (Object)null))
		{
			return (Object)(object)CraftingStation.HaveBuildStationInRange(val.m_name, ((Component)Player.m_localPlayer).transform.position) != (Object)null;
		}
		return true;
	}

	private static string NothingToSalvageMessage(Inventory inventory)
	{
		string[] array = (from item in inventory.GetAllItems()
			where IsGear(item) && !StationInRange(item)
			select Localization.instance.Localize(RequiredStation(item).m_name)).Distinct().ToArray();
		if (array.Length != 0)
		{
			return "Salvaging this gear needs a nearby " + string.Join(" or ", array);
		}
		return "Nothing to salvage in this chest";
	}

	private static Dictionary<ItemDrop, int> Refunds(List<ItemData> items)
	{
		Dictionary<ItemDrop, int> dictionary = new Dictionary<ItemDrop, int>();
		foreach (ItemData item in items)
		{
			Requirement[] resources = ObjectDB.instance.GetRecipe(item).m_resources;
			foreach (Requirement val in resources)
			{
				if (!((Object)(object)val.m_resItem == (Object)null))
				{
					bool upgraderResource = val.m_upgraderResource;
					int[] upgradeCosts = ((IEnumerable<int>)SalvageMath.PaidUpgradeLevels(item.m_quality, item.m_shared.m_maxQuality, upgraderResource)).Select((Func<int, int>)val.GetAmount).ToArray();
					int num = SalvageMath.Refund(SalvageMath.PaidOnCraft(upgraderResource) ? val.GetAmount(1) : 0, upgradeCosts, baseRate.Value, upgradeRate.Value);
					if (num > 0)
					{
						dictionary.TryGetValue(val.m_resItem, out var value);
						dictionary[val.m_resItem] = value + num;
					}
				}
			}
		}
		return dictionary;
	}

	private static void OnClick()
	{
		//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c6: Expected O, but got Unknown
		//IL_00c6: Expected O, but got Unknown
		//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00cb: Expected O, but got Unknown
		Container container = CurrentContainer.Invoke(InventoryGui.instance);
		if ((Object)(object)container == (Object)null || UnifiedPopup.IsVisible())
		{
			return;
		}
		List<ItemData> list = Salvageable(container.GetInventory());
		if (list.Count == 0)
		{
			((Character)Player.m_localPlayer).Message((MessageType)2, NothingToSalvageMessage(container.GetInventory()), 0, (Sprite)null, false);
			return;
		}
		UnifiedPopup.Push((PopupBase)new YesNoPopup("Salvage " + list.Count + ((list.Count == 1) ? " item" : " items") + "?", Preview(Refunds(list)), (PopupButtonCallback)delegate
		{
			UnifiedPopup.Pop();
			Salvage(container);
		}, new PopupButtonCallback(UnifiedPopup.Pop), false, false));
	}

	private static string Preview(Dictionary<ItemDrop, int> refunds)
	{
		List<string> list = (from pair in refunds
			orderby pair.Value descending
			select pair.Value + " " + Localization.instance.Localize(pair.Key.m_itemData.m_shared.m_name)).ToList();
		if (list.Count == 0)
		{
			return "These items return no materials and will be destroyed.";
		}
		int num = list.Count - 8;
		IEnumerable<string> source = list.Take(8);
		string text = string.Join("\n", source.ToArray());
		return "You get:\n" + text + ((num > 0) ? ("\n+ " + num + " more") : "");
	}

	private static void Salvage(Container container)
	{
		if ((Object)(object)container == (Object)null || (Object)(object)CurrentContainer.Invoke(InventoryGui.instance) != (Object)(object)container)
		{
			return;
		}
		Inventory inventory = container.GetInventory();
		List<ItemData> list = Salvageable(inventory);
		Dictionary<ItemDrop, int> dictionary = Refunds(list);
		Debug.Log((object)("Chest Salvage: salvaged " + string.Join(", ", list.Select((ItemData i) => i.m_shared.m_name + " q" + i.m_quality).ToArray()) + " for " + string.Join(", ", dictionary.Select((KeyValuePair<ItemDrop, int> r) => r.Value + " " + ((Object)r.Key).name).ToArray())));
		foreach (ItemData item in list)
		{
			inventory.RemoveItem(item);
		}
		foreach (KeyValuePair<ItemDrop, int> item2 in dictionary)
		{
			Give(item2.Key, item2.Value, inventory);
		}
		nextRefresh = 0f;
	}

	private static void Give(ItemDrop item, int amount, Inventory chest)
	{
		//IL_0089: Unknown result type (might be due to invalid IL or missing references)
		//IL_0094: Unknown result type (might be due to invalid IL or missing references)
		//IL_0099: Unknown result type (might be due to invalid IL or missing references)
		//IL_009e: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
		Player localPlayer = Player.m_localPlayer;
		int num = Mathf.Max(item.m_itemData.m_shared.m_maxStackSize, 1);
		while (amount > 0)
		{
			int num2 = Mathf.Min(amount, num);
			amount -= num2;
			if (chest.CanAddItem(((Component)item).gameObject, num2))
			{
				chest.AddItem(((Component)item).gameObject, num2);
			}
			else if (((Humanoid)localPlayer).GetInventory().CanAddItem(((Component)item).gameObject, num2))
			{
				((Humanoid)localPlayer).GetInventory().AddItem(((Component)item).gameObject, num2);
			}
			else
			{
				ItemDrop.DropItem(item.m_itemData.Clone(), num2, ((Component)localPlayer).transform.position + ((Component)localPlayer).transform.forward + Vector3.up, Quaternion.identity);
			}
		}
	}
}