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 MagicHammer v1.0.1
BepInEx/plugins/MagicHammer/MagicHammer.dll
Decompiled 3 days agousing System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using BepInEx; using BepInEx.Configuration; using HarmonyLib; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: AssemblyVersion("0.0.0.0")] namespace MagicHammer; internal sealed class Glow : IDisposable { private static readonly FieldInfo Equipment = AccessTools.Field(typeof(Humanoid), "m_visEquipment"); private static readonly FieldInfo RightVisual = AccessTools.Field(typeof(VisEquipment), "m_rightItemInstance"); private readonly Dictionary<Renderer, Material[]> originals = new Dictionary<Renderer, Material[]>(); private readonly List<Material> copies = new List<Material>(); private GameObject lightObject; internal GameObject Target { get; private set; } internal static GameObject HeldVisual(Player player) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Expected O, but got Unknown object value = Equipment.GetValue(player); if (value != null) { return (GameObject)RightVisual.GetValue(value); } return null; } internal Glow(GameObject target, Plugin plugin) { //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Expected O, but got Unknown //IL_00b5: 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_0183: Unknown result type (might be due to invalid IL or missing references) //IL_018a: Expected O, but got Unknown //IL_01c1: Unknown result type (might be due to invalid IL or missing references) //IL_01cd: Unknown result type (might be due to invalid IL or missing references) //IL_01ec: Unknown result type (might be due to invalid IL or missing references) //IL_01f8: Unknown result type (might be due to invalid IL or missing references) Target = target; try { Color val = default(Color); if (!ColorUtility.TryParseHtmlString(plugin.GlowColour.Value, ref val)) { val = new Color(0.4f, 0.8f, 1f); } if (plugin.GlowIntensity.Value > 0f) { lightObject = new GameObject("MagicHammer_Glow"); ((Object)lightObject).hideFlags = (HideFlags)52; lightObject.transform.SetParent(target.transform, false); lightObject.transform.localPosition = new Vector3(0f, 0.15f, 0f); Light val2 = lightObject.AddComponent<Light>(); val2.type = (LightType)2; val2.color = val; val2.range = plugin.GlowRange.Value; val2.intensity = plugin.GlowIntensity.Value; val2.shadows = (LightShadows)0; } if (plugin.Emission.Value <= 0f) { return; } Renderer[] componentsInChildren = target.GetComponentsInChildren<Renderer>(true); foreach (Renderer val3 in componentsInChildren) { Material[] sharedMaterials = val3.sharedMaterials; Material[] array = (Material[])sharedMaterials.Clone(); bool flag = false; for (int j = 0; j < sharedMaterials.Length; j++) { Material val4 = sharedMaterials[j]; if (!((Object)(object)val4 == (Object)null) && (val4.HasProperty("_EmissionColor") || val4.HasProperty("_EmissiveColor"))) { Material val5 = new Material(val4); copies.Add(val5); ((Object)val5).hideFlags = (HideFlags)52; val5.EnableKeyword("_EMISSION"); if (val5.HasProperty("_EmissionColor")) { val5.SetColor("_EmissionColor", val * plugin.Emission.Value); } if (val5.HasProperty("_EmissiveColor")) { val5.SetColor("_EmissiveColor", val * plugin.Emission.Value); } array[j] = val5; flag = true; } } if (flag) { originals[val3] = sharedMaterials; val3.sharedMaterials = array; } } } catch { Dispose(); throw; } } public void Dispose() { if ((Object)(object)lightObject != (Object)null) { Object.Destroy((Object)(object)lightObject); } foreach (KeyValuePair<Renderer, Material[]> original in originals) { if ((Object)(object)original.Key != (Object)null) { original.Key.sharedMaterials = original.Value; } } foreach (Material copy in copies) { if ((Object)(object)copy != (Object)null) { Object.Destroy((Object)(object)copy); } } originals.Clear(); copies.Clear(); } } internal static class Hooks { internal static readonly MethodInfo CountOriginal = AccessTools.Method(typeof(Inventory), "CountItems", new Type[3] { typeof(string), typeof(int), typeof(bool) }, (Type[])null); internal static readonly MethodInfo HaveOriginal = AccessTools.Method(typeof(Inventory), "HaveItem", new Type[2] { typeof(string), typeof(bool) }, (Type[])null); internal static Func<Inventory, string, int, bool, bool, int> CraftUiCount; internal static int Count(Inventory inventory, string name, int quality, bool worldLevel) { if (!((Object)(object)Plugin.Instance == (Object)null)) { return Plugin.Instance.Count(inventory, name, quality, worldLevel); } return inventory.CountItems(name, quality, worldLevel); } internal static bool Have(Inventory inventory, string name, bool worldLevel) { return Count(inventory, name, -1, worldLevel) > 0; } internal static int UiCount(Inventory inventory, string name, int quality, bool worldLevel, bool craft) { if (!craft) { return Count(inventory, name, quality, worldLevel); } return inventory.CountItems(name, quality, worldLevel); } internal static int CombinedUiCount(Inventory inventory, string name, int quality, bool worldLevel, bool craft) { if (!craft) { return Count(inventory, name, quality, worldLevel); } return CraftUiCount(inventory, name, quality, worldLevel, arg5: true); } internal static bool Build(Player player, Piece piece) { if (!((Object)(object)Plugin.Instance == (Object)null)) { return Plugin.Instance.Build(player, piece); } return player.TryPlacePiece(piece); } internal static void Consume(Player player, Requirement[] requirements, int quality, int itemQuality, int multiplier) { if ((Object)(object)Plugin.Instance == (Object)null) { player.ConsumeResources(requirements, quality, itemQuality, multiplier); } else { Plugin.Instance.ConsumePaid(player, requirements, quality, itemQuality, multiplier); } } internal static bool Calls(CodeInstruction instruction, MethodInfo method) { if (instruction.opcode == OpCodes.Call || instruction.opcode == OpCodes.Callvirt) { return object.Equals(instruction.operand, method); } return false; } } [HarmonyPatch] internal static class RequirementsPatch { private static MethodBase TargetMethod() { return AccessTools.Method(typeof(Player), "HaveRequirements", new Type[2] { typeof(Piece), typeof(RequirementMode) }, (Type[])null); } private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions) { List<CodeInstruction> list = instructions.ToList(); int num = 0; int num2 = 0; foreach (CodeInstruction item in list) { if (Hooks.Calls(item, Hooks.CountOriginal)) { item.opcode = OpCodes.Call; item.operand = AccessTools.Method(typeof(Hooks), "Count", (Type[])null, (Type[])null); num++; } else if (Hooks.Calls(item, Hooks.HaveOriginal)) { item.opcode = OpCodes.Call; item.operand = AccessTools.Method(typeof(Hooks), "Have", (Type[])null, (Type[])null); num2++; } } if (num != 1 || num2 != 1) { throw new InvalidOperationException("Unsupported HaveRequirements IL layout."); } return list; } } [HarmonyPatch] internal static class PlacementPatch { private static MethodBase TargetMethod() { return AccessTools.Method(typeof(Player), "UpdatePlacement", new Type[2] { typeof(bool), typeof(float) }, (Type[])null); } private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions) { List<CodeInstruction> list = instructions.ToList(); MethodInfo method = AccessTools.Method(typeof(Player), "TryPlacePiece", new Type[1] { typeof(Piece) }, (Type[])null); MethodInfo method2 = AccessTools.Method(typeof(Player), "ConsumeResources", new Type[4] { typeof(Requirement[]), typeof(int), typeof(int), typeof(int) }, (Type[])null); int num = 0; int num2 = 0; foreach (CodeInstruction item in list) { if (Hooks.Calls(item, method)) { item.opcode = OpCodes.Call; item.operand = AccessTools.Method(typeof(Hooks), "Build", (Type[])null, (Type[])null); num++; } else if (Hooks.Calls(item, method2)) { item.opcode = OpCodes.Call; item.operand = AccessTools.Method(typeof(Hooks), "Consume", (Type[])null, (Type[])null); num2++; } } if (num != 1 || num2 != 1) { throw new InvalidOperationException("Unsupported UpdatePlacement IL layout."); } return list; } } [HarmonyAfter(new string[] { "au.tyvalheim.reliablecrafting" })] [HarmonyPatch] internal static class RequirementUiPatch { private static MethodBase TargetMethod() { return AccessTools.Method(typeof(InventoryGui), "SetupRequirement", (Type[])null, (Type[])null); } private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions) { //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Expected O, but got Unknown List<CodeInstruction> list = new List<CodeInstruction>(); int num = 0; foreach (CodeInstruction instruction in instructions) { MethodInfo methodInfo = instruction.operand as MethodInfo; if (methodInfo != null && methodInfo.DeclaringType.FullName == "ReliableCrafting.Hooks" && methodInfo.Name == "UiCount") { Hooks.CraftUiCount = (Func<Inventory, string, int, bool, bool, int>)Delegate.CreateDelegate(typeof(Func<Inventory, string, int, bool, bool, int>), methodInfo); instruction.opcode = OpCodes.Call; instruction.operand = AccessTools.Method(typeof(Hooks), "CombinedUiCount", (Type[])null, (Type[])null); num++; } else if (Hooks.Calls(instruction, Hooks.CountOriginal)) { CodeInstruction val = new CodeInstruction(OpCodes.Ldarg_3, (object)null); val.labels.AddRange(instruction.labels); val.blocks.AddRange(instruction.blocks); instruction.labels.Clear(); instruction.blocks.Clear(); list.Add(val); instruction.opcode = OpCodes.Call; instruction.operand = AccessTools.Method(typeof(Hooks), "UiCount", (Type[])null, (Type[])null); num++; } list.Add(instruction); } if (num != 1) { throw new InvalidOperationException("Unsupported build requirement UI IL layout."); } return list; } } [HarmonyPatch(typeof(Inventory), "ItemCheated", new Type[] { typeof(Requirement[]), typeof(int), typeof(bool) })] internal static class CheatedPatch { private static void Postfix(Inventory __instance, ref bool __result) { if ((Object)(object)Plugin.Instance != (Object)null && Plugin.Instance.ReservedCheated(__instance)) { __result = true; } } } [HarmonyPatch(typeof(Player), "PlacePiece", new Type[] { typeof(Piece), typeof(Vector3), typeof(Quaternion), typeof(bool), typeof(bool) })] internal static class SpawnBoundaryPatch { private static void Prefix(Player __instance) { if ((Object)(object)Plugin.Instance != (Object)null) { Plugin.Instance.PlacementEntered(__instance); } } } internal sealed class ReferenceSnapshot<T> where T : class { private readonly T[] items; private readonly int[] counts; internal ReferenceSnapshot(IEnumerable<T> source, Func<T, int> getCount) { items = source.ToArray(); counts = items.Select(getCount).ToArray(); } internal void Restore(IList<T> destination, Action<T, int> setCount) { for (int i = 0; i < items.Length; i++) { setCount(items[i], counts[i]); } destination.Clear(); T[] array = items; foreach (T item in array) { destination.Add(item); } } } internal sealed class Need { internal string Key; internal int Amount; } internal sealed class Stock { internal string Key; internal int Available; internal object Tag; } internal sealed class Withdrawal { internal Stock Stock; internal int Amount; } internal static class Planning { internal static List<Withdrawal> Create(IEnumerable<Need> needs, IList<Stock> stocks) { List<Withdrawal> list = new List<Withdrawal>(); foreach (IGrouping<string, Need> group in from n in needs where n.Amount > 0 group n by n.Key) { int num = group.Sum((Need n) => n.Amount); foreach (Stock item in stocks.Where((Stock s) => s.Key == group.Key)) { int num2 = Math.Min(num, Math.Max(0, item.Available)); if (num2 > 0) { list.Add(new Withdrawal { Stock = item, Amount = num2 }); } num -= num2; if (num == 0) { break; } } if (num > 0) { return null; } } return list; } internal static HashSet<string> ParseList(string text) { return new HashSet<string>(from s in text.Split(new char[1] { ',' }) select s.Trim() into s where s.Length > 0 select s, StringComparer.OrdinalIgnoreCase); } internal static Dictionary<string, string> ReadSection(IEnumerable<string> lines, string section) { Dictionary<string, string> dictionary = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); bool flag = false; foreach (string line in lines) { string text = line.Trim(); if (text.Length == 0 || text.StartsWith("#") || text.StartsWith(";")) { continue; } if (text.StartsWith("[") && text.EndsWith("]")) { flag = string.Equals(text.Substring(1, text.Length - 2).Trim(), section, StringComparison.OrdinalIgnoreCase); continue; } int num = text.IndexOf('='); if (flag && num > 0) { dictionary[text.Substring(0, num).Trim()] = text.Substring(num + 1).Split('#', ';')[0].Trim(); } } return dictionary; } } [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInPlugin("au.tyvalheim.magichammer", "Magic Hammer", "1.0.1")] public sealed class Plugin : BaseUnityPlugin { private sealed class Source { internal Inventory Inventory; internal Container Chest; internal ItemData Item; } private sealed class Receipt { internal List<Withdrawal> Plan; internal Dictionary<Inventory, ReferenceSnapshot<ItemData>> Backups = new Dictionary<Inventory, ReferenceSnapshot<ItemData>>(); internal HashSet<Inventory> Touched = new HashSet<Inventory>(); internal bool Cheated; internal bool PlacementEntered; } public const string Id = "au.tyvalheim.magichammer"; internal static Plugin Instance; internal ConfigEntry<bool> Enabled; internal ConfigEntry<bool> GlowEnabled; internal ConfigEntry<bool> LogUse; internal ConfigEntry<float> Radius; internal ConfigEntry<float> GlowIntensity; internal ConfigEntry<float> GlowRange; internal ConfigEntry<float> Emission; internal ConfigEntry<string> GlowColour; private ConfigEntry<bool> inventoryFirst; private ConfigEntry<bool> privateChests; private ConfigEntry<bool> reload; private ConfigEntry<bool> catalogue; private ConfigEntry<bool> messages; private ConfigEntry<float> refresh; private ConfigEntry<int> minimumStock; private ConfigEntry<string> chestPrefabs; private ConfigEntry<string> prohibitedList; private HashSet<string> allowedChests; private HashSet<string> prohibited; private Dictionary<string, int> reserves = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase); private Container[] cachedChests = (Container[])(object)new Container[0]; private List<Stock> cachedStocks; private int stockFrame = -1; private float nextChestScan; private float nextFileCheck; private DateTime configStamp; private ObjectDB cataloguedDb; private Harmony harmony; private bool faulted; private bool glowFaulted; private Glow glow; private Receipt activeReceipt; private Requirement[] paidResources; private Player paidPlayer; private int paidFrame; private MethodInfo requestChestAccess; private bool accessPending; private static readonly MethodInfo LoadChest = AccessTools.Method(typeof(Container), "Load", Type.EmptyTypes, (Type[])null); private static readonly MethodInfo ChestAccess = AccessTools.Method(typeof(Container), "CheckAccess", new Type[1] { typeof(long) }, (Type[])null); private static readonly FieldInfo NoPlacementCost = AccessTools.Field(typeof(Player), "m_noPlacementCost"); private static readonly MethodInfo InventoryChanged = AccessTools.Method(typeof(Inventory), "Changed", new Type[2] { typeof(bool), typeof(bool) }, (Type[])null); private void Awake() { //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Expected O, but got Unknown //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Expected O, but got Unknown //IL_011f: Unknown result type (might be due to invalid IL or missing references) //IL_0129: Expected O, but got Unknown //IL_01ea: Unknown result type (might be due to invalid IL or missing references) //IL_01f4: Expected O, but got Unknown //IL_0229: Unknown result type (might be due to invalid IL or missing references) //IL_0233: Expected O, but got Unknown //IL_0268: Unknown result type (might be due to invalid IL or missing references) //IL_0272: Expected O, but got Unknown //IL_0374: Unknown result type (might be due to invalid IL or missing references) //IL_037e: Expected O, but got Unknown Instance = this; ((BaseUnityPlugin)this).Config.SaveOnConfigSet = false; Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Enable chest-assisted hammer building."); Radius = ((BaseUnityPlugin)this).Config.Bind<float>("General", "ChestRadiusMeters", 20f, new ConfigDescription("3D chest radius around the player, approximately a vanilla workbench build radius.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 100f), new object[0])); refresh = ((BaseUnityPlugin)this).Config.Bind<float>("General", "ChestDiscoveryIntervalSeconds", 1f, new ConfigDescription("Discovery cache interval; access and stock are checked again before payment.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 10f), new object[0])); inventoryFirst = ((BaseUnityPlugin)this).Config.Bind<bool>("Materials", "UsePlayerInventoryFirst", true, "Use carried materials before nearest chests. False prefers chests."); prohibitedList = ((BaseUnityPlugin)this).Config.Bind<string>("Materials", "Prohibited", "", "Additional comma-separated prefab names forbidden from CHESTS; carried materials remain usable."); minimumStock = ((BaseUnityPlugin)this).Config.Bind<int>("Materials", "MinimumStockPerChest", 0, new ConfigDescription("Leave this many of each material in EACH chest. ReserveAmounts entries override this per material.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 100000), new object[0])); chestPrefabs = ((BaseUnityPlugin)this).Config.Bind<string>("Chests", "AllowedPrefabs", "piece_chest_wood,piece_chest,piece_chest_blackmetal,piece_chest_private", "Only listed player-built Container prefabs are eligible. Remove a name to exclude that chest type."); privateChests = ((BaseUnityPlugin)this).Config.Bind<bool>("Chests", "IncludePersonalChests", false, "Allow personal chests only if the local player has vanilla access. Ward checks always apply."); GlowEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Glow", "Enabled", true, "Make the locally held hammer glow. Cosmetic, client-only."); GlowColour = ((BaseUnityPlugin)this).Config.Bind<string>("Glow", "Colour", "#66CCFF", "Glow colour as an HTML hex colour."); GlowIntensity = ((BaseUnityPlugin)this).Config.Bind<float>("Glow", "LightIntensity", 1.2f, new ConfigDescription("Point-light intensity; zero disables the light.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 5f), new object[0])); GlowRange = ((BaseUnityPlugin)this).Config.Bind<float>("Glow", "LightRangeMeters", 3f, new ConfigDescription("Shadowless light radius.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 10f), new object[0])); Emission = ((BaseUnityPlugin)this).Config.Bind<float>("Glow", "EmissionIntensity", 1.5f, new ConfigDescription("Hammer material emission where the shader supports it.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 8f), new object[0])); reload = ((BaseUnityPlugin)this).Config.Bind<bool>("Configuration", "ReloadOnFileChange", true, "Re-read settings within two seconds after saving this file."); catalogue = ((BaseUnityPlugin)this).Config.Bind<bool>("Configuration", "AppendDiscoveredMaterials", true, "Append missing commented material examples when a world is loaded, including modded materials."); messages = ((BaseUnityPlugin)this).Config.Bind<bool>("Diagnostics", "ShowBuildMessages", true, "Show a short message after using chest materials or if stock changed."); LogUse = ((BaseUnityPlugin)this).Config.Bind<bool>("Diagnostics", "LogChestConsumption", false, "Log chest material quantities after successful builds."); try { if (LoadChest == null || ChestAccess == null || NoPlacementCost == null || InventoryChanged == null) { throw new MissingMemberException("Required Valheim inventory/build API was not found."); } if (!File.Exists(((BaseUnityPlugin)this).Config.ConfigFilePath)) { WriteInitialConfig(); } ReadMaterialRules(); configStamp = File.GetLastWriteTimeUtc(((BaseUnityPlugin)this).Config.ConfigFilePath); harmony = new Harmony("au.tyvalheim.magichammer"); harmony.PatchAll(typeof(Plugin).Assembly); Type type = AccessTools.TypeByName("ReliableCrafting.Plugin"); if (type != null) { requestChestAccess = AccessTools.Method(type, "RequestChestAccess", new Type[3] { typeof(Container), typeof(Player), typeof(float) }, (Type[])null); } ((BaseUnityPlugin)this).Logger.LogInfo((object)"Magic Hammer 1.0.1 loaded. Read-only remote stock previews; optional Reliable Crafting chest-access handshake."); } catch (Exception ex) { Fault(ex); if (harmony != null) { harmony.UnpatchSelf(); } } } private void WriteInitialConfig() { ((BaseUnityPlugin)this).Config.Save(); File.AppendAllText(((BaseUnityPlugin)this).Config.ConfigFilePath, "\n[ReserveAmounts]\n# Wood = 50\n# Stone = 50\n\n[ProhibitedMaterials]\n# Uncomment a prefab to prohibit chest withdrawal. true = prohibited.\n# FineWood = true\n# RoundLog = true\n"); } private void ReadMaterialRules() { string[] lines = File.ReadAllLines(((BaseUnityPlugin)this).Config.ConfigFilePath); HashSet<string> hashSet = Planning.ParseList(prohibitedList.Value); foreach (KeyValuePair<string, string> item in Planning.ReadSection(lines, "ProhibitedMaterials")) { if (!bool.TryParse(item.Value, out var result)) { throw new FormatException("Invalid ProhibitedMaterials entry: " + item.Key); } if (result) { hashSet.Add(item.Key); } } Dictionary<string, int> dictionary = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase); foreach (KeyValuePair<string, string> item2 in Planning.ReadSection(lines, "ReserveAmounts")) { if (!int.TryParse(item2.Value, out var result2) || result2 < 0) { throw new FormatException("Invalid ReserveAmounts entry: " + item2.Key); } dictionary[item2.Key] = result2; } prohibited = hashSet; reserves = dictionary; allowedChests = Planning.ParseList(chestPrefabs.Value); nextChestScan = 0f; stockFrame = -1; } private void Update() { if (faulted) { return; } try { if (reload.Value && Time.unscaledTime >= nextFileCheck) { nextFileCheck = Time.unscaledTime + 2f; DateTime lastWriteTimeUtc = File.GetLastWriteTimeUtc(((BaseUnityPlugin)this).Config.ConfigFilePath); if (lastWriteTimeUtc != configStamp) { ((BaseUnityPlugin)this).Config.Reload(); ReadMaterialRules(); configStamp = lastWriteTimeUtc; ((BaseUnityPlugin)this).Logger.LogInfo((object)"Magic Hammer configuration reloaded."); DisposeGlow(); } } if (catalogue.Value && (Object)(object)ObjectDB.instance != (Object)null && (Object)(object)ZNetScene.instance != (Object)null && (Object)(object)cataloguedDb != (Object)(object)ObjectDB.instance) { AppendMaterialCatalogue(); cataloguedDb = ObjectDB.instance; } } catch (Exception ex) { Fault(ex); } if (glowFaulted) { return; } try { UpdateGlow(); } catch (Exception ex2) { glowFaulted = true; DisposeGlow(); ((BaseUnityPlugin)this).Logger.LogWarning((object)("Hammer glow disabled: " + ex2)); } } internal static string Prefab(Object obj) { if (!(obj == (Object)null)) { return obj.name.Replace("(Clone)", "").Trim(); } return ""; } internal bool CanUse(Player player) { if (faulted || !Enabled.Value || (Object)(object)player == (Object)null || (Object)(object)player != (Object)(object)Player.m_localPlayer) { return false; } ItemData rightItem = ((Humanoid)player).RightItem; if (rightItem != null) { return string.Equals(Prefab((Object)(object)rightItem.m_dropPrefab), "Hammer", StringComparison.OrdinalIgnoreCase); } return false; } private bool Usable(Container chest, Player player, bool requireOwner = false) { //IL_0066: 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_0076: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Invalid comparison between Unknown and I4 //IL_0101: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)chest == (Object)null || !allowedChests.Contains(Prefab((Object)(object)((Component)chest).gameObject))) { return false; } Piece component = ((Component)chest).GetComponent<Piece>(); ZNetView component2 = ((Component)chest).GetComponent<ZNetView>(); if ((Object)(object)component == (Object)null || !component.IsPlacedByPlayer() || (Object)(object)component2 == (Object)null || !component2.IsValid() || (requireOwner && !component2.IsOwner())) { return false; } Vector3 val = ((Component)chest).transform.position - ((Component)player).transform.position; if (((Vector3)(ref val)).sqrMagnitude > Radius.Value * Radius.Value) { return false; } if (chest.IsInUse() || component2.GetZDO().GetInt(ZDOVars.s_inUse, 0) != 0) { return false; } if (!privateChests.Value && (int)chest.m_privacy != 2) { return false; } if ((bool)ChestAccess.Invoke(chest, new object[1] { player.GetPlayerID() })) { return PrivateArea.CheckAccess(((Component)chest).transform.position, 0f, false, true); } return false; } private Container[] Chests(Player player, bool force) { if (force || Time.unscaledTime >= nextChestScan) { cachedChests = Object.FindObjectsByType<Container>((FindObjectsSortMode)0); nextChestScan = Time.unscaledTime + refresh.Value; } Container[] array = cachedChests.Where((Container c) => Usable(c, player)).OrderBy(delegate(Container c) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) Vector3 val = ((Component)c).transform.position - ((Component)player).transform.position; return ((Vector3)(ref val)).sqrMagnitude; }).ThenBy((Container c) => ((Object)c).GetInstanceID()) .ToArray(); Container[] array2 = array; foreach (Container obj in array2) { LoadChest.Invoke(obj, null); } return array; } private List<Stock> Stocks(Player player, bool force) { if (!force && stockFrame == Time.frameCount && cachedStocks != null) { return cachedStocks; } List<Stock> list = new List<Stock>(); foreach (ItemData allItem in ((Humanoid)player).GetInventory().GetAllItems()) { if (allItem.m_stack > 0 && allItem.m_worldLevel >= Game.m_worldLevel) { list.Add(new Stock { Key = allItem.m_shared.m_name, Available = allItem.m_stack, Tag = new Source { Inventory = ((Humanoid)player).GetInventory(), Item = allItem } }); } } List<Stock> list2 = new List<Stock>(); Container[] array = Chests(player, force); foreach (Container val in array) { foreach (IGrouping<string, ItemData> item in (from val2 in val.GetInventory().GetAllItems() where val2.m_stack > 0 && val2.m_worldLevel >= Game.m_worldLevel && (Object)(object)val2.m_dropPrefab != (Object)null select val2).GroupBy<ItemData, string>((ItemData val2) => Prefab((Object)(object)val2.m_dropPrefab), StringComparer.OrdinalIgnoreCase)) { if (prohibited.Contains(item.Key)) { continue; } if (!reserves.TryGetValue(item.Key, out var value)) { value = minimumStock.Value; } int num = Math.Max(0, item.Sum((ItemData val2) => val2.m_stack) - value); foreach (ItemData item2 in item) { int num2 = Math.Min(num, item2.m_stack); if (num2 > 0) { list2.Add(new Stock { Key = item2.m_shared.m_name, Available = num2, Tag = new Source { Inventory = val.GetInventory(), Chest = val, Item = item2 } }); } num -= num2; } } } cachedStocks = (inventoryFirst.Value ? list.Concat(list2).ToList() : list2.Concat(list).ToList()); stockFrame = Time.frameCount; return cachedStocks; } internal int Count(Inventory inventory, string name, int quality, bool worldLevel) { int num = inventory.CountItems(name, quality, worldLevel); Player localPlayer = Player.m_localPlayer; if (!CanUse(localPlayer) || inventory != ((Humanoid)localPlayer).GetInventory()) { return num; } try { int num2 = (from s in Stocks(localPlayer, force: false) where (Object)(object)((Source)s.Tag).Chest != (Object)null && s.Key == name && (quality < 0 || ((Source)s.Tag).Item.m_quality == quality) select s).Sum((Stock s) => s.Available); return checked(num + num2); } catch (Exception ex) { Fault(ex); return num; } } private Receipt Reserve(Player player, Piece piece) { IEnumerable<Need> needs = from r in piece.m_resources where (Object)(object)r.m_resItem != (Object)null && r.GetAmount(0) > 0 select new Need { Key = r.m_resItem.m_itemData.m_shared.m_name, Amount = r.GetAmount(0) }; List<Withdrawal> list = Planning.Create(needs, Stocks(player, force: true)); if (list == null) { return null; } foreach (Source item in from w in list select (Source)w.Stock.Tag into s where (Object)(object)s.Chest != (Object)null group s by s.Chest into g select g.First()) { if (!((requestChestAccess != null) ? ((bool)requestChestAccess.Invoke(null, new object[3] { item.Chest, player, Radius.Value })) : Usable(item.Chest, player, requireOwner: true))) { accessPending = true; if (requestChestAccess == null) { Tell(player, "Magic Hammer: materials found, but chest access is needed. Open/close it, or enable Reliable Crafting for remote access."); } return null; } } Receipt receipt = new Receipt(); receipt.Plan = list; receipt.Cheated = list.Any((Withdrawal w) => ((Source)w.Stock.Tag).Item.m_cheated); Receipt receipt2 = receipt; foreach (Inventory item2 in list.Select((Withdrawal w) => ((Source)w.Stock.Tag).Inventory).Distinct()) { receipt2.Backups.Add(item2, new ReferenceSnapshot<ItemData>(item2.GetAllItems(), (ItemData i) => i.m_stack)); } try { foreach (Withdrawal item3 in list) { Source source = (Source)item3.Stock.Tag; if ((Object)(object)source.Chest != (Object)null && !Usable(source.Chest, player, requireOwner: true)) { throw new InvalidOperationException("Chest access changed during reservation."); } if (!source.Inventory.GetAllItems().Contains(source.Item) || source.Item.m_stack < item3.Amount) { throw new InvalidOperationException("Material stock changed during reservation."); } receipt2.Touched.Add(source.Inventory); if (!source.Inventory.RemoveItem(source.Item, item3.Amount)) { throw new InvalidOperationException("The game rejected a material withdrawal."); } } return receipt2; } catch { Restore(receipt2); throw; } } private void Restore(Receipt receipt) { List<Exception> list = new List<Exception>(); foreach (Inventory item2 in receipt.Touched) { try { receipt.Backups[item2].Restore(item2.GetAllItems(), delegate(ItemData val, int count) { val.m_stack = count; }); InventoryChanged.Invoke(item2, new object[2] { false, false }); } catch (Exception item) { list.Add(item); } } if (list.Count > 0) { throw new AggregateException("One or more inventory restore callbacks failed.", list); } } internal bool Build(Player player, Piece piece) { //IL_002f: Unknown result type (might be due to invalid IL or missing references) paidResources = null; paidPlayer = null; if (!CanUse(player) || (bool)NoPlacementCost.GetValue(player) || ZoneSystem.instance.GetGlobalKey(piece.FreeBuildKey())) { return player.TryPlacePiece(piece); } if (activeReceipt != null) { throw new InvalidOperationException("Nested Magic Hammer placement is unsupported."); } Receipt receipt = null; bool flag = false; accessPending = false; try { receipt = Reserve(player, piece); if (receipt == null) { if (!accessPending) { Tell(player, "Magic Hammer: materials unavailable (chest busy, protected, out of range or stock changed)."); } return false; } activeReceipt = receipt; flag = player.TryPlacePiece(piece); if (!flag) { Restore(receipt); } else { paidResources = piece.m_resources; paidPlayer = player; paidFrame = Time.frameCount; int num = receipt.Plan.Where((Withdrawal w) => (Object)(object)((Source)w.Stock.Tag).Chest != (Object)null).Sum((Withdrawal w) => w.Amount); if (num > 0) { Tell(player, "Magic Hammer: used " + num + " materials from nearby chests."); if (LogUse.Value) { ((BaseUnityPlugin)this).Logger.LogInfo((object)("Built " + Prefab((Object)(object)((Component)piece).gameObject) + ": " + string.Join(", ", (from w in receipt.Plan where (Object)(object)((Source)w.Stock.Tag).Chest != (Object)null select Prefab((Object)(object)((Source)w.Stock.Tag).Item.m_dropPrefab) + " x" + w.Amount).ToArray()))); } } } return flag; } catch (Exception ex) { if (receipt != null && !receipt.PlacementEntered && !flag) { try { Restore(receipt); } catch (Exception ex2) { ((BaseUnityPlugin)this).Logger.LogError((object)("Material restoration failed: " + ex2)); } } Fault(ex); return flag; } finally { activeReceipt = null; stockFrame = -1; } } internal void ConsumePaid(Player player, Requirement[] resources, int quality, int itemQuality, int multiplier) { if ((Object)(object)player == (Object)(object)paidPlayer && object.ReferenceEquals(resources, paidResources) && paidFrame == Time.frameCount) { paidResources = null; paidPlayer = null; } else { player.ConsumeResources(resources, quality, itemQuality, multiplier); } } internal bool ReservedCheated(Inventory inventory) { if (activeReceipt != null && activeReceipt.Cheated && (Object)(object)Player.m_localPlayer != (Object)null) { return inventory == ((Humanoid)Player.m_localPlayer).GetInventory(); } return false; } internal void PlacementEntered(Player player) { if (activeReceipt != null && (Object)(object)player == (Object)(object)Player.m_localPlayer) { activeReceipt.PlacementEntered = true; } } private void Tell(Player player, string text) { if (messages.Value) { ((Character)player).Message((MessageType)1, text, 0, (Sprite)null, false); } } internal void Fault(Exception ex) { faulted = true; ((BaseUnityPlugin)this).Logger.LogError((object)("Magic Hammer chest assistance disabled until restart. Native building remains available. " + ex)); DisposeGlow(); } private void AppendMaterialCatalogue() { //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Invalid comparison between Unknown and I4 SortedSet<string> sortedSet = new SortedSet<string>(StringComparer.OrdinalIgnoreCase); foreach (GameObject item in ObjectDB.instance.m_items) { ItemDrop component = item.GetComponent<ItemDrop>(); if ((Object)(object)component != (Object)null && (int)component.m_itemData.m_shared.m_itemType == 1) { sortedSet.Add(((Object)item).name); } } GameObject itemPrefab = ObjectDB.instance.GetItemPrefab("Hammer"); if ((Object)(object)itemPrefab != (Object)null) { PieceTable buildPieces = itemPrefab.GetComponent<ItemDrop>().m_itemData.m_shared.m_buildPieces; if ((Object)(object)buildPieces != (Object)null) { foreach (GameObject piece in buildPieces.m_pieces) { Piece val = (((Object)(object)piece == (Object)null) ? null : piece.GetComponent<Piece>()); if (!((Object)(object)val != (Object)null)) { continue; } Requirement[] resources = val.m_resources; foreach (Requirement val2 in resources) { if ((Object)(object)val2.m_resItem != (Object)null) { sortedSet.Add(((Object)((Component)val2.m_resItem).gameObject).name); } } } } } HashSet<string> existing = new HashSet<string>(StringComparer.OrdinalIgnoreCase); string[] array = File.ReadAllLines(((BaseUnityPlugin)this).Config.ConfigFilePath); foreach (string text in array) { string text2 = text.Trim().TrimStart('#', ';').Trim(); int num = text2.IndexOf('='); if (num > 0) { existing.Add(text2.Substring(0, num).Trim()); } } string[] array2 = sortedSet.Where((string n) => !existing.Contains(n)).ToArray(); if (array2.Length > 0) { File.AppendAllText(((BaseUnityPlugin)this).Config.ConfigFilePath, "\n[ProhibitedMaterials]\n# Additional materials discovered in this game installation:\n" + string.Join("\n", array2.Select((string n) => "# " + n + " = true").ToArray()) + "\n"); configStamp = File.GetLastWriteTimeUtc(((BaseUnityPlugin)this).Config.ConfigFilePath); ((BaseUnityPlugin)this).Logger.LogInfo((object)("Added " + array2.Length + " commented material examples to the config.")); } } private void UpdateGlow() { Player localPlayer = Player.m_localPlayer; if (!GlowEnabled.Value || !CanUse(localPlayer)) { DisposeGlow(); return; } GameObject val = Glow.HeldVisual(localPlayer); if ((Object)(object)val == (Object)null) { DisposeGlow(); return; } if (glow != null && (Object)(object)glow.Target != (Object)(object)val) { DisposeGlow(); } if (glow == null) { glow = new Glow(val, this); } } private void DisposeGlow() { if (glow != null) { glow.Dispose(); glow = null; } } private void OnDestroy() { DisposeGlow(); if (harmony != null) { harmony.UnpatchSelf(); } if ((Object)(object)Instance == (Object)(object)this) { Instance = null; } } }