using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using UnityEngine; using UnityEngine.EventSystems; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("HammerCraftingMaterials")] [assembly: AssemblyDescription("Right-click any piece in the hammer build menu to pull its required materials from nearby chests directly into your inventory. Configurable search radius.")] [assembly: AssemblyProduct("HammerCraftingMaterials")] [assembly: AssemblyCompany("drummercraig")] [assembly: ComVisible(false)] [assembly: AssemblyFileVersion("1.1.3")] [assembly: AssemblyVersion("1.1.3.0")] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } } namespace HammerCraftingMaterials { [BepInPlugin("drummercraig.hammercraftingmaterials", "HammerCraftingMaterials", "1.1.3")] public class HammerCraftingMaterialsPlugin : BaseUnityPlugin { public const string PluginGUID = "drummercraig.hammercraftingmaterials"; public const string PluginName = "HammerCraftingMaterials"; public const string PluginVersion = "1.1.3"; internal static ManualLogSource Log; internal static ConfigEntry ChestSearchRadius; private readonly Harmony _harmony = new Harmony("drummercraig.hammercraftingmaterials"); private void Awake() { Log = ((BaseUnityPlugin)this).Logger; ChestSearchRadius = ((BaseUnityPlugin)this).Config.Bind("General", "ChestSearchRadius", 10f, "How far (meters) to search for nearby chests when pulling materials."); Log.LogInfo((object)string.Format("{0} v{1} loaded! Search radius: {2}m", "HammerCraftingMaterials", "1.1.3", ChestSearchRadius.Value)); ResilientPatcher.ApplyAll(_harmony, Assembly.GetExecutingAssembly(), Log); } private void Update() { //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Expected O, but got Unknown //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) try { if (!Input.GetMouseButtonDown(1)) { return; } Hud instance = Hud.instance; if ((Object)(object)instance == (Object)null) { return; } GameObject value = Traverse.Create((object)instance).Field("m_pieceSelectionWindow").GetValue(); if ((Object)(object)value == (Object)null || !value.activeSelf) { return; } bool forcePull = Input.GetKey((KeyCode)304) || Input.GetKey((KeyCode)303); PointerEventData val = new PointerEventData(EventSystem.current) { position = Vector2.op_Implicit(Input.mousePosition) }; List list = new List(); EventSystem current = EventSystem.current; if (current != null) { current.RaycastAll(val, list); } foreach (RaycastResult item in list) { RaycastResult current2 = item; PieceIconReference componentInParent = ((RaycastResult)(ref current2)).gameObject.GetComponentInParent(); if ((Object)(object)componentInParent?.Piece == (Object)null) { continue; } Player localPlayer = Player.m_localPlayer; if (!((Object)(object)localPlayer == (Object)null)) { string text = PullLogic.PullMaterialsForPiece(componentInParent.Piece, localPlayer, forcePull); Log.LogInfo((object)("[HCM] " + text)); MessageHud instance2 = MessageHud.instance; if (instance2 != null) { instance2.ShowMessage((MessageType)1, text, 0, (Sprite)null, false); } break; } } } catch (Exception arg) { Log.LogError((object)$"[HCM] Update failed: {arg}"); } } private void OnDestroy() { _harmony.UnpatchSelf(); } } public class PieceIconReference : MonoBehaviour { public Piece Piece; } public static class PullLogic { public static List GetNearbyContainers(Vector3 origin, float radius) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) List list = new List(); Collider[] array = Physics.OverlapSphere(origin, radius); for (int i = 0; i < array.Length; i++) { Container componentInParent = ((Component)array[i]).GetComponentInParent(); if (!((Object)(object)componentInParent == (Object)null) && !list.Contains(componentInParent) && !((Object)(object)((Component)componentInParent).GetComponent() != (Object)null) && Traverse.Create((object)componentInParent).Method("CheckAccess", new object[1] { Game.instance.GetPlayerProfile().GetPlayerID() }).GetValue()) { list.Add(componentInParent); } } return list; } public static string PullMaterialsForPiece(Piece piece, Player player, bool forcePull = false) { //IL_0041: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)piece == (Object)null) { return "No piece selected."; } if (piece.m_resources == null || piece.m_resources.Length == 0) { return Localization.instance.Localize(piece.m_name) + " has no material requirements."; } List nearbyContainers = GetNearbyContainers(((Component)player).transform.position, HammerCraftingMaterialsPlugin.ChestSearchRadius.Value); if (nearbyContainers.Count == 0) { return "No accessible chests found nearby."; } Dictionary dictionary = new Dictionary(); Requirement[] resources = piece.m_resources; foreach (Requirement val in resources) { if (!((Object)(object)val.m_resItem == (Object)null)) { string name = val.m_resItem.m_itemData.m_shared.m_name; int num = ((Humanoid)player).GetInventory().CountItems(name, -1, true); int num2 = val.m_amount - num; if (num2 > 0 || forcePull) { dictionary[name] = (forcePull ? val.m_amount : num2); } } } if (dictionary.Count == 0) { return "You already have all required materials!"; } Dictionary dictionary2 = new Dictionary(); Inventory inventory = ((Humanoid)player).GetInventory(); foreach (Container item in nearbyContainers) { if (dictionary.Count == 0) { break; } Inventory inventory2 = item.GetInventory(); bool flag = false; foreach (KeyValuePair item2 in dictionary.ToList()) { string itemName = item2.Key; int value = item2.Value; int num3 = inventory2.CountItems(itemName, -1, true); if (num3 <= 0) { continue; } int num4 = Mathf.Min(num3, value); ItemData val2 = ((IEnumerable)inventory2.GetAllItems()).FirstOrDefault((Func)((ItemData val3) => val3.m_shared.m_name == itemName)); if (val2 == null || (!inventory.HaveEmptySlot() && inventory.FindFreeStackSpace(itemName, 1f) <= 0)) { continue; } inventory2.RemoveItem(itemName, num4, -1, true); flag = true; string text = (((Object)(object)val2.m_dropPrefab != (Object)null) ? ((Object)val2.m_dropPrefab).name : val2.m_shared.m_name); if (inventory.AddItem(text, num4, val2.m_quality, val2.m_variant, player.GetPlayerID(), player.GetPlayerName(), false) != null) { dictionary2.TryGetValue(itemName, out var value2); dictionary2[itemName] = value2 + num4; dictionary[itemName] -= num4; if (dictionary[itemName] <= 0) { dictionary.Remove(itemName); } } else { inventory2.AddItem(text, num4, val2.m_quality, val2.m_variant, 0L, "", false); } } if (flag) { Traverse.Create((object)item).Method("Save", Array.Empty()).GetValue(); } } if (dictionary2.Count == 0) { return "None of the required materials were found in nearby chests."; } string text2 = string.Join(", ", dictionary2.Select((KeyValuePair kvp) => $"{kvp.Value}x {Localization.instance.Localize(kvp.Key)}")); if (dictionary.Count == 0) { if (!forcePull) { return "Pulled: " + text2; } return "Force pulled: " + text2; } string text3 = string.Join(", ", dictionary.Select((KeyValuePair kvp) => $"{kvp.Value}x {Localization.instance.Localize(kvp.Key)}")); return "Pulled: " + text2 + " | Still missing: " + text3; } } [HarmonyPatch(typeof(Hud), "UpdatePieceList", new Type[] { typeof(Player), typeof(Vector2Int), typeof(PieceCategory), typeof(bool) })] public static class Hud_UpdatePieceList_Patch { private static readonly FieldInfo? _pieceIconsField = AccessTools.Field(typeof(Hud), "m_pieceIcons"); private static FieldInfo? _goField; [HarmonyPostfix] private static void Postfix(Hud __instance) { try { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { return; } PieceTable value = Traverse.Create((object)localPlayer).Field("m_buildPieces").GetValue(); if ((Object)(object)value == (Object)null) { return; } List piecesInSelectedCategory = value.GetPiecesInSelectedCategory(); if (piecesInSelectedCategory == null || piecesInSelectedCategory.Count == 0) { return; } if (!(_pieceIconsField?.GetValue(__instance) is IList list)) { HammerCraftingMaterialsPlugin.Log.LogWarning((object)"[HCM] m_pieceIcons field not found on Hud"); return; } for (int i = 0; i < list.Count && i < piecesInSelectedCategory.Count; i++) { object obj = list[i]; if (obj == null) { continue; } if (_goField == null) { _goField = AccessTools.Field(obj.GetType(), "m_go"); } object? obj2 = _goField?.GetValue(obj); GameObject val = (GameObject)((obj2 is GameObject) ? obj2 : null); if (val != null) { Piece val2 = piecesInSelectedCategory[i]; if (!((Object)(object)val2 == (Object)null)) { (val.GetComponent() ?? val.AddComponent()).Piece = val2; } } } } catch (Exception arg) { HammerCraftingMaterialsPlugin.Log.LogError((object)$"[HCM] UpdatePieceList postfix failed: {arg}"); } } } internal static class ResilientPatcher { internal static void ApplyAll(Harmony harmony, Assembly assembly, ManualLogSource log) { if (harmony == null || assembly == null) { return; } int num = 0; int num2 = 0; List list = null; Type[] typesFromAssembly = AccessTools.GetTypesFromAssembly(assembly); foreach (Type type in typesFromAssembly) { try { List list2 = harmony.CreateClassProcessor(type).Patch(); if (list2 != null && list2.Count > 0) { num++; if (log != null) { log.LogDebug((object)$"[HCM] patched {type.Name} ({list2.Count} method(s))."); } } } catch (Exception ex) { num2++; (list ?? (list = new List())).Add(type.Name); if (log != null) { log.LogWarning((object)("[HCM] SKIP patch class " + type.Name + ": " + ex.Message)); } } } if (num2 == 0) { if (log != null) { log.LogInfo((object)$"[HCM] patches: {num} applied, 0 skipped."); } } else if (log != null) { log.LogWarning((object)(string.Format("[HCM] patches: {0} applied, {1} skipped ({2}). ", num, num2, string.Join(", ", list)) + "A skipped patch usually means a Valheim update changed a hooked method; that feature is disabled but the mod is still running.")); } } } }