using System;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using UnityEngine;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("AutoSmelter")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("AutoSmelter")]
[assembly: AssemblyCopyright("Copyright © 2026")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("17d61898-8ea4-42c2-97ed-b4c8b98278e7")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace AutoSmelter;
[BepInPlugin("com.autosmelter.valheim", "AutoSmelter", "1.0.0")]
[BepInDependency(/*Could not decode attribute arguments.*/)]
public class AutoSmelterPlugin : BaseUnityPlugin
{
public const string PluginGUID = "com.autosmelter.valheim";
public const string PluginName = "AutoSmelter";
public const string PluginVersion = "1.0.0";
internal static ManualLogSource Log;
private GameObject automationObject;
private void Awake()
{
//IL_002b: Unknown result type (might be due to invalid IL or missing references)
//IL_0035: Expected O, but got Unknown
Log = ((BaseUnityPlugin)this).Logger;
Log.LogInfo((object)"Loading AutoSmelter v1.0.0");
ConfigManager.Initialize(((BaseUnityPlugin)this).Config);
automationObject = new GameObject("AutoSmelterAutomation");
automationObject.AddComponent<SmelterAutomation>();
Object.DontDestroyOnLoad((Object)(object)automationObject);
Log.LogInfo((object)"AutoSmelter loaded successfully!");
}
private void OnDestroy()
{
if ((Object)(object)automationObject != (Object)null)
{
Object.Destroy((Object)(object)automationObject);
}
}
}
public static class ConfigManager
{
public static ConfigEntry<float> ChestRange;
public static ConfigEntry<float> UpdateInterval;
public static ConfigEntry<bool> EnableKilnAutomation;
public static ConfigEntry<bool> EnableSmelterAutomation;
public static void Initialize(ConfigFile config)
{
ChestRange = config.Bind<float>("General", "ChestRange", 10f, "Maximum distance (meters) to search for chests");
UpdateInterval = config.Bind<float>("General", "UpdateInterval", 2f, "Seconds between automation checks");
EnableKilnAutomation = config.Bind<bool>("Automation", "EnableKilnAutomation", true, "Enable automatic charcoal kiln management");
EnableSmelterAutomation = config.Bind<bool>("Automation", "EnableSmelterAutomation", true, "Enable automatic smelter management");
}
}
public static class ItemTransferHelper
{
public static Container FindNearestChest(Vector3 position, float range)
{
//IL_0022: Unknown result type (might be due to invalid IL or missing references)
//IL_002a: Unknown result type (might be due to invalid IL or missing references)
Container result = null;
float num = float.MaxValue;
Container[] array = Object.FindObjectsByType<Container>((FindObjectsSortMode)0);
foreach (Container val in array)
{
if (!((Object)(object)val == (Object)null))
{
float num2 = Vector3.Distance(position, ((Component)val).transform.position);
if (num2 <= range && num2 < num)
{
result = val;
num = num2;
}
}
}
return result;
}
public static Container FindNearestChestWithItem(Vector3 position, string itemName, float range)
{
//IL_0022: Unknown result type (might be due to invalid IL or missing references)
//IL_002a: Unknown result type (might be due to invalid IL or missing references)
Container result = null;
float num = float.MaxValue;
Container[] array = Object.FindObjectsByType<Container>((FindObjectsSortMode)0);
foreach (Container val in array)
{
if (!((Object)(object)val == (Object)null))
{
float num2 = Vector3.Distance(position, ((Component)val).transform.position);
if (num2 <= range && num2 < num && HasItem(val, itemName))
{
result = val;
num = num2;
}
}
}
return result;
}
public static Container FindNearestChestWithOre(Vector3 position, float range)
{
//IL_0053: Unknown result type (might be due to invalid IL or missing references)
//IL_005b: Unknown result type (might be due to invalid IL or missing references)
string[] array = new string[5] { "CopperOre", "TinOre", "IronOre", "SilverOre", "BlackMetalScrap" };
Container result = null;
float num = float.MaxValue;
Container[] array2 = Object.FindObjectsByType<Container>((FindObjectsSortMode)0);
foreach (Container val in array2)
{
if ((Object)(object)val == (Object)null)
{
continue;
}
float num2 = Vector3.Distance(position, ((Component)val).transform.position);
if (!(num2 <= range) || !(num2 < num))
{
continue;
}
string[] array3 = array;
foreach (string itemName in array3)
{
if (HasItem(val, itemName))
{
result = val;
num = num2;
break;
}
}
}
return result;
}
public static bool HasItem(Container container, string itemName)
{
if ((Object)(object)container == (Object)null)
{
return false;
}
Inventory inventory = container.GetInventory();
if (inventory == null)
{
return false;
}
return inventory.GetAllItems()?.Any((ItemData item) => item != null && (Object)(object)item.m_dropPrefab != (Object)null && ((Object)item.m_dropPrefab).name == itemName) ?? false;
}
public static ItemData GetItemFromContainer(Container container, string itemName)
{
if ((Object)(object)container == (Object)null)
{
return null;
}
Inventory inventory = container.GetInventory();
if (inventory == null)
{
return null;
}
return inventory.GetAllItems()?.FirstOrDefault((Func<ItemData, bool>)((ItemData item) => item != null && (Object)(object)item.m_dropPrefab != (Object)null && ((Object)item.m_dropPrefab).name == itemName));
}
public static bool HasSpace(Container container, ItemData item)
{
if ((Object)(object)container == (Object)null || item == null)
{
return false;
}
Inventory inventory = container.GetInventory();
if (inventory == null)
{
return false;
}
return inventory.CanAddItem(item, 1);
}
public static bool TransferItem(Container source, Container destination, string itemName, int amount)
{
if ((Object)(object)source == (Object)null || (Object)(object)destination == (Object)null)
{
return false;
}
Inventory inventory = source.GetInventory();
Inventory inventory2 = destination.GetInventory();
if (inventory == null || inventory2 == null)
{
return false;
}
ItemData itemFromContainer = GetItemFromContainer(source, itemName);
if (itemFromContainer == null)
{
return false;
}
int num = Mathf.Min(amount, itemFromContainer.m_stack);
if (num <= 0)
{
return false;
}
if (!inventory2.CanAddItem(itemFromContainer, num))
{
return false;
}
inventory.RemoveItem(itemFromContainer, num);
ItemData val = itemFromContainer.Clone();
val.m_stack = num;
inventory2.AddItem(val);
return true;
}
}
public class SmelterAutomation : MonoBehaviour
{
private float lastUpdateTime;
private static Type kilnType;
private static FieldInfo kilnFuelField;
private static FieldInfo kilnMaxFuelField;
private static FieldInfo kilnProductField;
private static Type smelterType;
private static FieldInfo smelterFuelField;
private static FieldInfo smelterMaxFuelField;
private static FieldInfo smelterProductField;
private static FieldInfo smelterMaxOreField;
private static MethodInfo smelterGetQueueSizeMethod;
private static MethodInfo smelterCanAddOreMethod;
private static MethodInfo smelterAddOreMethod;
private void Start()
{
AutoSmelterPlugin.Log.LogInfo((object)"SmelterAutomation started");
InitializeReflection();
}
private void InitializeReflection()
{
try
{
Assembly assembly = null;
Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
foreach (Assembly assembly2 in assemblies)
{
string name = assembly2.GetName().Name;
if (name == "assembly_valheim" || name == "Assembly-CSharp")
{
assembly = assembly2;
break;
}
}
if (assembly == null)
{
AutoSmelterPlugin.Log.LogError((object)"Could not find assembly_valheim or Assembly-CSharp assembly");
return;
}
kilnType = assembly.GetType("CharcoalKiln");
if (kilnType == null)
{
AutoSmelterPlugin.Log.LogError((object)"Could not find CharcoalKiln type");
return;
}
kilnFuelField = kilnType.GetField("m_fuel", BindingFlags.Instance | BindingFlags.NonPublic);
kilnMaxFuelField = kilnType.GetField("m_maxFuel", BindingFlags.Instance | BindingFlags.NonPublic);
kilnProductField = kilnType.GetField("m_product", BindingFlags.Instance | BindingFlags.NonPublic);
smelterType = assembly.GetType("Smelter");
if (smelterType == null)
{
AutoSmelterPlugin.Log.LogError((object)"Could not find Smelter type");
return;
}
smelterFuelField = smelterType.GetField("m_fuel", BindingFlags.Instance | BindingFlags.NonPublic);
smelterMaxFuelField = smelterType.GetField("m_maxFuel", BindingFlags.Instance | BindingFlags.NonPublic);
smelterProductField = smelterType.GetField("m_product", BindingFlags.Instance | BindingFlags.NonPublic);
smelterMaxOreField = smelterType.GetField("m_maxOre", BindingFlags.Instance | BindingFlags.NonPublic);
smelterGetQueueSizeMethod = smelterType.GetMethod("GetQueueSize", BindingFlags.Instance | BindingFlags.NonPublic);
smelterCanAddOreMethod = smelterType.GetMethod("CanAddOre", BindingFlags.Instance | BindingFlags.NonPublic);
smelterAddOreMethod = smelterType.GetMethod("AddOre", BindingFlags.Instance | BindingFlags.NonPublic);
AutoSmelterPlugin.Log.LogInfo((object)"Reflection initialized successfully");
}
catch (Exception ex)
{
AutoSmelterPlugin.Log.LogError((object)("Failed to initialize reflection: " + ex.Message));
}
}
private void Update()
{
if (Time.time - lastUpdateTime >= ConfigManager.UpdateInterval.Value)
{
lastUpdateTime = Time.time;
ProcessAutomation();
}
}
private void ProcessAutomation()
{
if (ConfigManager.EnableKilnAutomation.Value)
{
ProcessCharcoalKilns();
}
if (ConfigManager.EnableSmelterAutomation.Value)
{
ProcessSmelters();
}
}
private void ProcessCharcoalKilns()
{
//IL_006b: Unknown result type (might be due to invalid IL or missing references)
//IL_0072: Expected O, but got Unknown
//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
//IL_0174: Unknown result type (might be due to invalid IL or missing references)
if (kilnType == null)
{
return;
}
Object[] array = Object.FindObjectsByType(kilnType, (FindObjectsSortMode)0);
if (array == null || array.Length == 0)
{
return;
}
Object[] array2 = array;
foreach (Object val in array2)
{
if (val == (Object)null)
{
continue;
}
try
{
float num = (float)kilnFuelField.GetValue(val);
float num2 = (float)kilnMaxFuelField.GetValue(val);
ItemData val2 = (ItemData)kilnProductField.GetValue(val);
if (!(num < num2))
{
goto IL_013b;
}
Object obj = ((val is MonoBehaviour) ? val : null);
Transform val3 = ((obj != null) ? ((Component)obj).transform : null);
if ((Object)(object)val3 == (Object)null)
{
continue;
}
Container val4 = ItemTransferHelper.FindNearestChestWithItem(val3.position, "Wood", ConfigManager.ChestRange.Value);
if ((Object)(object)val4 != (Object)null)
{
int num3 = (int)(num2 - num);
if (ItemTransferHelper.TransferItem(val4, null, "Wood", num3))
{
float num4 = Mathf.Min(num + (float)num3, num2);
kilnFuelField.SetValue(val, num4);
AutoSmelterPlugin.Log.LogDebug((object)$"Added {num3} wood to kiln");
}
}
goto IL_013b;
IL_013b:
if (val2 == null || val2.m_stack <= 0)
{
continue;
}
Object obj2 = ((val is MonoBehaviour) ? val : null);
Transform val5 = ((obj2 != null) ? ((Component)obj2).transform : null);
if ((Object)(object)val5 == (Object)null)
{
continue;
}
Container val6 = ItemTransferHelper.FindNearestChest(val5.position, ConfigManager.ChestRange.Value);
if ((Object)(object)val6 != (Object)null && ItemTransferHelper.HasSpace(val6, val2))
{
ItemData val7 = val2.Clone();
int stack = val7.m_stack;
if (val6.GetInventory().AddItem(val7))
{
kilnProductField.SetValue(val, null);
AutoSmelterPlugin.Log.LogDebug((object)$"Transferred {stack} coal to chest");
}
}
}
catch (Exception ex)
{
AutoSmelterPlugin.Log.LogError((object)("Error processing kiln: " + ex.Message));
}
}
}
private void ProcessSmelters()
{
//IL_0090: Unknown result type (might be due to invalid IL or missing references)
//IL_0097: Expected O, but got Unknown
//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
//IL_018e: Unknown result type (might be due to invalid IL or missing references)
//IL_0323: Unknown result type (might be due to invalid IL or missing references)
if (smelterType == null)
{
return;
}
Object[] array = Object.FindObjectsByType(smelterType, (FindObjectsSortMode)0);
if (array == null || array.Length == 0)
{
return;
}
Object[] array2 = array;
foreach (Object val in array2)
{
if (val == (Object)null)
{
continue;
}
try
{
float num = (float)smelterFuelField.GetValue(val);
float num2 = (float)smelterMaxFuelField.GetValue(val);
int num3 = (int)smelterMaxOreField.GetValue(val);
int num4 = (int)smelterGetQueueSizeMethod.Invoke(val, null);
ItemData val2 = (ItemData)smelterProductField.GetValue(val);
if (!(num < num2))
{
goto IL_0160;
}
Object obj = ((val is MonoBehaviour) ? val : null);
Transform val3 = ((obj != null) ? ((Component)obj).transform : null);
if ((Object)(object)val3 == (Object)null)
{
continue;
}
Container val4 = ItemTransferHelper.FindNearestChestWithItem(val3.position, "Coal", ConfigManager.ChestRange.Value);
if ((Object)(object)val4 != (Object)null)
{
int num5 = (int)(num2 - num);
if (ItemTransferHelper.TransferItem(val4, null, "Coal", num5))
{
float num6 = Mathf.Min(num + (float)num5, num2);
smelterFuelField.SetValue(val, num6);
AutoSmelterPlugin.Log.LogDebug((object)$"Added {num5} coal to smelter");
}
}
goto IL_0160;
IL_0160:
if (num4 >= num3)
{
goto IL_02ea;
}
Object obj2 = ((val is MonoBehaviour) ? val : null);
Transform val5 = ((obj2 != null) ? ((Component)obj2).transform : null);
if ((Object)(object)val5 == (Object)null)
{
continue;
}
Container val6 = ItemTransferHelper.FindNearestChestWithOre(val5.position, ConfigManager.ChestRange.Value);
if ((Object)(object)val6 != (Object)null)
{
string[] array3 = new string[5] { "CopperOre", "TinOre", "IronOre", "SilverOre", "BlackMetalScrap" };
foreach (string text in array3)
{
if (!ItemTransferHelper.HasItem(val6, text))
{
continue;
}
ItemData itemFromContainer = ItemTransferHelper.GetItemFromContainer(val6, text);
if (itemFromContainer != null && (bool)smelterCanAddOreMethod.Invoke(val, new object[1] { itemFromContainer.m_dropPrefab }))
{
int num7 = Mathf.Min(itemFromContainer.m_stack, num3 - num4);
val6.GetInventory().RemoveItem(itemFromContainer, num7);
for (int k = 0; k < num7; k++)
{
smelterAddOreMethod.Invoke(val, new object[1] { itemFromContainer.m_dropPrefab });
}
AutoSmelterPlugin.Log.LogDebug((object)$"Added {num7} {text} to smelter");
break;
}
}
}
goto IL_02ea;
IL_02ea:
if (val2 == null || val2.m_stack <= 0)
{
continue;
}
Object obj3 = ((val is MonoBehaviour) ? val : null);
Transform val7 = ((obj3 != null) ? ((Component)obj3).transform : null);
if ((Object)(object)val7 == (Object)null)
{
continue;
}
Container val8 = ItemTransferHelper.FindNearestChest(val7.position, ConfigManager.ChestRange.Value);
if ((Object)(object)val8 != (Object)null && ItemTransferHelper.HasSpace(val8, val2))
{
ItemData val9 = val2.Clone();
int stack = val9.m_stack;
if (val8.GetInventory().AddItem(val9))
{
smelterProductField.SetValue(val, null);
AutoSmelterPlugin.Log.LogDebug((object)$"Transferred {stack} ingots to chest");
}
}
}
catch (Exception ex)
{
AutoSmelterPlugin.Log.LogError((object)("Error processing smelter: " + ex.Message));
}
}
}
}