Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of BuildMaterials v1.0.1
BuildMaterials.dll
Decompiled a day agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyCompany("BuildMaterials")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("BuildMaterials")] [assembly: AssemblyTitle("BuildMaterials")] [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 BuildMaterials { [BepInPlugin("com.dreich.buildmaterials", "Build Materials", "1.0.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] public class BuildMaterialsPlugin : BaseUnityPlugin { public const string PluginGuid = "com.dreich.buildmaterials"; public const string PluginName = "Build Materials"; public const string PluginVersion = "1.0.0"; internal static ConfigEntry<bool> Enabled; internal static ConfigEntry<int> Multiplier; internal static ConfigEntry<bool> ShowMessage; internal static ConfigEntry<bool> UseDverger; internal static ConfigEntry<bool> UseCraftyBoxes; internal static ConfigEntry<bool> DebugLog; internal static ConfigEntry<bool> PullEvenIfCarrying; internal static ConfigEntry<bool> PlaySound; internal static ManualLogSource Log; private void Awake() { //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Expected O, but got Unknown //IL_017d: Unknown result type (might be due to invalid IL or missing references) Log = ((BaseUnityPlugin)this).Logger; Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Right-click a piece in the build menu to pull the materials out of nearby storage (Dvergr Automation and/or AzuCraftyBoxes)."); Multiplier = ((BaseUnityPlugin)this).Config.Bind<int>("General", "Multiplier", 1, new ConfigDescription("How many pieces' worth of materials one right-click pulls (see PullEvenIfCarrying).", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 100), Array.Empty<object>())); PullEvenIfCarrying = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "PullEvenIfCarrying", true, "On: every right-click pulls a full piece's worth of materials (times Multiplier), even if you already carry them. Off: only pull what you're short of, so you end up carrying enough for Multiplier pieces."); PlaySound = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "PlaySound", true, "Play the build menu's select sound (the one a left-click makes) when a right-click pulls materials."); ShowMessage = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "ShowMessage", true, "Show a message in the top left corner listing the materials you pulled."); UseDverger = ((BaseUnityPlugin)this).Config.Bind<bool>("Storage", "UseDvergrAutomation", true, "Pull from the chests Dvergr Automation's AutoSorter links."); UseCraftyBoxes = ((BaseUnityPlugin)this).Config.Bind<bool>("Storage", "UseAzuCraftyBoxes", true, "Pull from the containers AzuCraftyBoxes reaches (same range and per-container item rules it uses for building)."); DebugLog = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "LogRightClicks", true, "Write a line to the BepInEx log for every right-click in the build menu, saying what happened. Handy if right-click seems to do nothing."); bool flag = Dverger.Init(); bool flag2 = CraftyBoxes.Init(); ((BaseUnityPlugin)this).Logger.LogInfo((object)("Dvergr Automation: " + (flag ? "found" : "not found") + ", AzuCraftyBoxes: " + (flag2 ? "found" : "not found"))); if (!flag && !flag2) { ((BaseUnityPlugin)this).Logger.LogError((object)"Neither Dvergr Automation (tested 0.12.4) nor AzuCraftyBoxes (tested 1.8.28) was found, or their internals changed; right-click pulling is turned off."); } else { new Harmony("com.dreich.buildmaterials").PatchAll(typeof(BuildMaterialsPlugin).Assembly); } } private void Update() { RightClick.Poll(); Pull.Tick(); } } [HarmonyPatch(typeof(Hud), "OnRightClickPiece")] internal static class HudOnRightClickPiecePatch { private static bool Prefix(Hud __instance, UIInputHandler ih) { if (RightClick.HandledThisFrame) { return false; } return !RightClick.Handle(__instance, ih, "icon callback"); } } internal static class RightClick { private const BindingFlags AnyInstance = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; private static readonly MethodInfo GetSelectedGridMethod = typeof(Hud).GetMethod("GetSelectedGrid", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[1] { typeof(UIInputHandler) }, null); private static readonly FieldInfo HoveredPieceField = typeof(Hud).GetField("m_hoveredPiece", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); private static readonly FieldInfo BuildPiecesField = typeof(Player).GetField("m_buildPieces", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); private static readonly MethodInfo GetRightItemMethod = typeof(Humanoid).GetMethod("GetRightItem", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null); private const string HammerName = "$item_hammer"; private static int handledFrame = -1; internal static bool HandledThisFrame => handledFrame == Time.frameCount; internal static void Poll() { if (!HandledThisFrame && BuildMaterialsPlugin.Enabled.Value && ZInput.GetMouseButtonDown(1) && Hud.IsPieceSelectionVisible()) { Hud instance = Hud.instance; if (!((Object)(object)instance == (Object)null)) { Handle(instance, null, "mouse"); } } } internal static bool Handle(Hud hud, UIInputHandler ih, string source) { if (!BuildMaterialsPlugin.Enabled.Value) { return false; } Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null || ((Character)localPlayer).IsDead()) { return false; } if (!HoldingHammer(localPlayer, out var held)) { Debug("right-click (" + source + ") ignored: holding '" + held + "', not the hammer"); return false; } Piece val = FindPiece(hud, ih, localPlayer); if ((Object)(object)val == (Object)null) { Debug("right-click (" + source + ") ignored: no build piece under the cursor"); return false; } handledFrame = Time.frameCount; Debug("right-click (" + source + ") on " + val.m_name); if (!localPlayer.HaveRequirements(val, (RequirementMode)0)) { Pull.Say(localPlayer, "You can't build " + val.m_name + " right now"); return true; } PlaySelectSound(hud); Pull.Start(localPlayer, val); return true; } private static void PlaySelectSound(Hud hud) { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) if (!BuildMaterialsPlugin.PlaySound.Value || hud.m_selectItemEffect == null) { return; } try { hud.m_selectItemEffect.Create(((Component)hud).transform.position, Quaternion.identity, (Transform)null, 1f, -1, default(ZDOID)); } catch (Exception ex) { Debug("playing the select sound failed: " + ex.Message); } } private static Piece FindPiece(Hud hud, UIInputHandler ih, Player player) { //IL_0066: 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) //IL_0093: Unknown result type (might be due to invalid IL or missing references) Piece val = (Piece)((HoveredPieceField == null) ? null : /*isinst with value type is only supported in some contexts*/); if ((Object)(object)val != (Object)null) { return val; } if ((Object)(object)ih == (Object)null || GetSelectedGridMethod == null || BuildPiecesField == null) { return null; } Vector2Int val2 = (Vector2Int)GetSelectedGridMethod.Invoke(hud, new object[1] { ih }); if (((Vector2Int)(ref val2)).x == -1) { return null; } object? value = BuildPiecesField.GetValue(player); PieceTable val3 = (PieceTable)((value is PieceTable) ? value : null); if (!((Object)(object)val3 == (Object)null)) { return val3.GetPiece(val2); } return null; } private static bool HoldingHammer(Player player, out string held) { held = "nothing"; if (GetRightItemMethod == null) { held = "(GetRightItem not found)"; return false; } object? obj = GetRightItemMethod.Invoke(player, null); ItemData val = (ItemData)((obj is ItemData) ? obj : null); if (val == null || val.m_shared == null) { return false; } held = val.m_shared.m_name; return held == "$item_hammer"; } private static void Debug(string message) { if (BuildMaterialsPlugin.DebugLog.Value) { BuildMaterialsPlugin.Log.LogInfo((object)message); } } } internal static class Pull { private struct Need { internal string Name; internal ItemDrop Drop; internal int Amount; } private const float Patience = 5f; private static Piece pendingPiece; private static float pendingUntil; internal static void Start(Player player, Piece piece) { pendingPiece = null; if (!TryPull(player, piece, lastTry: false)) { pendingPiece = piece; pendingUntil = Time.time + 5f; Say(player, "Fetching materials for " + piece.m_name + " from storage..."); } } internal static void Tick() { if ((Object)(object)pendingPiece == (Object)null) { return; } Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null || ((Character)localPlayer).IsDead()) { pendingPiece = null; return; } bool flag = Time.time >= pendingUntil; if (TryPull(localPlayer, pendingPiece, flag) || flag) { pendingPiece = null; } } private static bool TryPull(Player player, Piece piece, bool lastTry) { //IL_01ff: Unknown result type (might be due to invalid IL or missing references) //IL_0206: Unknown result type (might be due to invalid IL or missing references) //IL_020b: Unknown result type (might be due to invalid IL or missing references) //IL_0210: Unknown result type (might be due to invalid IL or missing references) //IL_0215: Unknown result type (might be due to invalid IL or missing references) //IL_021c: Unknown result type (might be due to invalid IL or missing references) int multiplier = Mathf.Max(1, BuildMaterialsPlugin.Multiplier.Value); Requirement[] resources = piece.m_resources; if (resources == null) { return true; } Inventory inventory = ((Humanoid)player).GetInventory(); List<Need> list = Needs(inventory, resources, multiplier); if (list.Count == 0) { Say(player, "You are already carrying the materials for " + piece.m_name); return true; } bool flag = BuildMaterialsPlugin.UseDverger.Value && Dverger.Available; bool num = BuildMaterialsPlugin.UseCraftyBoxes.Value && CraftyBoxes.Available && CraftyBoxes.PullingAllowed(); List<Container> list2 = (flag ? Dverger.Pool(player) : new List<Container>()); List<object> list3 = (num ? CraftyBoxes.Nearby(player) : new List<object>()); if (list2.Count == 0 && list3.Count == 0) { Say(player, "No storage in range to pull from"); return true; } if (list2.Count > 0 && !lastTry && Dverger.CheckStorage(player, list2, resources, DvergerMultiplier(inventory, resources, multiplier)) == Dverger.Readiness.Pending) { return false; } List<string> list4 = new List<string>(); bool flag2 = false; bool flag3 = false; foreach (Need item in list) { int num2 = Mathf.Min(item.Amount, Room(inventory, item.Drop.m_itemData)); if (num2 < item.Amount) { flag2 = true; } if (num2 <= 0) { continue; } int num3 = 0; if (list2.Count > 0) { num3 += Dverger.SpendReserve(item.Name, num2); if (num3 < num2) { num3 += Dverger.RemoveFromContainers(list2, item.Name, num2 - num3); } } if (num3 < num2 && list3.Count > 0) { num3 += CraftyBoxes.Take(list3, item.Name, ((Object)item.Drop).name, num2 - num3); } if (num3 < num2) { flag3 = true; } if (num3 > 0) { int num4 = Add(inventory, ((Component)item.Drop).gameObject, item.Drop.m_itemData, num3); if (num4 > 0) { Transform transform = ((Component)player).transform; ItemDrop.DropItem(item.Drop.m_itemData, num4, transform.position + transform.forward + Vector3.up, transform.rotation); flag2 = true; } list4.Add(num3 + " " + item.Name); } } if (list4.Count == 0) { Say(player, flag2 ? "Your inventory is full" : "Nothing could be taken from storage"); } else { Say(player, "Pulled " + string.Join(", ", list4.ToArray()) + (flag2 ? " (inventory full)" : (flag3 ? " (storage ran short)" : ""))); } return true; } private static int DvergerMultiplier(Inventory inventory, Requirement[] requirements, int multiplier) { if (!BuildMaterialsPlugin.PullEvenIfCarrying.Value) { return multiplier; } int num = 0; foreach (Requirement val in requirements) { if (val != null && !((Object)(object)val.m_resItem == (Object)null)) { int amount = val.GetAmount(0); if (amount > 0) { int num2 = inventory.CountItems(val.m_resItem.m_itemData.m_shared.m_name, -1, true); num = Mathf.Max(num, (num2 + amount - 1) / amount); } } } return multiplier + num; } private static List<Need> Needs(Inventory inventory, Requirement[] requirements, int multiplier) { Dictionary<string, int> dictionary = new Dictionary<string, int>(); List<Need> list = new List<Need>(); foreach (Requirement val in requirements) { if (val == null || (Object)(object)val.m_resItem == (Object)null) { continue; } int num = val.GetAmount(0) * multiplier; if (num > 0) { string name = val.m_resItem.m_itemData.m_shared.m_name; if (dictionary.TryGetValue(name, out var value)) { Need value2 = list[value]; value2.Amount += num; list[value] = value2; } else { dictionary[name] = list.Count; list.Add(new Need { Name = name, Drop = val.m_resItem, Amount = num }); } } } if (BuildMaterialsPlugin.PullEvenIfCarrying.Value) { return list; } for (int num2 = list.Count - 1; num2 >= 0; num2--) { Need value3 = list[num2]; value3.Amount -= inventory.CountItems(value3.Name, -1, true); if (value3.Amount <= 0) { list.RemoveAt(num2); } else { list[num2] = value3; } } return list; } private static int Room(Inventory inventory, ItemData itemData) { int num = Mathf.Max(1, itemData.m_shared.m_maxStackSize); return inventory.FindFreeStackSpace(itemData.m_shared.m_name, (float)Game.m_worldLevel) + inventory.GetEmptySlots() * num; } private static int Add(Inventory inventory, GameObject prefab, ItemData itemData, int amount) { int num = Mathf.Max(1, itemData.m_shared.m_maxStackSize); while (amount > 0) { int num2 = Mathf.Min(num, amount); while (num2 > 0 && !inventory.CanAddItem(prefab, num2)) { num2 = ((num2 > 1) ? (num2 / 2) : 0); } if (num2 == 0 || !inventory.AddItem(prefab, num2)) { break; } amount -= num2; } return amount; } internal static void Say(Player player, string message) { if (BuildMaterialsPlugin.ShowMessage.Value) { ((Character)player).Message((MessageType)1, message, 0, (Sprite)null, false); } } } internal static class Dverger { internal enum Readiness { Covered, Pending, Short } internal const string Guid = "MidngightsFX.DvergerAutomation"; internal const string TestedVersion = "0.12.4"; private const BindingFlags AnyStatic = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic; private static MethodInfo nearPoint; private static MethodInfo removeFrom; private static MethodInfo checkStorage; private static MethodInfo spend; internal static bool Available { get; private set; } internal static bool Init() { Type type = AccessTools.TypeByName("DvergerAutomation.ContainerNetwork"); Type type2 = AccessTools.TypeByName("DvergerAutomation.CraftFromStoragePatches"); Type type3 = AccessTools.TypeByName("DvergerAutomation.StorageOwnership"); Type type4 = AccessTools.TypeByName("DvergerAutomation.StorageReserve"); if (type == null || type2 == null || type3 == null || type4 == null) { return false; } nearPoint = type.GetMethod("GetContainersNearPoint", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[1] { typeof(Vector3) }, null); removeFrom = type2.GetMethod("RemoveFromContainers", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[4] { typeof(List<Container>), typeof(string), typeof(int), typeof(int) }, null); checkStorage = type3.GetMethod("CheckStorage", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[6] { typeof(Player), typeof(List<Container>), typeof(Requirement[]), typeof(CraftingStation), typeof(int), typeof(int) }, null); spend = type4.GetMethod("Spend", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[3] { typeof(string), typeof(int), typeof(int) }, null); Available = nearPoint != null && removeFrom != null && checkStorage != null && spend != null; if (!Available) { BuildMaterialsPlugin.Log.LogWarning((object)"Dvergr Automation is installed but its internals don't match 0.12.4; not using it."); } return Available; } internal static List<Container> Pool(Player player) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) if (nearPoint.Invoke(null, new object[1] { ((Component)player).transform.position }) is List<Container> collection) { return new List<Container>(collection); } return new List<Container>(); } internal static Readiness CheckStorage(Player player, List<Container> pool, Requirement[] requirements, int multiplier) { return (Readiness)Convert.ToInt32(checkStorage.Invoke(null, new object[6] { player, pool, requirements, null, 0, multiplier })); } internal static int SpendReserve(string name, int amount) { return (int)spend.Invoke(null, new object[3] { name, amount, -1 }); } internal static int RemoveFromContainers(List<Container> pool, string name, int amount) { return (int)removeFrom.Invoke(null, new object[4] { pool, name, amount, -1 }); } } internal static class CraftyBoxes { internal const string Guid = "Azumatt.AzuCraftyBoxes"; internal const string TestedVersion = "1.8.28"; private const BindingFlags AnyStatic = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic; private static MethodInfo getNearby; private static MethodInfo canBePulled; private static MethodInfo shouldPrevent; private static FieldInfo rangeField; private static MethodInfo itemCount; private static MethodInfo process; private static MethodInfo prefabName; internal static bool Available { get; private set; } internal static bool Init() { Type type = AccessTools.TypeByName("AzuCraftyBoxes.API"); Type type2 = AccessTools.TypeByName("AzuCraftyBoxes.AzuCraftyBoxesPlugin"); Type type3 = AccessTools.TypeByName("AzuCraftyBoxes.Util.Functions.MiscFunctions"); Type type4 = AccessTools.TypeByName("AzuCraftyBoxes.IContainers.IContainer"); if (type == null || type2 == null || type4 == null) { return false; } MethodInfo[] methods = type.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); foreach (MethodInfo methodInfo in methods) { if (methodInfo.Name == "GetNearbyContainers" && methodInfo.IsGenericMethodDefinition && methodInfo.GetParameters().Length == 2) { try { getNearby = methodInfo.MakeGenericMethod(typeof(Player)); } catch (ArgumentException) { } } } canBePulled = type.GetMethod("CanItemBePulled", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[2] { typeof(string), typeof(string) }, null); shouldPrevent = ((type3 == null) ? null : type3.GetMethod("ShouldPrevent", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null)); rangeField = type2.GetField("mRange", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); itemCount = type4.GetMethod("ItemCount", new Type[1] { typeof(string) }); process = type4.GetMethod("ProcessContainerInventory", new Type[3] { typeof(string), typeof(int), typeof(int) }); prefabName = type4.GetMethod("GetPrefabName", Type.EmptyTypes); Available = getNearby != null && rangeField != null && itemCount != null && process != null && prefabName != null; if (!Available) { BuildMaterialsPlugin.Log.LogWarning((object)"AzuCraftyBoxes is installed but its internals don't match 1.8.28; not using it."); } return Available; } internal static bool PullingAllowed() { if (shouldPrevent == null) { return true; } try { return !(bool)shouldPrevent.Invoke(null, null); } catch (Exception) { return true; } } internal static List<object> Nearby(Player player) { List<object> list = new List<object>(); if (!(rangeField.GetValue(null) is ConfigEntry<float> val)) { return list; } if (!(getNearby.Invoke(null, new object[2] { player, val.Value }) is IEnumerable enumerable)) { return list; } foreach (object item in enumerable) { if (item != null) { list.Add(item); } } return list; } internal static int Take(List<object> pool, string name, string itemPrefab, int amount) { int num = 0; foreach (object item in pool) { if (num >= amount) { break; } try { if (!(canBePulled != null)) { goto IL_0066; } string text = prefabName.Invoke(item, null) as string; if ((bool)canBePulled.Invoke(null, new object[2] { text, itemPrefab })) { goto IL_0066; } goto end_IL_0020; IL_0066: int num2 = (int)itemCount.Invoke(item, new object[1] { name }); if (num2 > 0) { int num3 = Mathf.Min(num2, amount - num); process.Invoke(item, new object[3] { name, 0, num3 }); int num4 = (int)itemCount.Invoke(item, new object[1] { name }); num += Mathf.Max(0, Mathf.Min(num3, num2 - num4)); } end_IL_0020:; } catch (Exception ex) { BuildMaterialsPlugin.Log.LogWarning((object)("AzuCraftyBoxes: taking " + name + " from a container failed: " + ex.Message)); } } return num; } } }