Decompiled source of ShiftFillFromStorage v1.0.0

ShiftFillFromStorage.dll

Decompiled 11 hours ago
using System;
using System.Collections;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.6.2", FrameworkDisplayName = ".NET Framework 4.6.2")]
[assembly: AssemblyCompany("ShiftFillFromStorage")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("ShiftFillFromStorage")]
[assembly: AssemblyTitle("ShiftFillFromStorage")]
[assembly: AssemblyVersion("1.0.0.0")]
[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 ShiftFillFromStorage
{
	[BepInPlugin("pieter.valheim.shiftfillfromstorage", "Shift Fill From Storage", "1.0.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class Plugin : BaseUnityPlugin
	{
		public const string Guid = "pieter.valheim.shiftfillfromstorage";

		public const string Name = "Shift Fill From Storage";

		public const string Version = "1.0.0";

		private const string LcfspGuid = "LuminairPrime.valheim.localcraftfromstorageprime";

		private const string BetterSmelterGuid = "com.alan.bettersmelter";

		private const int MaxItemsPerFill = 200;

		private static Plugin _instance;

		private static bool _filling;

		private static FieldInfo _inSwitch;

		private static PropertyInfo _busy;

		private static PropertyInfo _settings;

		private static PropertyInfo _dispatchReturned;

		private static PropertyInfo _enabled;

		private static PropertyInfo _fuelFromStorage;

		private static PropertyInfo _oreFromStorage;

		private static PropertyInfo _refuelDistance;

		private static MethodInfo _start;

		private static MethodInfo _fuelName;

		private static MethodInfo _fuelRoom;

		private static MethodInfo _oreRoom;

		private static MethodInfo _carriesOre;

		private static MethodInfo _suppliesAny;

		private static ConstructorInfo _operation;

		private static ConstructorInfo _fuelSupply;

		private static ConstructorInfo _oreSupply;

		private static MethodInfo _betterFuelPrefix;

		private static MethodInfo _betterOrePrefix;

		private static ConfigEntry<bool> _fermenterFromStorage;

		private static readonly ConditionalWeakTable<object, Fermenter> FermenterTargets = new ConditionalWeakTable<object, Fermenter>();

		private static MethodInfo _spare;

		private static MethodInfo _updateCover;

		private static FieldInfo _hasRoof;

		private static FieldInfo _exposed;

		private static bool Busy => (bool)_busy.GetValue(null);

		private static object Settings => _settings.GetValue(null);

		private void Awake()
		{
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Expected O, but got Unknown
			_instance = this;
			try
			{
				Resolve();
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("Disabled, Local Craft From Storage Prime internals not found: " + ex.Message));
				return;
			}
			Harmony val = new Harmony("pieter.valheim.shiftfillfromstorage");
			MethodInfo methodInfo = AccessTools.Method(typeof(Smelter), "OnAddFuel", (Type[])null, (Type[])null);
			MethodInfo methodInfo2 = AccessTools.Method(typeof(Smelter), "OnAddOre", (Type[])null, (Type[])null);
			_betterFuelPrefix = AccessTools.Method(AccessTools.TypeByName("Smelter_OnAddFuel_BulkPatch"), "Prefix", (Type[])null, (Type[])null);
			_betterOrePrefix = AccessTools.Method(AccessTools.TypeByName("Smelter_OnAddOre_BulkPatch"), "Prefix", (Type[])null, (Type[])null);
			if (_betterFuelPrefix != null)
			{
				val.Unpatch((MethodBase)methodInfo, _betterFuelPrefix);
				val.Patch((MethodBase)methodInfo, Hook("BetterFuelWrapper"), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			}
			if (_betterOrePrefix != null)
			{
				val.Unpatch((MethodBase)methodInfo2, _betterOrePrefix);
				val.Patch((MethodBase)methodInfo2, Hook("BetterOreWrapper"), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			}
			val.Patch((MethodBase)methodInfo, (HarmonyMethod)null, Hook("AddFuelPostfix"), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			val.Patch((MethodBase)methodInfo2, (HarmonyMethod)null, Hook("AddOrePostfix"), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			_fermenterFromStorage = ((BaseUnityPlugin)this).Config.Bind<bool>("Fermenter", "Fill from storage", true, "Interacting with an empty fermenter while carrying no mead base takes one from a nearby chest.");
			try
			{
				PatchFermenter(val);
			}
			catch (Exception ex2)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("Fermenter from storage disabled: " + ex2.Message));
			}
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Shift-filling smelters now also pulls from nearby chests; fermenters fill from chests.");
		}

		private static HarmonyMethod Hook(string name)
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Expected O, but got Unknown
			return new HarmonyMethod(typeof(Plugin).GetMethod(name, BindingFlags.Static | BindingFlags.NonPublic));
		}

		private static void Resolve()
		{
			Type type = L("SupplyAction");
			_inSwitch = Need<FieldInfo>(AccessTools.Field(type, "InSwitch"), "InSwitch");
			_fuelName = Need<MethodInfo>(AccessTools.Method(type, "FuelName", (Type[])null, (Type[])null), "FuelName");
			_fuelRoom = Need<MethodInfo>(AccessTools.Method(type, "SmelterFuelRoom", (Type[])null, (Type[])null), "SmelterFuelRoom");
			_oreRoom = Need<MethodInfo>(AccessTools.Method(type, "SmelterOreRoom", (Type[])null, (Type[])null), "SmelterOreRoom");
			_carriesOre = Need<MethodInfo>(AccessTools.Method(type, "CarriesOre", (Type[])null, (Type[])null), "CarriesOre");
			_suppliesAny = Need<MethodInfo>(AccessTools.Method(type, "SuppliesAny", (Type[])null, (Type[])null), "SuppliesAny");
			Type type2 = L("ContainerAccessService");
			_busy = Need<PropertyInfo>(AccessTools.Property(type2, "Busy"), "Busy");
			_start = Need<MethodInfo>(AccessTools.Method(type2, "Start", (Type[])null, (Type[])null), "Start");
			Type type3 = L("SupplyPaymentOperation");
			_operation = Need<ConstructorInfo>(AccessTools.Constructor(type3, new Type[4]
			{
				typeof(Player),
				L("SupplyTarget"),
				typeof(Vector3),
				typeof(float)
			}, false), "SupplyPaymentOperation()");
			_dispatchReturned = Need<PropertyInfo>(AccessTools.Property(type3, "DispatchReturned"), "DispatchReturned");
			_fuelSupply = Need<ConstructorInfo>(AccessTools.Constructor(L("SmelterFuelSupply"), new Type[2]
			{
				typeof(Smelter),
				typeof(string)
			}, false), "SmelterFuelSupply()");
			_oreSupply = Need<ConstructorInfo>(AccessTools.Constructor(L("SmelterOreSupply"), new Type[1] { typeof(Smelter) }, false), "SmelterOreSupply()");
			_settings = Need<PropertyInfo>(AccessTools.Property(L("Plugin"), "Settings"), "Settings");
			Type type4 = L("RuntimeSettings");
			_enabled = Need<PropertyInfo>(AccessTools.Property(type4, "Enabled"), "Enabled");
			_fuelFromStorage = Need<PropertyInfo>(AccessTools.Property(type4, "SmelterFuelFromStorage"), "SmelterFuelFromStorage");
			_oreFromStorage = Need<PropertyInfo>(AccessTools.Property(type4, "SmelterOreFromStorage"), "SmelterOreFromStorage");
			_refuelDistance = Need<PropertyInfo>(AccessTools.Property(type4, "RefuelDistance"), "RefuelDistance");
			static Type L(string name)
			{
				return Need<Type>(AccessTools.TypeByName("LocalCraftFromStoragePrime." + name), name);
			}
			static T Need<T>(T member, string what) where T : class
			{
				return member ?? throw new MissingMemberException(what);
			}
		}

		private static bool ShiftHeld()
		{
			if (!ZInput.GetKey((KeyCode)304, true))
			{
				return ZInput.GetKey((KeyCode)303, true);
			}
			return true;
		}

		private static bool Setting(PropertyInfo p)
		{
			object settings = Settings;
			if (settings != null)
			{
				return (bool)p.GetValue(settings);
			}
			return false;
		}

		private static string FuelName(Smelter s)
		{
			return _fuelName.Invoke(null, new object[1] { s.m_fuelItem }) as string;
		}

		private static bool FuelRoom(Smelter s)
		{
			return (bool)_fuelRoom.Invoke(null, new object[1] { s });
		}

		private static bool OreRoom(Smelter s)
		{
			return (bool)_oreRoom.Invoke(null, new object[1] { s });
		}

		private static bool CarriesFuel(Player p, Smelter s)
		{
			string text = FuelName(s);
			if (text != null)
			{
				return ((Humanoid)p).GetInventory().HaveItem(text, true);
			}
			return false;
		}

		private static bool CarriesOre(Player p, Smelter s)
		{
			return (bool)_carriesOre.Invoke(null, new object[2]
			{
				s,
				((Humanoid)p).GetInventory()
			});
		}

		private static int FuelSpace(Smelter s)
		{
			ZNetView component = ((Component)s).GetComponent<ZNetView>();
			if ((Object)(object)component == (Object)null || !component.IsValid())
			{
				return 0;
			}
			return Mathf.Max(0, s.m_maxFuel - Mathf.CeilToInt(component.GetZDO().GetFloat(ZDOVars.s_fuel, 0f)));
		}

		private static int OreSpace(Smelter s)
		{
			ZNetView component = ((Component)s).GetComponent<ZNetView>();
			if ((Object)(object)component == (Object)null || !component.IsValid())
			{
				return 0;
			}
			return Mathf.Max(0, s.m_maxOre - component.GetZDO().GetInt(ZDOVars.s_queued, 0));
		}

		private static bool BetterFuelWrapper(Switch sw, Humanoid user, ItemData item, Smelter __instance, ref bool __result)
		{
			Player val = (Player)(object)((user is Player) ? user : null);
			if (val != null && item == null && !CarriesFuel(val, __instance))
			{
				return true;
			}
			return CallBetter(_betterFuelPrefix, sw, user, item, __instance, ref __result);
		}

		private static bool BetterOreWrapper(Switch sw, Humanoid user, ItemData item, Smelter __instance, ref bool __result)
		{
			Player val = (Player)(object)((user is Player) ? user : null);
			if (val != null && item == null && !CarriesOre(val, __instance))
			{
				return true;
			}
			return CallBetter(_betterOrePrefix, sw, user, item, __instance, ref __result);
		}

		private static bool CallBetter(MethodInfo prefix, Switch sw, Humanoid user, ItemData item, Smelter smelter, ref bool result)
		{
			object[] array = new object[5] { sw, user, item, smelter, result };
			bool result2 = (bool)prefix.Invoke(null, array);
			result = (bool)array[4];
			return result2;
		}

		private static void AddFuelPostfix(Humanoid user, ItemData item, Smelter __instance)
		{
			TryFill(user, item, __instance, fuel: true);
		}

		private static void AddOrePostfix(Humanoid user, ItemData item, Smelter __instance)
		{
			TryFill(user, item, __instance, fuel: false);
		}

		private static void TryFill(Humanoid user, ItemData item, Smelter smelter, bool fuel)
		{
			if (_filling || item != null || !ShiftHeld() || !(bool)_inSwitch.GetValue(null))
			{
				return;
			}
			Player val = (Player)(object)((user is Player) ? user : null);
			if (val == null || (Object)(object)val != (Object)(object)Player.m_localPlayer || !Setting(_enabled))
			{
				return;
			}
			if (fuel)
			{
				if (!Setting(_fuelFromStorage) || FuelName(smelter) == null || CarriesFuel(val, smelter))
				{
					return;
				}
			}
			else if (!Setting(_oreFromStorage) || CarriesOre(val, smelter) || !(bool)_suppliesAny.Invoke(null, new object[2] { smelter, Settings }))
			{
				return;
			}
			int num = (fuel ? FuelSpace(smelter) : OreSpace(smelter));
			if (num > 1)
			{
				((MonoBehaviour)_instance).StartCoroutine(Fill(val, smelter, fuel, Mathf.Min(num - 1, 200)));
			}
		}

		private static void PatchFermenter(Harmony harmony)
		{
			Type type = AccessTools.TypeByName("LocalCraftFromStoragePrime.SmelterOreSupply");
			_spare = AccessTools.Method(AccessTools.TypeByName("LocalCraftFromStoragePrime.SupplyTarget"), "Spare", (Type[])null, (Type[])null) ?? throw new MissingMemberException("SupplyTarget.Spare");
			_updateCover = AccessTools.Method(typeof(Fermenter), "UpdateCover", new Type[2]
			{
				typeof(float),
				typeof(bool)
			}, (Type[])null) ?? throw new MissingMemberException("Fermenter.UpdateCover");
			_hasRoof = AccessTools.Field(typeof(Fermenter), "m_hasRoof") ?? throw new MissingMemberException("Fermenter.m_hasRoof");
			_exposed = AccessTools.Field(typeof(Fermenter), "m_exposed") ?? throw new MissingMemberException("Fermenter.m_exposed");
			Redirect(AccessTools.PropertyGetter(type, "Station"), "StationPrefix");
			Redirect(AccessTools.PropertyGetter(type, "MissingMessage"), "MissingPrefix");
			Redirect(AccessTools.Method(type, "CanReceive", (Type[])null, (Type[])null), "CanReceivePrefix");
			Redirect(AccessTools.Method(type, "Carried", (Type[])null, (Type[])null), "CarriedPrefix");
			Redirect(AccessTools.Method(type, "Pick", (Type[])null, (Type[])null), "PickPrefix");
			Redirect(AccessTools.Method(type, "Send", (Type[])null, (Type[])null), "SendPrefix");
			Redirect(AccessTools.Method(type, "ShowFeedback", (Type[])null, (Type[])null), "FeedbackPrefix");
			harmony.Patch((MethodBase)AccessTools.Method(typeof(Fermenter), "Interact", (Type[])null, (Type[])null), Hook("FermenterInteractPrefix"), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			void Redirect(MethodInfo method, string hook)
			{
				if (method == null)
				{
					throw new MissingMemberException("SmelterOreSupply member for " + hook);
				}
				harmony.Patch((MethodBase)method, Hook(hook), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			}
		}

		private static bool FermenterEmpty(Fermenter f)
		{
			ZNetView val = (((Object)(object)f == (Object)null) ? null : ((Component)f).GetComponent<ZNetView>());
			if ((Object)(object)val != (Object)null && val.IsValid())
			{
				return val.GetZDO().GetInt(ZDOVars.s_content, 0) == 0;
			}
			return false;
		}

		private static ItemData CarriedMeadBase(Fermenter f, Inventory inventory)
		{
			foreach (ItemConversion item2 in f.m_conversion)
			{
				ItemData item = inventory.GetItem(item2.m_from.m_itemData.m_shared.m_name, -1, false);
				if (item != null)
				{
					return item;
				}
			}
			return null;
		}

		private static bool FermenterInteractPrefix(Fermenter __instance, Humanoid user, bool hold, ref bool __result)
		{
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0119: Unknown result type (might be due to invalid IL or missing references)
			if (!hold && _fermenterFromStorage.Value)
			{
				Player val = (Player)(object)((user is Player) ? user : null);
				if (val != null && !((Object)(object)val != (Object)(object)Player.m_localPlayer))
				{
					if (!Setting(_enabled) || Busy || !FermenterEmpty(__instance))
					{
						return true;
					}
					if (!PrivateArea.CheckAccess(((Component)__instance).transform.position, 0f, false, false))
					{
						return true;
					}
					_updateCover.Invoke(__instance, new object[2] { 0f, true });
					if (!(bool)_hasRoof.GetValue(__instance) || (bool)_exposed.GetValue(__instance))
					{
						return true;
					}
					if (CarriedMeadBase(__instance, ((Humanoid)val).GetInventory()) != null)
					{
						return true;
					}
					object obj = _oreSupply.Invoke(new object[1]);
					FermenterTargets.Add(obj, __instance);
					float num = (float)_refuelDistance.GetValue(Settings);
					_start.Invoke(null, new object[1] { _operation.Invoke(new object[4]
					{
						val,
						obj,
						((Component)__instance).transform.position,
						num
					}) });
					__result = true;
					return false;
				}
			}
			return true;
		}

		private static bool StationPrefix(object __instance, ref Component __result)
		{
			if (!FermenterTargets.TryGetValue(__instance, out var value))
			{
				return true;
			}
			__result = (Component)(object)value;
			return false;
		}

		private static bool MissingPrefix(object __instance, ref string __result)
		{
			if (!FermenterTargets.TryGetValue(__instance, out var _))
			{
				return true;
			}
			__result = "$msg_noprocessableitems";
			return false;
		}

		private static bool CanReceivePrefix(object __instance, ref bool __result)
		{
			if (!FermenterTargets.TryGetValue(__instance, out var value))
			{
				return true;
			}
			__result = FermenterEmpty(value);
			return false;
		}

		private static bool CarriedPrefix(object __instance, Inventory inventory, ref bool __result)
		{
			if (!FermenterTargets.TryGetValue(__instance, out var value))
			{
				return true;
			}
			__result = CarriedMeadBase(value, inventory) != null;
			return false;
		}

		private static bool PickPrefix(object __instance, object[] __args, ref ItemData __result)
		{
			if (!FermenterTargets.TryGetValue(__instance, out var value))
			{
				return true;
			}
			__result = null;
			foreach (ItemConversion item in value.m_conversion)
			{
				object? obj = _spare.Invoke(null, new object[3]
				{
					__args[0],
					item.m_from.m_itemData.m_shared.m_name,
					__args[1]
				});
				ItemData val = (ItemData)((obj is ItemData) ? obj : null);
				if ((Object)(object)val?.m_dropPrefab != (Object)null)
				{
					__result = val;
					break;
				}
			}
			return false;
		}

		private static bool SendPrefix(object __instance, ZNetView view, string prefab, bool cheated)
		{
			if (!FermenterTargets.TryGetValue(__instance, out var _))
			{
				return true;
			}
			view.InvokeRPC("RPC_AddItem", new object[2]
			{
				StringExtensionMethods.GetStableHashCode(prefab),
				cheated
			});
			return false;
		}

		private static bool FeedbackPrefix(object __instance, Player player, string name)
		{
			if (!FermenterTargets.TryGetValue(__instance, out var _))
			{
				return true;
			}
			((Character)player).Message((MessageType)2, "$msg_added " + name, 0, (Sprite)null, false);
			return false;
		}

		private static IEnumerator Fill(Player player, Smelter smelter, bool fuel, int remaining)
		{
			_filling = true;
			try
			{
				while (Busy)
				{
					yield return null;
				}
				Vector3 origin = ((Component)smelter).transform.position;
				float radius = (float)_refuelDistance.GetValue(Settings);
				while (remaining > 0 && (Object)(object)smelter != (Object)null && (Object)(object)player == (Object)(object)Player.m_localPlayer && !((Character)player).IsDead() && (fuel ? FuelRoom(smelter) : OreRoom(smelter)) && !(fuel ? CarriesFuel(player, smelter) : CarriesOre(player, smelter)))
				{
					object obj = (fuel ? _fuelSupply.Invoke(new object[2]
					{
						smelter,
						FuelName(smelter)
					}) : _oreSupply.Invoke(new object[1] { smelter }));
					object operation = _operation.Invoke(new object[4] { player, obj, origin, radius });
					_start.Invoke(null, new object[1] { operation });
					yield return null;
					while (Busy)
					{
						yield return null;
					}
					if ((bool)_dispatchReturned.GetValue(operation))
					{
						remaining--;
						continue;
					}
					break;
				}
			}
			finally
			{
				_filling = false;
			}
		}
	}
}