Decompiled source of Enigmas Enhanced Forging v0.1.2

EnigmasEnhancedForging.dll

Decompiled a day ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Jotunn.Configs;
using Jotunn.Entities;
using Jotunn.Managers;
using Jotunn.Utils;
using Microsoft.CodeAnalysis;
using TMPro;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyVersion("0.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 EnigmasEnhancedForging
{
	[BepInPlugin("EnigmaPrime.EnigmasEnhancedForging", "Enigma's Enhanced Forging", "0.1.2")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[NetworkCompatibility(/*Could not decode attribute arguments.*/)]
	public class Plugin : BaseUnityPlugin
	{
		public const string PluginGuid = "EnigmaPrime.EnigmasEnhancedForging";

		public const string PluginName = "Enigma's Enhanced Forging";

		public const string PluginVersion = "0.1.2";

		internal static ManualLogSource Log;

		private Harmony _harmony;

		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;
			StationConfig.Bind(((BaseUnityPlugin)this).Config);
			TinScrap.Register();
			SilverScrap.Register();
			FlametalScrap.Register();
			_harmony = new Harmony("EnigmaPrime.EnigmasEnhancedForging");
			_harmony.PatchAll();
			SalvagerCompat.Apply(_harmony);
			foreach (MethodBase patchedMethod in _harmony.GetPatchedMethods())
			{
				Log.LogInfo((object)("Patched: " + patchedMethod.DeclaringType?.Name + "." + patchedMethod.Name));
			}
			Log.LogInfo((object)"Enigma's Enhanced Forging 0.1.2 loaded.");
		}

		private void OnDestroy()
		{
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}
	}
	internal static class StationConfig
	{
		public static ConfigEntry<bool> SmelterScreen;

		public static ConfigEntry<int> InputSlotMax;

		public static ConfigEntry<int> FuelSlotMax;

		public static ConfigEntry<int> OutputSlotMax;

		public static ConfigEntry<float> OreSeconds;

		public static ConfigEntry<float> OreCoal;

		public static ConfigEntry<float> ScrapSeconds;

		public static ConfigEntry<float> ScrapCoal;

		public static ConfigEntry<float> BarSeconds;

		public static ConfigEntry<float> BarCoal;

		public static ConfigEntry<float> CoolDownSeconds;

		public static ConfigEntry<float> FinishSoundPitch;

		public static ConfigEntry<float> FinishSoundVolume;

		public static ConfigEntry<float> FireCrackleVolume;

		public static ConfigEntry<string> FinishHissPrefab;

		public static ConfigEntry<bool> FinishBubbling;

		public static ConfigEntry<bool> FinishCookingDone;

		public static ConfigEntry<int> BronzeOutput;

		public static ConfigEntry<bool> RemoveForgeBronzeRecipe;

		public static ConfigEntry<int> RefineryInputSlotMax;

		public static ConfigEntry<int> RefineryFuelSlotMax;

		public static ConfigEntry<float> RefinerySeconds;

		public static ConfigEntry<int> RefinerySap;

		public static ConfigEntry<float> RefineryPitch;

		public static ConfigEntry<float> RefineryVolume;

		public static ConfigEntry<float> RefineryLoopVolume;

		public static ConfigEntry<bool> WindAnimation;

		public static ConfigEntry<float> WindUpSeconds;

		public static ConfigEntry<float> WindDownSeconds;

		public static ConfigEntry<bool> WindDownMatchesCoolDown;

		public static ConfigEntry<string> RefineryThudPrefab;

		public static ConfigEntry<float> RefinerySoftSwitchSize;

		public static ConfigEntry<float> SmelterCostMultiplier;

		public static ConfigEntry<float> KilnCostMultiplier;

		public static ConfigEntry<float> BlastFurnaceCostMultiplier;

		public static ConfigEntry<float> RefineryCostMultiplier;

		public static ConfigEntry<float> FrostKilnCostMultiplier;

		public static void Bind(ConfigFile cfg)
		{
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Expected O, but got Unknown
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Expected O, but got Unknown
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Expected O, but got Unknown
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b3: Expected O, but got Unknown
			//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dd: Expected O, but got Unknown
			//IL_0120: Unknown result type (might be due to invalid IL or missing references)
			//IL_012a: Expected O, but got Unknown
			//IL_0146: Unknown result type (might be due to invalid IL or missing references)
			//IL_0150: Expected O, but got Unknown
			//IL_016c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0176: Expected O, but got Unknown
			//IL_0193: Unknown result type (might be due to invalid IL or missing references)
			//IL_019d: Expected O, but got Unknown
			//IL_01ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c4: Expected O, but got Unknown
			//IL_01f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fc: Expected O, but got Unknown
			//IL_021f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0229: Expected O, but got Unknown
			//IL_0257: Unknown result type (might be due to invalid IL or missing references)
			//IL_0261: Expected O, but got Unknown
			//IL_028f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0299: Expected O, but got Unknown
			//IL_02c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d1: Expected O, but got Unknown
			//IL_031e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0328: Expected O, but got Unknown
			//IL_0371: Unknown result type (might be due to invalid IL or missing references)
			//IL_037b: Expected O, but got Unknown
			//IL_03c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_03ce: Expected O, but got Unknown
			//IL_040e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0418: Expected O, but got Unknown
			//IL_0438: Unknown result type (might be due to invalid IL or missing references)
			//IL_0442: Expected O, but got Unknown
			//IL_0462: Unknown result type (might be due to invalid IL or missing references)
			//IL_046c: Expected O, but got Unknown
			//IL_048c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0496: Expected O, but got Unknown
			//IL_04b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_04c0: Expected O, but got Unknown
			//IL_04fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0507: Expected O, but got Unknown
			//IL_0535: Unknown result type (might be due to invalid IL or missing references)
			//IL_053f: Expected O, but got Unknown
			//IL_056d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0577: Expected O, but got Unknown
			//IL_05a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_05af: Expected O, but got Unknown
			//IL_05dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_05e7: Expected O, but got Unknown
			//IL_065c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0666: Expected O, but got Unknown
			AcceptableValueRange<float> val = new AcceptableValueRange<float>(1f, 10f);
			SmelterCostMultiplier = cfg.Bind<float>("Build cost", "SmelterCostMultiplier", 3f, new ConfigDescription("Multiplies every ingredient in the Smelter's build cost. 1 is the game's own cost. The windows hold far more than the vanilla stations, so fewer are needed. Needs a game restart to change.", (AcceptableValueBase)(object)val, Array.Empty<object>()));
			KilnCostMultiplier = cfg.Bind<float>("Build cost", "CharcoalKilnCostMultiplier", 3f, new ConfigDescription("Multiplies every ingredient in the Charcoal Kiln's build cost. 1 is the game's own cost. Needs a game restart to change.", (AcceptableValueBase)(object)val, Array.Empty<object>()));
			BlastFurnaceCostMultiplier = cfg.Bind<float>("Build cost", "BlastFurnaceCostMultiplier", 3f, new ConfigDescription("Multiplies every ingredient in the Blast Furnace's build cost. 1 is the game's own cost. Needs a game restart to change.", (AcceptableValueBase)(object)val, Array.Empty<object>()));
			RefineryCostMultiplier = cfg.Bind<float>("Build cost", "EitrRefineryCostMultiplier", 2f, new ConfigDescription("Multiplies every ingredient in the Eitr Refinery's build cost. 1 is the game's own cost. Needs a game restart to change.", (AcceptableValueBase)(object)val, Array.Empty<object>()));
			FrostKilnCostMultiplier = cfg.Bind<float>("Build cost", "FrostKilnCostMultiplier", 3f, new ConfigDescription("Multiplies every ingredient in the Frost Kiln's build cost. 1 is the game's own cost. Needs a game restart to change.", (AcceptableValueBase)(object)val, Array.Empty<object>()));
			SmelterScreen = cfg.Bind<bool>("Stations", "SmelterScreen", true, "Replace the Smelter's add ore and add coal switches with an inventory screen (input, coal and output rows). Needs a game restart to change.");
			AcceptableValueRange<int> val2 = new AcceptableValueRange<int>(0, 200);
			InputSlotMax = cfg.Bind<int>("Stations", "InputSlotMax", 0, new ConfigDescription("Most items one slot of the Input row can hold. 0 means a full stack.", (AcceptableValueBase)(object)val2, Array.Empty<object>()));
			FuelSlotMax = cfg.Bind<int>("Stations", "FuelSlotMax", 0, new ConfigDescription("Most items one slot of the Coal row can hold. 0 means a full stack.", (AcceptableValueBase)(object)val2, Array.Empty<object>()));
			OutputSlotMax = cfg.Bind<int>("Stations", "OutputSlotMax", 0, new ConfigDescription("Most items one slot of the Output row can hold. 0 means a full stack.", (AcceptableValueBase)(object)val2, Array.Empty<object>()));
			RefineryInputSlotMax = cfg.Bind<int>("Refinery", "InputSlotMax", 25, new ConfigDescription("Most Softtissue one slot of the Eitr Refinery's Softtissue box can hold. 4 slots, so 25 is 100 Softtissue, about an hour of refining. 0 means a full stack.", (AcceptableValueBase)(object)val2, Array.Empty<object>()));
			RefineryFuelSlotMax = cfg.Bind<int>("Refinery", "SapSlotMax", 25, new ConfigDescription("Most Sap one slot of the Eitr Refinery's Sap box can hold. 4 slots, so 25 is 100 Sap, about an hour of refining. 0 means a full stack.", (AcceptableValueBase)(object)val2, Array.Empty<object>()));
			RefinerySeconds = cfg.Bind<float>("Refinery", "SecondsPerEitr", 40f, new ConfigDescription("Seconds to refine one Softtissue into Eitr (the game's own value is 40).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 600f), Array.Empty<object>()));
			RefinerySap = cfg.Bind<int>("Refinery", "SapPerEitr", 1, new ConfigDescription("Sap used to refine one Softtissue (the game's own value is 1).", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 20), Array.Empty<object>()));
			RefineryPitch = cfg.Bind<float>("Refinery", "FinishSoundPitch", 0.5f, new ConfigDescription("Pitch and speed of the finish sounds when an Eitr comes out of the refinery. 1 is the original, lower is slower and deeper.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 1f), Array.Empty<object>()));
			RefineryVolume = cfg.Bind<float>("Refinery", "FinishSoundVolume", 1f, new ConfigDescription("Volume of the refinery's finish sounds (bubbling and the cooking-done hiss).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
			RefineryLoopVolume = cfg.Bind<float>("Refinery", "RunningSoundVolume", 1f, new ConfigDescription("Volume of the refinery's own running sound while it refines (0 is silent).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
			RefineryThudPrefab = cfg.Bind<string>("Refinery", "FinishThudPrefab", "sfx_build_hammer_stone", "Name of the sound prefab played with the bubbling when an Eitr comes out (a hammer thud by default; try sfx_build_hammer_wood, sfx_build_hammer_metal or sfx_build_hammer_default). Leave empty for none.");
			RefinerySoftSwitchSize = cfg.Bind<float>("Refinery", "SofttissueSpotSize", 2.5f, new ConfigDescription("How much bigger the click area of the Softtissue spot (top of the refinery) is than the game's own, so it is not lost to the refinery's body. 1 is the original. Needs a game restart to change.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 6f), Array.Empty<object>()));
			WindAnimation = cfg.Bind<bool>("Animation", "WindUpAndDown", true, "Machine animations (Blast Furnace wheels, the refinery's grinding) and the refinery's running sound speed up when the machine starts and slow down when it stops, instead of switching on and off.");
			WindUpSeconds = cfg.Bind<float>("Animation", "WindUpSeconds", 4f, new ConfigDescription("Seconds for a machine to reach full speed after it is lit.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 60f), Array.Empty<object>()));
			WindDownMatchesCoolDown = cfg.Bind<bool>("Animation", "WindDownMatchesCoolDown", true, "When on, a machine slows down over the whole cool-down time, so it comes to rest just as the contents cool and can be taken. When off, WindDownSeconds is used instead.");
			WindDownSeconds = cfg.Bind<float>("Animation", "WindDownSeconds", 10f, new ConfigDescription("Seconds for a machine to slow to a stop after it is doused or runs out. Only used when WindDownMatchesCoolDown is off.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 120f), Array.Empty<object>()));
			AcceptableValueRange<float> val3 = new AcceptableValueRange<float>(1f, 600f);
			AcceptableValueRange<float> val4 = new AcceptableValueRange<float>(0f, 20f);
			OreSeconds = cfg.Bind<float>("Smelting", "OreSeconds", 30f, new ConfigDescription("Seconds to melt one ore into a bar.", (AcceptableValueBase)(object)val3, Array.Empty<object>()));
			OreCoal = cfg.Bind<float>("Smelting", "OreCoal", 2f, new ConfigDescription("Coal used to melt one ore.", (AcceptableValueBase)(object)val4, Array.Empty<object>()));
			ScrapSeconds = cfg.Bind<float>("Smelting", "ScrapSeconds", 15f, new ConfigDescription("Seconds to melt one scrap into a bar (already smelted once, so faster).", (AcceptableValueBase)(object)val3, Array.Empty<object>()));
			ScrapCoal = cfg.Bind<float>("Smelting", "ScrapCoal", 1f, new ConfigDescription("Coal used to melt one scrap.", (AcceptableValueBase)(object)val4, Array.Empty<object>()));
			BarSeconds = cfg.Bind<float>("Smelting", "BarSeconds", 10f, new ConfigDescription("Seconds to alloy one bar into Bronze (used by the Bronze run).", (AcceptableValueBase)(object)val3, Array.Empty<object>()));
			RemoveForgeBronzeRecipe = cfg.Bind<bool>("Smelting", "RemoveForgeBronzeRecipe", true, "Remove the vanilla Forge crafting recipes for Bronze, so Bronze can only be made in the Smelter. Needs a game restart to change.");
			BronzeOutput = cfg.Bind<int>("Smelting", "BronzeOutput", 3, new ConfigDescription("Bronze bars made by one Bronze run (2 copper-family items plus 1 tin-family item). The cost is the same whatever you pick; only the yield changes.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 3), Array.Empty<object>()));
			CoolDownSeconds = cfg.Bind<float>("Smelting", "CoolDownSeconds", 20f, new ConfigDescription("While a smelter is working, and for this many seconds after it stops, everything inside is too hot to take out. Adding items is always allowed.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 600f), Array.Empty<object>()));
			FinishSoundPitch = cfg.Bind<float>("Sound", "FinishSoundPitch", 0.5f, new ConfigDescription("Pitch and speed of the bubbling sound when a bar comes out. 1 is the original, lower is slower and deeper.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 1f), Array.Empty<object>()));
			FinishSoundVolume = cfg.Bind<float>("Sound", "FinishSoundVolume", 1f, new ConfigDescription("Volume of the sounds played when a bar comes out (bubbling, done chime). Separate from the fire crackle.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
			FireCrackleVolume = cfg.Bind<float>("Sound", "FireCrackleVolume", 1f, new ConfigDescription("Volume of the fire crackling while a forge is lit. Separate from the finish sounds.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
			FinishHissPrefab = cfg.Bind<string>("Sound", "FinishHissPrefab", "sfx_kiln_produce", "Name of an extra sound prefab played with the bubbling when a bar comes out (a steam puff). Leave empty for none.");
			FinishBubbling = cfg.Bind<bool>("Sound", "FinishBubbling", true, "Play the cauldron bubbling when a bar comes out. Turn off (with FinishCookingDone) to test an extra sound on its own via FinishHissPrefab.");
			FinishCookingDone = cfg.Bind<bool>("Sound", "FinishCookingDone", true, "Play the meat rack 'done' sound when a bar comes out.");
			BarCoal = cfg.Bind<float>("Smelting", "BarCoal", 0.6667f, new ConfigDescription("Coal used to alloy one bar into Bronze (used by the Bronze run).", (AcceptableValueBase)(object)val4, Array.Empty<object>()));
		}

		public static int SlotCap(int row, int itemMaxStack)
		{
			int num = row switch
			{
				1 => FuelSlotMax.Value, 
				0 => InputSlotMax.Value, 
				_ => OutputSlotMax.Value, 
			};
			if (num <= 0)
			{
				return itemMaxStack;
			}
			return Mathf.Min(num, itemMaxStack);
		}
	}
	internal class StationContainer : MonoBehaviour
	{
		internal enum ViewMode
		{
			Main,
			Input,
			Fuel
		}

		internal enum RowKind
		{
			Plain,
			Fuel,
			Output
		}

		private class LightRef
		{
			public Light L;

			public Traverse Base;

			public float Orig;

			public bool Flicker;
		}

		private class PartRef
		{
			public ParticleSystem P;

			public float Rate;

			public bool IsGlow;

			public Material[] Mats;

			public List<Color[]> Orig;

			public string[] Props;

			public bool Backdrop;
		}

		private class GlowRef
		{
			public Renderer R;

			public Material[] Mats;

			public List<Color[]> Orig = new List<Color[]>();

			public string[] Props;
		}

		private enum StepResult
		{
			Idle,
			Blocked,
			Progressed
		}

		private class Run
		{
			public readonly List<string> Inputs = new List<string>();

			public float Seconds;

			public int Coal;

			public ItemData Product;

			public int Count = 1;
		}

		public const int Width = 4;

		public const int Height = 3;

		public const int RowInput = 0;

		public const int RowFuel = 1;

		public const int RowOutput = 2;

		internal static readonly ConditionalWeakTable<Inventory, StationContainer> Registry = new ConditionalWeakTable<Inventory, StationContainer>();

		internal static bool MachineWrite;

		private readonly HashSet<string> _inputs = new HashSet<string>();

		private string _fuelName = "";

		private Smelter _smelter;

		private Container _container;

		private Inventory _registered;

		private ZNetView _nview;

		private Traverse _updateRoof;

		private Traverse _updateSmoke;

		private Traverse _updateState;

		private Traverse _haveRoof;

		private Traverse _blockedSmoke;

		private const string KeyItem = "ep_item";

		private const string KeyProgress = "ep_prog";

		private const string KeyTime = "ep_time";

		private const string KeyOn = "ep_on";

		private const string KeyActive = "ep_active";

		private const string KeyHotUntil = "ep_hot";

		private static readonly HashSet<string> _modelLogged = new HashSet<string>();

		internal static ViewMode PendingView = ViewMode.Main;

		internal static int PendingSerial;

		internal static float PendingUntil;

		internal static StationContainer OpenStation;

		internal static ViewMode OpenView = ViewMode.Main;

		private static readonly string[] PartColorProps = new string[3] { "_TintColor", "_Color", "_EmissionColor" };

		private readonly List<AudioSource> _fireSources = new List<AudioSource>();

		private readonly List<LightRef> _lights = new List<LightRef>();

		private readonly List<PartRef> _parts = new List<PartRef>();

		private bool _fireInit;

		private float _glowShown = -1f;

		private float _flamesShown = -1f;

		private readonly List<GlowRef> _glowMeshes = new List<GlowRef>();

		private readonly List<AudioSource> _loopSources = new List<AudioSource>();

		private readonly List<Traverse> _loopSounds = new List<Traverse>();

		private readonly List<Traverse> _fireSounds = new List<Traverse>();

		private static readonly string[] ColorProps = new string[2] { "_TintColor", "_EmissionColor" };

		private float _spin = -1f;

		private bool _spinHeld;

		private readonly Dictionary<Animator, bool[]> _animHas = new Dictionary<Animator, bool[]>();

		private float _coastEnd = -1f;

		private bool _downPhase;

		private const float StartSpeed = 0.35f;

		private const float SlowestSpeed = 0.2f;

		private const float RestTolerance = 0.012f;

		private const float CoastLimit = 25f;

		private const float CoastSeconds = 3.5f;

		private const float CoastFloor = 0.1f;

		private const float MaxSpeedChange = 0.5f;

		private readonly Dictionary<Animator, float> _animSpeed = new Dictionary<Animator, float>();

		private float _lastApplyT = -1f;

		private readonly Dictionary<AudioSource, float> _fireBase = new Dictionary<AudioSource, float>();

		private bool _fireWasOn;

		private int _soundLogs;

		private int _soundChanges;

		private string _soundKey = "";

		private float _lightFailUntil;

		private static EffectList _doneEffects;

		private static EffectList _hissEffects;

		private static bool _doneChecked;

		private static float _pitchOverride = -1f;

		private static float _gainOverride = -1f;

		private static bool _hissWarned;

		private static readonly HashSet<string> CopperFamily = new HashSet<string> { "CopperOre", "CopperScrap", "Copper" };

		private static readonly HashSet<string> TinFamily = new HashSet<string> { "TinOre", "EP_TinScrap", "Tin" };

		private const string BronzeTag = "BRONZE|";

		internal bool Ready { get; private set; }

		internal bool IsKiln { get; private set; }

		internal bool IsFrostKiln { get; private set; }

		internal float AutoCloseDistance { get; private set; }

		internal bool IsRefinery { get; private set; }

		private int FirstOutputRow
		{
			get
			{
				if (!IsKiln)
				{
					return 2;
				}
				return 1;
			}
		}

		internal bool PowerOn
		{
			get
			{
				if ((Object)(object)_nview != (Object)null && _nview.IsValid())
				{
					return _nview.GetZDO().GetInt("ep_on", 0) != 0;
				}
				return false;
			}
		}

		private bool CfgBubbling => StationConfig.FinishBubbling.Value;

		private bool CfgCookingDone
		{
			get
			{
				if (!IsRefinery)
				{
					return StationConfig.FinishCookingDone.Value;
				}
				return false;
			}
		}

		private string CfgHiss
		{
			get
			{
				if (IsRefinery)
				{
					if (StationConfig.RefineryThudPrefab.Value == null)
					{
						return "";
					}
					return StationConfig.RefineryThudPrefab.Value.Trim();
				}
				string text = ((StationConfig.FinishHissPrefab != null) ? StationConfig.FinishHissPrefab.Value : "");
				if (text == null)
				{
					return "";
				}
				return text.Trim();
			}
		}

		private void Start()
		{
			_smelter = ((Component)this).GetComponent<Smelter>();
			_container = ((Component)this).GetComponent<Container>();
			if ((Object)(object)_smelter == (Object)null || (Object)(object)_container == (Object)null)
			{
				return;
			}
			foreach (ItemConversion item in _smelter.m_conversion)
			{
				if (item != null && (Object)(object)item.m_from != (Object)null)
				{
					_inputs.Add(((Object)((Component)item.m_from).gameObject).name);
				}
			}
			string text = PrefabName();
			if (text == "smelter")
			{
				_inputs.Add("Copper");
				_inputs.Add("Tin");
			}
			IsFrostKiln = text == "piece_FrostKiln";
			IsKiln = text == "charcoal_kiln" || IsFrostKiln;
			IsRefinery = text == "eitrrefinery";
			if (IsFrostKiln && (Object)(object)_smelter.m_fuelItem != (Object)null)
			{
				_inputs.Add(((Object)((Component)_smelter.m_fuelItem).gameObject).name);
			}
			_fuelName = ((!IsKiln && (Object)(object)_smelter.m_fuelItem != (Object)null) ? ((Object)((Component)_smelter.m_fuelItem).gameObject).name : "");
			_nview = ((Component)this).GetComponent<ZNetView>();
			_updateRoof = Traverse.Create((object)_smelter).Method("UpdateRoof", Array.Empty<object>());
			_updateSmoke = Traverse.Create((object)_smelter).Method("UpdateSmoke", Array.Empty<object>());
			_updateState = Traverse.Create((object)_smelter).Method("UpdateState", Array.Empty<object>());
			_haveRoof = Traverse.Create((object)_smelter).Field("m_haveRoof");
			_blockedSmoke = Traverse.Create((object)_smelter).Field("m_blockedSmoke");
			Ready = (Object)(object)_nview != (Object)null && (IsKiln || _fuelName.Length > 0);
			_registered = _container.GetInventory();
			if (_registered != null)
			{
				Registry.Remove(_registered);
				Registry.Add(_registered, this);
			}
			if (IsRefinery)
			{
				WireView(_smelter.m_addOreSwitch, ViewMode.Input, "Softtissue");
				WireView(_smelter.m_addWoodSwitch, ViewMode.Fuel, "Sap");
			}
			else
			{
				Wire(_smelter.m_addOreSwitch);
				Wire(_smelter.m_addWoodSwitch);
			}
			Wire(_smelter.m_emptyOreSwitch);
			if (IsFrostKiln)
			{
				AutoCloseDistance = ReachOf(_smelter.m_addOreSwitch, _smelter.m_addWoodSwitch, _smelter.m_emptyOreSwitch);
				if (_modelLogged.Add("close:" + text))
				{
					Plugin.Log.LogInfo((object)("Window stays open up to " + AutoCloseDistance.ToString("F1") + " m from the centre of " + text + "."));
				}
			}
			((MonoBehaviour)this).Invoke("MigrateLegacy", 0.5f);
			if (_modelLogged.Add(text))
			{
				Plugin.Log.LogInfo((object)("Station model log for " + text + ":"));
				LogModel();
			}
		}

		private float ReachOf(params Switch[] switches)
		{
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			float num = 0f;
			foreach (Switch val in switches)
			{
				if ((Object)(object)val != (Object)null)
				{
					num = Mathf.Max(num, Vector3.Distance(((Component)this).transform.position, ((Component)val).transform.position));
				}
			}
			return num + 2.5f;
		}

		internal int ViewRow()
		{
			if ((Object)(object)OpenStation != (Object)(object)this)
			{
				return -1;
			}
			if (OpenView != ViewMode.Input)
			{
				if (OpenView != ViewMode.Fuel)
				{
					return -1;
				}
				return 1;
			}
			return 0;
		}

		internal int CapFor(int row, int itemMaxStack)
		{
			if (IsRefinery && row == 0)
			{
				return Capped(StationConfig.RefineryInputSlotMax.Value, itemMaxStack);
			}
			if (IsRefinery && row == 1)
			{
				return Capped(StationConfig.RefineryFuelSlotMax.Value, itemMaxStack);
			}
			return StationConfig.SlotCap(row, itemMaxStack);
		}

		private static int Capped(int cfg, int itemMaxStack)
		{
			if (cfg <= 0)
			{
				return itemMaxStack;
			}
			return Mathf.Min(cfg, itemMaxStack);
		}

		private static void EnlargeColliders(GameObject go, float factor)
		{
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00da: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)go == (Object)null || factor <= 1.001f)
			{
				return;
			}
			try
			{
				Collider[] componentsInChildren = go.GetComponentsInChildren<Collider>(true);
				foreach (Collider val in componentsInChildren)
				{
					if (!val.isTrigger)
					{
						BoxCollider val2 = (BoxCollider)(object)((val is BoxCollider) ? val : null);
						SphereCollider val3 = (SphereCollider)(object)((val is SphereCollider) ? val : null);
						CapsuleCollider val4 = (CapsuleCollider)(object)((val is CapsuleCollider) ? val : null);
						MeshCollider val5 = (MeshCollider)(object)((val is MeshCollider) ? val : null);
						if ((Object)(object)val2 != (Object)null)
						{
							val2.size *= factor;
						}
						else if ((Object)(object)val3 != (Object)null)
						{
							val3.radius *= factor;
						}
						else if ((Object)(object)val4 != (Object)null)
						{
							val4.radius *= factor;
							val4.height *= factor;
						}
						else if ((Object)(object)val5 != (Object)null && (Object)(object)val5.sharedMesh != (Object)null)
						{
							Bounds bounds = val5.sharedMesh.bounds;
							BoxCollider obj = ((Component)val).gameObject.AddComponent<BoxCollider>();
							obj.center = ((Bounds)(ref bounds)).center;
							obj.size = ((Bounds)(ref bounds)).size * factor;
						}
						Plugin.Log.LogInfo((object)("Softtissue spot: enlarged " + ((object)val).GetType().Name + " on " + ((Object)((Component)val).gameObject).name + " x" + factor));
					}
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Could not enlarge the Softtissue spot: " + ex.Message));
			}
		}

		private void WireView(Switch sw, ViewMode view, string what)
		{
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Expected O, but got Unknown
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Expected O, but got Unknown
			if ((Object)(object)sw == (Object)null)
			{
				return;
			}
			if (view == ViewMode.Input)
			{
				EnlargeColliders(((Component)sw).gameObject, StationConfig.RefinerySoftSwitchSize.Value);
			}
			sw.m_onUse = (Callback)delegate(Switch caller, Humanoid user, ItemData item)
			{
				if (item != null || (Object)(object)_container == (Object)null)
				{
					return false;
				}
				PendingView = view;
				PendingSerial++;
				PendingUntil = Time.time + 1f;
				return _container.Interact(user, false, false);
			};
			sw.m_onHover = (TooltipCallback)(() => Localization.instance.Localize((((Object)(object)_smelter != (Object)null && !string.IsNullOrEmpty(_smelter.m_name)) ? _smelter.m_name : "$piece_eitrrefinery") + " (" + what + ")\n[<color=yellow><b>$KEY_Use</b></color>] $piece_container_open"));
		}

		internal bool MainLocked(int row)
		{
			if (IsRefinery && (Object)(object)OpenStation == (Object)(object)this && OpenView == ViewMode.Main)
			{
				return row != 2;
			}
			return false;
		}

		internal static bool LockBlock(Inventory from, ItemData item)
		{
			if (from == null || item == null || !Registry.TryGetValue(from, out var value))
			{
				return false;
			}
			if (!value.MainLocked(item.m_gridPos.y))
			{
				return false;
			}
			if ((Object)(object)Player.m_localPlayer != (Object)null)
			{
				((Character)Player.m_localPlayer).Message((MessageType)2, "Use the Softtissue and Sap spots to change these", 0, (Sprite)null, false);
			}
			return true;
		}

		internal static bool ViewMoveAll(Inventory from, Inventory to)
		{
			if (from == null || to == null || !Registry.TryGetValue(from, out var value))
			{
				return false;
			}
			if (Registry.TryGetValue(to, out var _))
			{
				return false;
			}
			int num = value.ViewRow();
			if (num < 0 && value.IsRefinery && (Object)(object)OpenStation == (Object)(object)value && OpenView == ViewMode.Main)
			{
				num = 2;
			}
			if (num < 0)
			{
				return false;
			}
			foreach (ItemData item in new List<ItemData>(from.GetAllItems()))
			{
				if (item.m_gridPos.y == num && to.AddItem(item))
				{
					from.RemoveItem(item);
				}
			}
			return true;
		}

		internal static bool MoveEverything(Inventory from, Inventory to)
		{
			if (from == null || to == null || !Registry.TryGetValue(from, out var value))
			{
				return false;
			}
			if (Registry.TryGetValue(to, out value))
			{
				return false;
			}
			foreach (ItemData item in new List<ItemData>(from.GetAllItems()))
			{
				if (to.AddItem(item))
				{
					from.RemoveItem(item);
				}
			}
			return true;
		}

		internal RowKind KindOf(int row)
		{
			if (IsKiln)
			{
				if (row != 0)
				{
					return RowKind.Output;
				}
				return RowKind.Fuel;
			}
			return row switch
			{
				2 => RowKind.Output, 
				1 => RowKind.Fuel, 
				_ => RowKind.Plain, 
			};
		}

		private void LogModel()
		{
			//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0263: Unknown result type (might be due to invalid IL or missing references)
			//IL_0268: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				Smelter smelter = _smelter;
				Plugin.Log.LogInfo((object)("Forge model: enabled=" + NameOf(smelter.m_enabledObject) + ", disabled=" + NameOf(smelter.m_disabledObject) + ", haveFuel=" + NameOf(smelter.m_haveFuelObject) + ", haveOre=" + NameOf(smelter.m_haveOreObject) + ", noOre=" + NameOf(smelter.m_noOreObject)));
				if (smelter.m_animators != null)
				{
					Animator[] animators = smelter.m_animators;
					foreach (Animator val in animators)
					{
						if ((Object)(object)val == (Object)null)
						{
							continue;
						}
						List<string> list = new List<string>();
						AnimatorControllerParameter[] parameters = val.parameters;
						foreach (AnimatorControllerParameter val2 in parameters)
						{
							list.Add(val2.name + ":" + ((object)val2.type/*cast due to .constrained prefix*/).ToString());
						}
						List<string> list2 = new List<string>();
						if ((Object)(object)val.runtimeAnimatorController != (Object)null)
						{
							AnimationClip[] animationClips = val.runtimeAnimatorController.animationClips;
							foreach (AnimationClip val3 in animationClips)
							{
								list2.Add(((Object)val3).name);
							}
						}
						Plugin.Log.LogInfo((object)("Forge model animator: " + PathOf(((Component)val).transform) + ", controller " + (((Object)(object)val.runtimeAnimatorController != (Object)null) ? ((Object)val.runtimeAnimatorController).name : "none") + ", params [" + string.Join(", ", list.ToArray()) + "], clips [" + string.Join(", ", list2.ToArray()) + "]"));
					}
				}
				Renderer[] componentsInChildren = ((Component)this).GetComponentsInChildren<Renderer>(true);
				foreach (Renderer val4 in componentsInChildren)
				{
					if (val4 is ParticleSystemRenderer)
					{
						continue;
					}
					Material[] sharedMaterials = val4.sharedMaterials;
					foreach (Material val5 in sharedMaterials)
					{
						if (!((Object)(object)val5 == (Object)null))
						{
							string text = (val5.HasProperty("_EmissionColor") ? ("emission " + ((object)val5.GetColor("_EmissionColor")/*cast due to .constrained prefix*/).ToString() + (val5.IsKeywordEnabled("_EMISSION") ? " (on)" : " (off)")) : "no emission");
							Plugin.Log.LogInfo((object)("Forge model renderer: " + PathOf(((Component)val4).transform) + ", material " + ((Object)val5).name + ", shader " + ((Object)val5.shader).name + ", " + text));
						}
					}
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Forge model log failed: " + ex.Message));
			}
		}

		private static string NameOf(GameObject go)
		{
			if (!((Object)(object)go != (Object)null))
			{
				return "none";
			}
			return ((Object)go).name;
		}

		private string PathOf(Transform t)
		{
			string text = ((Object)t).name;
			while ((Object)(object)t.parent != (Object)null && (Object)(object)t.parent != (Object)(object)((Component)this).transform)
			{
				t = t.parent;
				text = ((Object)t).name + "/" + text;
			}
			return text;
		}

		private void OnDestroy()
		{
			if (_registered != null)
			{
				Registry.Remove(_registered);
			}
		}

		private string PrefabName()
		{
			return ((Object)((Component)this).gameObject).name.Replace("(Clone)", "").Trim();
		}

		private void Wire(Switch sw)
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Expected O, but got Unknown
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Expected O, but got Unknown
			if (!((Object)(object)sw == (Object)null))
			{
				sw.m_onUse = new Callback(OnUse);
				sw.m_onHover = new TooltipCallback(OnHover);
			}
		}

		private bool OnUse(Switch caller, Humanoid user, ItemData item)
		{
			if (item != null || (Object)(object)_container == (Object)null)
			{
				return false;
			}
			return _container.Interact(user, false, false);
		}

		private string OnHover()
		{
			string text = (((Object)(object)_smelter != (Object)null && !string.IsNullOrEmpty(_smelter.m_name)) ? _smelter.m_name : "$piece_smelter");
			return Localization.instance.Localize(text + "\n[<color=yellow><b>$KEY_Use</b></color>] $piece_container_open");
		}

		private void MigrateLegacy()
		{
			try
			{
				ZNetView component = ((Component)this).GetComponent<ZNetView>();
				if ((Object)(object)component == (Object)null || !component.IsValid() || !component.IsOwner() || (Object)(object)_container == (Object)null || (Object)(object)_smelter == (Object)null)
				{
					return;
				}
				Inventory inventory = _container.GetInventory();
				if (inventory == null)
				{
					return;
				}
				ZDO zDO = component.GetZDO();
				float num = zDO.GetFloat(ZDOVars.s_fuel, 0f);
				int num2 = Mathf.FloorToInt(num);
				int num3 = zDO.GetInt(ZDOVars.s_queued, 0);
				int num4 = zDO.GetInt(ZDOVars.s_spawnAmount, 0);
				if (num2 <= 0 && num3 <= 0 && num4 <= 0)
				{
					return;
				}
				if (num2 > 0)
				{
					Place(inventory, _smelter.m_fuelItem, num2, output: false);
				}
				Dictionary<string, int> dictionary = new Dictionary<string, int>();
				for (int i = 0; i < num3; i++)
				{
					string text = zDO.GetString("item" + i, "");
					if (!string.IsNullOrEmpty(text))
					{
						dictionary.TryGetValue(text, out var value);
						dictionary[text] = value + 1;
					}
				}
				foreach (KeyValuePair<string, int> item in dictionary)
				{
					ItemConversion val = FindConversion(item.Key);
					if (val == null || (Object)(object)val.m_from == (Object)null)
					{
						Plugin.Log.LogWarning((object)("Smelter conversion: unknown queued item " + item.Key + " x" + item.Value + ", skipped."));
					}
					else
					{
						Place(inventory, val.m_from, item.Value, output: false);
					}
				}
				if (num4 > 0)
				{
					ItemConversion val2 = FindConversion(zDO.GetString(ZDOVars.s_spawnOre, ""));
					if (val2 != null && (Object)(object)val2.m_to != (Object)null)
					{
						Place(inventory, val2.m_to, num4, output: true);
					}
					else
					{
						Plugin.Log.LogWarning((object)"Smelter conversion: unknown finished item, skipped.");
					}
				}
				zDO.Set(ZDOVars.s_fuel, num - (float)num2);
				for (int j = 0; j < num3; j++)
				{
					zDO.Set("item" + j, "");
				}
				zDO.Set(ZDOVars.s_queued, 0, false);
				zDO.Set(ZDOVars.s_bakeTimer, 0f);
				zDO.Set(ZDOVars.s_spawnOre, "");
				zDO.Set(ZDOVars.s_spawnAmount, 0, false);
				Traverse.Create((object)_container).Method("Save", Array.Empty<object>()).GetValue();
				Plugin.Log.LogInfo((object)("Converted a smelter's contents into the station screen: " + num2 + " coal, " + num3 + " queued, " + num4 + " finished."));
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("Smelter conversion failed: " + ex));
			}
		}

		private ItemConversion FindConversion(string fromName)
		{
			if (string.IsNullOrEmpty(fromName))
			{
				return null;
			}
			foreach (ItemConversion item in _smelter.m_conversion)
			{
				if (item != null && (Object)(object)item.m_from != (Object)null && ((Object)((Component)item.m_from).gameObject).name == fromName)
				{
					return item;
				}
			}
			if (IsFrostKiln && (Object)(object)_smelter.m_fuelItem != (Object)null && fromName == ((Object)((Component)_smelter.m_fuelItem).gameObject).name)
			{
				foreach (ItemConversion item2 in _smelter.m_conversion)
				{
					if (item2 != null && (Object)(object)item2.m_from == (Object)null && (Object)(object)item2.m_to != (Object)null)
					{
						return item2;
					}
				}
			}
			return null;
		}

		private void Place(Inventory inv, ItemDrop prefab, int count, bool output)
		{
			//IL_0087: 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_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)prefab == (Object)null) && count > 0)
			{
				ItemData val = prefab.m_itemData.Clone();
				val.m_dropPrefab = ((Component)prefab).gameObject;
				val.m_stack = count;
				if (output)
				{
					AddToOutput(inv, val);
				}
				else
				{
					AddToRow(inv, val);
				}
				if (val.m_stack > 0)
				{
					Transform outputPoint = _smelter.m_outputPoint;
					Vector3 val2 = (((Object)(object)outputPoint != (Object)null) ? outputPoint.position : (((Component)this).transform.position + Vector3.up * 0.5f));
					Quaternion val3 = (((Object)(object)outputPoint != (Object)null) ? outputPoint.rotation : Quaternion.identity);
					Plugin.Log.LogInfo((object)("Smelter conversion: no room for " + val.m_stack + " " + ((Object)((Component)prefab).gameObject).name + ", dropped in front of the smelter."));
					ItemDrop.DropItem(val, 0, val2, val3);
				}
			}
		}

		internal void AddToOutput(Inventory inv, ItemData item)
		{
			MachineWrite = true;
			try
			{
				int num = StationConfig.SlotCap(2, item.m_shared.m_maxStackSize);
				for (int i = 0; i < 2; i++)
				{
					for (int j = FirstOutputRow; j <= 2; j++)
					{
						for (int k = 0; k < 4; k++)
						{
							if (item.m_stack <= 0)
							{
								break;
							}
							ItemData itemAt = inv.GetItemAt(k, j);
							if ((i != 0 || (itemAt != null && itemAt.IsSameType(item))) && (i != 1 || itemAt == null))
							{
								int num2 = num - (itemAt?.m_stack ?? 0);
								if (num2 > 0)
								{
									StationPatches.AddAt(inv, item, Mathf.Min(num2, item.m_stack), k, j);
								}
							}
						}
					}
				}
			}
			finally
			{
				MachineWrite = false;
			}
		}

		internal void TogglePower()
		{
			if (!Ready || (Object)(object)_nview == (Object)null || !_nview.IsValid())
			{
				return;
			}
			if (!_nview.IsOwner())
			{
				_nview.ClaimOwnership();
			}
			ZDO zDO = _nview.GetZDO();
			if (!PowerOn)
			{
				Inventory val = (((Object)(object)_container != (Object)null) ? _container.GetInventory() : null);
				if (val == null || !StartNext(zDO, val))
				{
					_lightFailUntil = Time.time + 6f;
					return;
				}
				zDO.Set("ep_on", 1);
				PlayAddEffects();
				return;
			}
			bool num = zDO.GetInt("ep_active", 0) == 1 || zDO.GetString("ep_item", "").Length > 0;
			zDO.Set("ep_on", 0);
			zDO.Set("ep_item", "");
			zDO.Set("ep_prog", 0f);
			zDO.Set("ep_active", 0);
			if (num)
			{
				zDO.Set("ep_hot", ZNet.instance.GetTime().Ticks + (long)(StationConfig.CoolDownSeconds.Value * 10000000f));
			}
		}

		private void PlayAddEffects()
		{
			try
			{
				PlayList(_smelter.m_oreAddedEffects);
				PlayList(_smelter.m_fuelAddedEffects);
				Traverse val = Traverse.Create((object)_smelter).Field("m_addedOreTime");
				if (val.FieldExists())
				{
					val.SetValue((object)Time.time);
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Add effects failed: " + ex.Message));
			}
		}

		private void PlayList(EffectList list)
		{
			//IL_001c: 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 (list != null && list.m_effectPrefabs != null && list.m_effectPrefabs.Length != 0)
			{
				list.Create(((Component)this).transform.position, ((Component)this).transform.rotation, (Transform)null, 1f, -1, default(ZDOID));
			}
		}

		internal bool HasItemsInRow(int row)
		{
			Inventory val = (((Object)(object)_container != (Object)null) ? _container.GetInventory() : null);
			if (val == null)
			{
				return false;
			}
			foreach (ItemData allItem in val.GetAllItems())
			{
				if (allItem.m_gridPos.y == row && allItem.m_stack > 0)
				{
					return true;
				}
			}
			return false;
		}

		internal bool IsRunning()
		{
			if ((Object)(object)_nview != (Object)null && _nview.IsValid())
			{
				return _nview.GetZDO().GetInt("ep_active", 0) == 1;
			}
			return false;
		}

		internal bool IsHot()
		{
			if ((Object)(object)_nview == (Object)null || !_nview.IsValid() || (Object)(object)ZNet.instance == (Object)null)
			{
				return false;
			}
			ZDO zDO = _nview.GetZDO();
			if (zDO.GetInt("ep_active", 0) == 1)
			{
				return true;
			}
			return ZNet.instance.GetTime().Ticks < zDO.GetLong("ep_hot", 0L);
		}

		internal static void FireTargets(float heat, out float glow, out float flames)
		{
			glow = Mathf.SmoothStep(0f, 1f, Mathf.Clamp01(heat / 0.5f));
			flames = Mathf.SmoothStep(0f, 1f, Mathf.Clamp01((heat - 0.5f) / 0.4f));
		}

		private bool Working()
		{
			if ((Object)(object)_nview != (Object)null && _nview.IsValid())
			{
				return _nview.GetZDO().GetInt("ep_active", 0) == 1;
			}
			return false;
		}

		private void GlideSpeed(Animator an, float target, float step, bool noSpeedUp)
		{
			if (!_animSpeed.TryGetValue(an, out var value) || !_spinHeld)
			{
				value = target;
			}
			if (noSpeedUp && target > value)
			{
				target = value;
			}
			value = Mathf.MoveTowards(value, target, step);
			_animSpeed[an] = value;
			an.speed = value;
		}

		private float HeldActiveF(bool coasting)
		{
			if (IsRefinery)
			{
				if (!(_downPhase || coasting))
				{
					return Mathf.Clamp01(_spin / 0.1f);
				}
				return 1f;
			}
			if (!coasting)
			{
				return Mathf.Clamp01(_spin);
			}
			return 0.2f;
		}

		private void ApplySpin()
		{
			//IL_01ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_0204: Unknown result type (might be due to invalid IL or missing references)
			//IL_0122: Unknown result type (might be due to invalid IL or missing references)
			//IL_0128: Invalid comparison between Unknown and I4
			//IL_0144: Unknown result type (might be due to invalid IL or missing references)
			//IL_014a: Invalid comparison between Unknown and I4
			if (_spin < 0f || (Object)(object)_smelter == (Object)null || _smelter.m_animators == null)
			{
				return;
			}
			bool flag = _spin <= 0.01f && !Working();
			if (flag && _spinHeld && _coastEnd < 0f)
			{
				_coastEnd = Time.time + 25f;
			}
			if (!flag)
			{
				_coastEnd = -1f;
			}
			bool flag2 = false;
			float step = ((_lastApplyT < 0f) ? 0f : (0.5f * Mathf.Max(0f, Time.time - _lastApplyT)));
			_lastApplyT = Time.time;
			Animator[] animators = _smelter.m_animators;
			foreach (Animator val in animators)
			{
				if ((Object)(object)val == (Object)null)
				{
					continue;
				}
				if (!_animHas.TryGetValue(val, out var value))
				{
					value = new bool[2];
					AnimatorControllerParameter[] parameters = val.parameters;
					foreach (AnimatorControllerParameter val2 in parameters)
					{
						if (val2.name == "active" && (int)val2.type == 4)
						{
							value[0] = true;
						}
						if (val2.name == "activef" && (int)val2.type == 1)
						{
							value[1] = true;
						}
					}
					_animHas[val] = value;
				}
				if (!value[0] && !value[1])
				{
					continue;
				}
				if (!flag)
				{
					if (value[0])
					{
						val.SetBool("active", true);
					}
					if (value[1])
					{
						val.SetFloat("activef", HeldActiveF(coasting: false));
					}
					GlideSpeed(val, _downPhase ? Mathf.Max(0.2f, _spin) : Mathf.Lerp(0.35f, 1f, _spin), step, noSpeedUp: false);
					_spinHeld = true;
				}
				else
				{
					if (!_spinHeld)
					{
						continue;
					}
					AnimatorStateInfo currentAnimatorStateInfo = val.GetCurrentAnimatorStateInfo(0);
					float num = ((AnimatorStateInfo)(ref currentAnimatorStateInfo)).normalizedTime - Mathf.Floor(((AnimatorStateInfo)(ref currentAnimatorStateInfo)).normalizedTime);
					if (!(num < 0.012f) && !(num > 0.988f) && !val.IsInTransition(0) && Time.time < _coastEnd)
					{
						if (value[0])
						{
							val.SetBool("active", true);
						}
						if (value[1])
						{
							val.SetFloat("activef", HeldActiveF(coasting: true));
						}
						float num2 = ((AnimatorStateInfo)(ref currentAnimatorStateInfo)).length * Mathf.Max(0.01f, val.speed);
						float num3 = 1f - num;
						GlideSpeed(val, Mathf.Clamp(num3 * num2 / 3.5f, 0.1f, 0.6f), step, noSpeedUp: true);
						flag2 = true;
					}
					else
					{
						val.speed = 0.35f;
						_animSpeed.Remove(val);
						if (value[0])
						{
							val.SetBool("active", false);
						}
						if (value[1])
						{
							val.SetFloat("activef", 0f);
						}
					}
				}
			}
			if (flag && !flag2)
			{
				_spinHeld = false;
				_coastEnd = -1f;
			}
		}

		private void LateUpdate()
		{
			//IL_02fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0300: Unknown result type (might be due to invalid IL or missing references)
			//IL_0316: Unknown result type (might be due to invalid IL or missing references)
			//IL_0324: Unknown result type (might be due to invalid IL or missing references)
			//IL_0332: Unknown result type (might be due to invalid IL or missing references)
			//IL_0340: Unknown result type (might be due to invalid IL or missing references)
			//IL_034e: Unknown result type (might be due to invalid IL or missing references)
			//IL_058b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0590: Unknown result type (might be due to invalid IL or missing references)
			//IL_0643: Unknown result type (might be due to invalid IL or missing references)
			//IL_0648: Unknown result type (might be due to invalid IL or missing references)
			//IL_065e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0668: Unknown result type (might be due to invalid IL or missing references)
			//IL_0672: Unknown result type (might be due to invalid IL or missing references)
			//IL_067c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0686: Unknown result type (might be due to invalid IL or missing references)
			if (!Ready || (Object)(object)_smelter == (Object)null || (Object)(object)_smelter.m_enabledObject == (Object)null)
			{
				return;
			}
			bool flag = Working();
			if (_spin < 0f)
			{
				_spin = (flag ? 1f : 0f);
			}
			float num = Mathf.Max(0.1f, StationConfig.WindUpSeconds.Value);
			float num2 = Mathf.Max(0.1f, StationConfig.WindDownMatchesCoolDown.Value ? StationConfig.CoolDownSeconds.Value : StationConfig.WindDownSeconds.Value);
			_downPhase = !flag;
			_spin = (flag ? Mathf.Min(1f, _spin + Time.deltaTime / num) : Mathf.Max(0f, _spin - Time.deltaTime / num2));
			if (StationConfig.WindAnimation.Value)
			{
				ApplySpin();
			}
			FireTargets(HeatLevel(), out var glow, out var flames);
			if (_glowShown < 0f)
			{
				_glowShown = glow;
				_flamesShown = flames;
			}
			float deltaTime = Time.deltaTime;
			_flamesShown = ((flames > _flamesShown) ? Mathf.Min(flames, _flamesShown + deltaTime / 3f) : flames);
			if (glow > _glowShown)
			{
				float num3 = ((flames > 0.01f && _glowShown < 0.05f) ? Mathf.Clamp01((_flamesShown - 0.35f) / 0.3f) : 1f);
				_glowShown = Mathf.Min(glow, _glowShown + deltaTime / 4f * num3);
			}
			else
			{
				_glowShown = glow;
			}
			GameObject enabledObject = _smelter.m_enabledObject;
			if (!_fireInit)
			{
				if (!enabledObject.activeInHierarchy)
				{
					return;
				}
				CollectFire(enabledObject);
				_fireInit = true;
			}
			foreach (LightRef light in _lights)
			{
				if (!((Object)(object)light.L == (Object)null))
				{
					float num4 = light.Orig * _glowShown;
					if (light.Flicker)
					{
						light.Base.SetValue((object)num4);
					}
					else
					{
						light.L.intensity = num4;
					}
				}
			}
			foreach (GlowRef glowMesh in _glowMeshes)
			{
				if ((Object)(object)glowMesh.R == (Object)null)
				{
					continue;
				}
				glowMesh.R.enabled = _glowShown > 0.01f;
				for (int i = 0; i < glowMesh.Mats.Length; i++)
				{
					if ((Object)(object)glowMesh.Mats[i] == (Object)null)
					{
						continue;
					}
					for (int j = 0; j < glowMesh.Props.Length; j++)
					{
						if (glowMesh.Props[j] != null && glowMesh.Mats[i].HasProperty(glowMesh.Props[j]))
						{
							Color val = glowMesh.Orig[i][j];
							glowMesh.Mats[i].SetColor(glowMesh.Props[j], new Color(val.r * _glowShown, val.g * _glowShown, val.b * _glowShown, val.a * _glowShown));
						}
					}
				}
			}
			foreach (Traverse fireSound in _fireSounds)
			{
				fireSound.SetValue((object)(_flamesShown * StationConfig.FireCrackleVolume.Value));
			}
			float num5 = (StationConfig.WindAnimation.Value ? Mathf.Clamp01(_spin) : Mathf.Clamp01(_flamesShown));
			foreach (Traverse loopSound in _loopSounds)
			{
				loopSound.SetValue((object)(num5 * StationConfig.RefineryLoopVolume.Value));
			}
			foreach (AudioSource loopSource in _loopSources)
			{
				if (!((Object)(object)loopSource == (Object)null))
				{
					float num6 = (loopSource.volume = num5 * StationConfig.RefineryLoopVolume.Value);
					if (StationConfig.WindAnimation.Value)
					{
						loopSource.pitch = Mathf.Lerp(0.6f, 1f, Mathf.Clamp01(_spin));
					}
					if (num6 > 0.01f && enabledObject.activeInHierarchy && ((Behaviour)loopSource).enabled && !loopSource.isPlaying)
					{
						loopSource.Play();
					}
				}
			}
			foreach (PartRef part in _parts)
			{
				if ((Object)(object)part.P == (Object)null)
				{
					continue;
				}
				float num8 = (part.IsGlow ? _glowShown : _flamesShown);
				if (part.Backdrop)
				{
					num8 = Mathf.Min(_glowShown, Mathf.SmoothStep(0f, 1f, Mathf.Clamp01(HeatLevel() / 0.25f)));
				}
				EmissionModule emission = part.P.emission;
				((EmissionModule)(ref emission)).rateOverTimeMultiplier = part.Rate * num8;
				((EmissionModule)(ref emission)).enabled = num8 > 0.02f;
				if (part.Mats == null)
				{
					continue;
				}
				ParticleSystemRenderer component = ((Component)part.P).GetComponent<ParticleSystemRenderer>();
				if ((Object)(object)component != (Object)null)
				{
					((Renderer)component).enabled = num8 > 0.01f;
				}
				for (int k = 0; k < part.Mats.Length; k++)
				{
					if ((Object)(object)part.Mats[k] == (Object)null)
					{
						continue;
					}
					for (int l = 0; l < part.Props.Length; l++)
					{
						if (part.Props[l] != null && part.Mats[k].HasProperty(part.Props[l]))
						{
							Color val2 = part.Orig[k][l];
							part.Mats[k].SetColor(part.Props[l], new Color(val2.r * num8, val2.g * num8, val2.b * num8, val2.a * num8));
						}
					}
				}
			}
			foreach (AudioSource fireSource in _fireSources)
			{
				if (!((Object)(object)fireSource == (Object)null))
				{
					if (_flamesShown > 0.05f && enabledObject.activeInHierarchy && ((Behaviour)fireSource).enabled && fireSource.loop && !fireSource.isPlaying)
					{
						fireSource.Play();
					}
					if (!_fireBase.TryGetValue(fireSource, out var value))
					{
						value = 0.8f;
					}
					fireSource.mute = false;
					fireSource.volume = Mathf.Clamp01(_flamesShown) * StationConfig.FireCrackleVolume.Value * value;
				}
			}
			if (IsFrostKiln)
			{
				WatchFrostSound(enabledObject);
			}
		}

		private void WatchFrostSound(GameObject fire)
		{
			//IL_02a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_0559: Unknown result type (might be due to invalid IL or missing references)
			//IL_0564: Unknown result type (might be due to invalid IL or missing references)
			bool activeInHierarchy = fire.activeInHierarchy;
			bool flag = activeInHierarchy && !_fireWasOn;
			string text = activeInHierarchy + "|" + PowerOn;
			foreach (AudioSource fireSource in _fireSources)
			{
				if (!((Object)(object)fireSource == (Object)null))
				{
					text = text + "|" + fireSource.spatialBlend.ToString("0.0") + "|" + fireSource.maxDistance.ToString("0") + "|" + fireSource.isPlaying + "|" + (((Object)(object)((Component)fireSource).transform.parent == (Object)null) ? "root" : ((Object)((Component)fireSource).transform.parent).name);
				}
			}
			if (text != _soundKey && _soundChanges++ < 60)
			{
				_soundKey = text;
				StringBuilder stringBuilder = new StringBuilder("Frost Kiln sound change: object " + (activeInHierarchy ? "on" : "off") + ", lit " + PowerOn + ", heat " + HeatLevel().ToString("0.00") + ", flames " + _flamesShown.ToString("0.00"));
				foreach (AudioSource fireSource2 in _fireSources)
				{
					if ((Object)(object)fireSource2 != (Object)null)
					{
						stringBuilder.Append(" [").Append(((Object)fireSource2).name).Append(" blend ")
							.Append(fireSource2.spatialBlend.ToString("0.0"))
							.Append(" max ")
							.Append(fireSource2.maxDistance.ToString("0"))
							.Append(" playing ")
							.Append(fireSource2.isPlaying)
							.Append(" vol ")
							.Append(fireSource2.volume.ToString("0.00"))
							.Append(" dist ")
							.Append(Vector3.Distance(((Component)fireSource2).transform.position, ((Component)this).transform.position).ToString("0.0"))
							.Append(" enabled ")
							.Append(((Behaviour)fireSource2).enabled)
							.Append("]");
					}
				}
				Plugin.Log.LogInfo((object)stringBuilder.ToString());
			}
			_fireWasOn = activeInHierarchy;
			if (!flag)
			{
				return;
			}
			_fireSources.RemoveAll((AudioSource s) => (Object)(object)s == (Object)null);
			AudioSource[] componentsInChildren = fire.GetComponentsInChildren<AudioSource>(true);
			foreach (AudioSource val in componentsInChildren)
			{
				if (!_fireSources.Contains(val))
				{
					_fireSources.Add(val);
					Plugin.Log.LogInfo((object)("Frost Kiln sound: picked up source " + ((Object)val).name));
				}
			}
			if (_soundLogs++ >= 8)
			{
				return;
			}
			StringBuilder stringBuilder2 = new StringBuilder("Frost Kiln sound, lit #" + _soundLogs + ": ");
			componentsInChildren = ((Component)((Component)this).transform).GetComponentsInChildren<AudioSource>(true);
			foreach (AudioSource val2 in componentsInChildren)
			{
				stringBuilder2.Append("[").Append(((Object)val2).name).Append(" blend ")
					.Append(val2.spatialBlend.ToString("0.0"))
					.Append(" min ")
					.Append(val2.minDistance.ToString("0"))
					.Append(" max ")
					.Append(val2.maxDistance.ToString("0"))
					.Append(" vol ")
					.Append(val2.volume.ToString("0.00"))
					.Append(" playing ")
					.Append(val2.isPlaying)
					.Append("] ");
			}
			componentsInChildren = Object.FindObjectsOfType<AudioSource>();
			foreach (AudioSource val3 in componentsInChildren)
			{
				if (!((Object)(object)val3 == (Object)null) && !((Component)val3).transform.IsChildOf(((Component)this).transform) && (((Object)val3).name + (((Object)(object)val3.clip != (Object)null) ? ((Object)val3.clip).name : "")).ToLowerInvariant().Contains("frost") && Vector3.Distance(((Component)val3).transform.position, ((Component)this).transform.position) < 8f)
				{
					stringBuilder2.Append("LOOSE[").Append(((Object)val3).name).Append(" blend ")
						.Append(val3.spatialBlend.ToString("0.0"))
						.Append(" playing ")
						.Append(val3.isPlaying)
						.Append("] ");
				}
			}
			Plugin.Log.LogInfo((object)stringBuilder2.ToString());
		}

		private void CollectFire(GameObject fire)
		{
			//IL_05cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_05d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_0284: Unknown result type (might be due to invalid IL or missing references)
			//IL_0289: Unknown result type (might be due to invalid IL or missing references)
			//IL_0497: Unknown result type (might be due to invalid IL or missing references)
			//IL_049c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0764: Unknown result type (might be due to invalid IL or missing references)
			//IL_0769: Unknown result type (might be due to invalid IL or missing references)
			//IL_0788: Unknown result type (might be due to invalid IL or missing references)
			//IL_078d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0791: Unknown result type (might be due to invalid IL or missing references)
			//IL_0796: Unknown result type (might be due to invalid IL or missing references)
			//IL_06d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_06d5: Unknown result type (might be due to invalid IL or missing references)
			Light[] componentsInChildren = fire.GetComponentsInChildren<Light>(true);
			foreach (Light val in componentsInChildren)
			{
				LightFlicker component = ((Component)val).GetComponent<LightFlicker>();
				LightRef lightRef = new LightRef
				{
					L = val,
					Flicker = ((Object)(object)component != (Object)null)
				};
				if (lightRef.Flicker)
				{
					lightRef.Base = Traverse.Create((object)component).Field("m_baseIntensity");
					lightRef.Orig = lightRef.Base.GetValue<float>();
				}
				else
				{
					lightRef.Orig = val.intensity;
				}
				_lights.Add(lightRef);
			}
			ZSFX[] componentsInChildren2 = fire.GetComponentsInChildren<ZSFX>(true);
			foreach (ZSFX val2 in componentsInChildren2)
			{
				Traverse val3 = Traverse.Create((object)val2).Field("m_volumeModifier");
				if (!val3.FieldExists())
				{
					continue;
				}
				if (IsRefinery)
				{
					_loopSounds.Add(val3);
					AudioSource component2 = ((Component)val2).GetComponent<AudioSource>();
					if ((Object)(object)component2 != (Object)null)
					{
						_loopSources.Add(component2);
					}
				}
				else
				{
					_fireSounds.Add(val3);
					AudioSource component3 = ((Component)val2).GetComponent<AudioSource>();
					if ((Object)(object)component3 != (Object)null)
					{
						_fireSources.Add(component3);
						float value = (IsFrostKiln ? 0.8f : ((val2.m_minVol + val2.m_maxVol) * 0.5f));
						_fireBase[component3] = value;
						Plugin.Log.LogInfo((object)("Fire loop " + ((Object)val2).name + " on " + ((Object)this).name + ": game volume " + val2.m_minVol.ToString("0.00") + " to " + val2.m_maxVol.ToString("0.00") + ", base " + value.ToString("0.00")));
					}
				}
				if (IsFrostKiln)
				{
					AudioSource component4 = ((Component)val2).GetComponent<AudioSource>();
					if ((Object)(object)component4 != (Object)null)
					{
						Plugin.Log.LogInfo((object)("Frost Kiln loop source before: spatialBlend " + component4.spatialBlend.ToString("0.00") + ", min " + component4.minDistance.ToString("0.0") + ", max " + component4.maxDistance.ToString("0.0") + ", rolloff " + ((object)component4.rolloffMode/*cast due to .constrained prefix*/).ToString() + ", volume " + component4.volume.ToString("0.00") + ", loop " + component4.loop));
					}
					TimedDestruction component5 = ((Component)val2).GetComponent<TimedDestruction>();
					Plugin.Log.LogInfo((object)("Frost Kiln loop " + ((Object)val2).name + ": maxVol " + val2.m_maxVol.ToString("0.00") + ", fadeIn " + val2.m_fadeInDuration.ToString("0.0") + ", fadeOut " + val2.m_fadeOutDuration.ToString("0.0") + ", fadeOutOnAwake " + val2.m_fadeOutOnAwake + ", timedDestruction " + (((Object)(object)component5 != (Object)null) ? ("timeout " + Traverse.Create((object)component5).Field("m_timeout").GetValue<float>() + " onAwake " + Traverse.Create((object)component5).Field("m_triggerOnAwake").GetValue<bool>()) : "none")));
				}
			}
			MeshRenderer[] componentsInChildren3 = fire.GetComponentsInChildren<MeshRenderer>(true);
			foreach (MeshRenderer val4 in componentsInChildren3)
			{
				GlowRef glowRef = new GlowRef
				{
					R = (Renderer)(object)val4,
					Mats = ((Renderer)val4).materials,
					Props = ColorProps
				};
				List<string> list = new List<string>();
				for (int j = 0; j < glowRef.Mats.Length; j++)
				{
					Color[] array = (Color[])(object)new Color[ColorProps.Length];
					for (int k = 0; k < ColorProps.Length; k++)
					{
						if ((Object)(object)glowRef.Mats[j] != (Object)null && glowRef.Mats[j].HasProperty(ColorProps[k]))
						{
							array[k] = glowRef.Mats[j].GetColor(ColorProps[k]);
							list.Add(ColorProps[k]);
						}
					}
					glowRef.Orig.Add(array);
				}
				string text = ((glowRef.Mats.Length != 0 && (Object)(object)glowRef.Mats[0] != (Object)null) ? ((Object)glowRef.Mats[0].shader).name : "none");
				Plugin.Log.LogInfo((object)("Forge fire mesh " + ((Object)val4).name + ": shader " + text + ", colour properties: " + ((list.Count > 0) ? string.Join(", ", list.ToArray()) : "none found")));
				_glowMeshes.Add(glowRef);
			}
			ParticleSystem[] componentsInChildren4 = fire.GetComponentsInChildren<ParticleSystem>(true);
			foreach (ParticleSystem val5 in componentsInChildren4)
			{
				string text2 = ((Object)val5).name.ToLowerInvariant();
				PartRef obj = new PartRef
				{
					P = val5
				};
				EmissionModule emission = val5.emission;
				obj.Rate = ((EmissionModule)(ref emission)).rateOverTimeMultiplier;
				obj.IsGlow = text2.Contains("glow") || text2.Contains("heat") || text2.Contains("ember");
				obj.Backdrop = IsKiln && text2.Contains("flare");
				PartRef partRef = obj;
				ParticleSystemRenderer component6 = ((Component)val5).GetComponent<ParticleSystemRenderer>();
				if ((IsFrostKiln || partRef.Backdrop) && (Object)(object)component6 != (Object)null)
				{
					partRef.Mats = ((Renderer)component6).materials;
					partRef.Props = PartColorProps;
					partRef.Orig = new List<Color[]>();
					Material[] mats = partRef.Mats;
					foreach (Material val6 in mats)
					{
						Color[] array2 = (Color[])(object)new Color[PartColorProps.Length];
						for (int m = 0; m < PartColorProps.Length; m++)
						{
							if ((Object)(object)val6 != (Object)null && val6.HasProperty(PartColorProps[m]))
							{
								array2[m] = val6.GetColor(PartColorProps[m]);
							}
						}
						partRef.Orig.Add(array2);
					}
					ManualLogSource log = Plugin.Log;
					string[] obj2 = new string[11]
					{
						IsFrostKiln ? "Frost Kiln" : "Kiln",
						" particle ",
						((Object)val5).name,
						": rate ",
						partRef.Rate.ToString("0.0"),
						", max ",
						null,
						null,
						null,
						null,
						null
					};
					MainModule main = val5.main;
					obj2[6] = ((MainModule)(ref main)).maxParticles.ToString();
					obj2[7] = ", lifetime ";
					main = val5.main;
					MinMaxCurve startLifetime = ((MainModule)(ref main)).startLifetime;
					obj2[8] = ((MinMaxCurve)(ref startLifetime)).constantMax.ToString("0.0");
					obj2[9] = ", shader ";
					obj2[10] = ((partRef.Mats.Length != 0 && (Object)(object)partRef.Mats[0] != (Object)null) ? ((Object)partRef.Mats[0].shader).name : "none");
					log.LogInfo((object)string.Concat(obj2));
				}
				_parts.Add(partRef);
			}
			Plugin.Log.LogInfo((object)("Forge fire: " + _lights.Count + " light(s), " + _parts.Count + " particle effect(s) under " + ((Object)fire).name));
			LogTree(fire.transform, "  ");
		}

		private static void LogTree(Transform t, string indent)
		{
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Expected O, but got Unknown
			List<string> list = new List<string>();
			Component[] components = ((Component)t).GetComponents<Component>();
			foreach (Component val in components)
			{
				if ((Object)(object)val != (Object)null && !(val is Transform))
				{
					list.Add(((object)val).GetType().Name);
				}
			}
			Plugin.Log.LogInfo((object)("Forge fire tree:" + indent + ((Object)t).name + ((list.Count > 0) ? (" [" + string.Join(", ", list.ToArray()) + "]") : "")));
			foreach (Transform item in t)
			{
				LogTree(item, indent + "  ");
			}
		}

		internal float HeatLevel()
		{
			if ((Object)(object)_nview == (Object)null || !_nview.IsValid() || (Object)(object)ZNet.instance == (Object)null)
			{
				return 0f;
			}
			ZDO zDO = _nview.GetZDO();
			if (zDO.GetInt("ep_active", 0) == 1)
			{
				return 1f;
			}
			long num = zDO.GetLong("ep_hot", 0L);
			long ticks = ZNet.instance.GetTime().Ticks;
			if (num <= ticks)
			{
				return 0f;
			}
			float num2 = Mathf.Max(1f, StationConfig.CoolDownSeconds.Value) * 10000000f;
			return Mathf.Clamp01((float)(num - ticks) / num2);
		}

		internal static bool HotBlock(Inventory from, Inventory to)
		{
			if (from == null || from == to)
			{
				return false;
			}
			if (!Registry.TryGetValue(from, out var value) || !value.IsHot())
			{
				return false;
			}
			if ((Object)(object)Player.m_localPlayer != (Object)null)
			{
				string text = (value.IsFrostKiln ? "Too cold to touch" : "Too hot to touch");
				if (value.IsRefinery)
				{
					text = (((Object)(object)OpenStation == (Object)(object)value && OpenView == ViewMode.Input) ? "You'd lose your fingers" : "Too acidic to touch");
				}
				((Character)Player.m_localPlayer).Message((MessageType)2, text, 0, (Sprite)null, false);
			}
			return true;
		}

		internal string StatusMessage()
		{
			if ((Object)(object)_nview == (Object)null || !_nview.IsValid() || (Object)(object)_container == (Object)null)
			{
				return "";
			}
			Inventory inventory = _container.GetInventory();
			if (inventory == null)
			{
				return "";
			}
			ZDO zDO = _nview.GetZDO();
			if (!PowerOn)
			{
				string text = Problem(inventory, sayEmpty: true);
				if (text.Length <= 0)
				{
					return "Press Light to start";
				}
				return text;
			}
			if (zDO.GetInt("ep_active", 0) == 1 || zDO.GetString("ep_item", "").Length > 0)
			{
				return "";
			}
			return Problem(inventory, sayEmpty: false);
		}

		internal bool CanLight()
		{
			if ((Object)(object)_nview == (Object)null || !_nview.IsValid() || (Object)(object)_container == (Object)null)
			{
				return false;
			}
			Inventory inventory = _container.GetInventory();
			if (inventory != null)
			{
				return Problem(inventory, sayEmpty: true).Length == 0;
			}
			return false;
		}

		private string Problem(Inventory inv, bool sayEmpty)
		{
			bool flag = false;
			for (int i = 0; i < 4; i++)
			{
				if (inv.GetItemAt(i, 0) != null)
				{
					flag = true;
					break;
				}
			}
			if (!flag)
			{
				if (!sayEmpty)
				{
					return "";
				}
				if (!IsRefinery)
				{
					return "Nothing in the Input row";
				}
				return "No Softtissue loaded";
			}
			int num = CountIn(inv, 1, _fuelName);
			bool flag2 = false;
			bool flag3 = false;
			List<string> units;
			bool flag4 = TryPlanBronze(inv, out units);
			if (flag4)
			{
				Run run = BuildRun("BRONZE|" + string.Join("|", units.ToArray()));
				if (run != null)
				{
					if (num >= run.Coal)
					{
						return "";
					}
					flag2 = true;
				}
			}
			for (int j = 0; j < 4; j++)
			{
				ItemData itemAt = inv.GetItemAt(j, 0);
				if (itemAt == null)
				{
					continue;
				}
				string text = ItemName(itemAt);
				if (flag4 && (CopperFamily.Contains(text) || TinFamily.Contains(text)))
				{
					continue;
				}
				Run run2 = BuildRun(text);
				if (run2 == null)
				{
					continue;
				}
				if (!HasInputs(inv, run2.Inputs))
				{
					flag3 = true;
					continue;
				}
				if (num >= run2.Coal)
				{
					return "";
				}
				flag2 = true;
			}
			if (flag2)
			{
				if (!IsRefinery)
				{
					return "Not enough coal";
				}
				return "Not enough Sap";
			}
			if (!flag3 || !IsFrostKiln)
			{
				return "Insufficient materials to continue recipe";
			}
			return "Not enough Ice";
		}

		private void PlayDoneEffects()
		{
			if (IsRefinery)
			{
				_pitchOverride = StationConfig.RefineryPitch.Value;
				_gainOverride = StationConfig.RefineryVolume.Value;
			}
			try
			{
				PlayDoneEffectsCore();
			}
			finally
			{
				_pitchOverride = -1f;
				_gainOverride = -1f;
			}
		}

		private void PlayDoneEffectsCore()
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_03ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_03c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_03d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_03d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0429: Unknown result type (might be due to invalid IL or missing references)
			//IL_0434: Unknown result type (might be due to invalid IL or missing references)
			//IL_0442: Unknown result type (might be due to invalid IL or missing references)
			//IL_0448: Unknown result type (might be due to invalid IL or missing references)
			//IL_04a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_04ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_04bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_04c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_0505: Unknown result type (might be due to invalid IL or missing references)
			//IL_0510: Unknown result type (might be due to invalid IL or missing references)
			if (IsKiln)
			{
				GameObject[] array = _smelter.m_produceEffects.Create(((Component)this).transform.position, ((Component)this).transform.rotation, (Transform)null, 1f, -1, default(ZDOID));
				if (array != null)
				{
					GameObject[] array2 = array;
					for (int i = 0; i < array2.Length; i++)
					{
						ShapeSound(array2[i], 1f);
					}
				}
				return;
			}
			if (!_doneChecked)
			{
				_doneChecked = true;
				try
				{
					Plugin.Log.LogInfo((object)("Smelter produce effects: " + DescribeEffects(_smelter.m_produceEffects)));
					if ((Object)(object)ZNetScene.instance != (Object)null)
					{
						List<string> list = new List<string>();
						foreach (GameObject prefab2 in ZNetScene.instance.m_prefabs)
						{
							if (!((Object)(object)prefab2 == (Object)null))
							{
								string text = ((Object)prefab2).name.ToLowerInvariant();
								if (text.StartsWith("sfx_") && (text.Contains("hiss") || text.Contains("steam") || text.Contains("sizzle") || text.Contains("extinguish") || text.Contains("quench") || text.Contains("boil") || text.Contains("bubbl") || text.Contains("fizz")))
								{
									list.Add(((Object)prefab2).name);
								}
							}
						}
						Plugin.Log.LogInfo((object)("Possible hiss sounds: " + ((list.Count > 0) ? string.Join(", ", list.ToArray()) : "(none found)")));
						foreach (GameObject prefab3 in ZNetScene.instance.m_prefabs)
						{
							if ((Object)(object)prefab3 == (Object)null)
							{
								continue;
							}
							CookingStation component = prefab3.GetComponent<CookingStation>();
							if (!((Object)(object)component == (Object)null) && component.m_doneEffect != null && component.m_doneEffect.m_effectPrefabs.Length != 0)
							{
								Plugin.Log.LogInfo((object)("Cooking done effect: " + ((Object)prefab3).name + " -> " + DescribeEffects(component.m_doneEffect)));
								bool flag = ((Object)prefab3).name.ToLowerInvariant().Contains("meatrack");
								if (_hissEffects == null || flag)
								{
									_hissEffects = component.m_doneEffect;
								}
							}
						}
					}
					GameObject val = (((Object)(object)ZNetScene.instance != (Object)null) ? ZNetScene.instance.GetPrefab("piece_cauldron") : null);
					CraftingStation val2 = (((Object)(object)val != (Object)null) ? val.GetComponent<CraftingStation>() : null);
					if ((Object)(object)val2 == (Object)null)
					{
						Plugin.Log.LogWarning((object)"Cauldron not found, keeping the smelter's own finish effects.");
					}
					else
					{
						Plugin.Log.LogInfo((object)("Cauldron craft effects: " + DescribeEffects(val2.m_craftItemEffects)));
						Plugin.Log.LogInfo((object)("Cauldron done effects: " + DescribeEffects(val2.m_craftItemDoneEffects)));
						if (val2.m_craftItemEffects != null && val2.m_craftItemEffects.m_effectPrefabs.Length != 0)
						{
							_doneEffects = val2.m_craftItemEffects;
						}
						else if (val2.m_craftItemDoneEffects != null && val2.m_craftItemDoneEffects.m_effectPrefabs.Length != 0)
						{
							_doneEffects = val2.m_craftItemDoneEffects;
						}
					}
				}
				catch (Exception ex)
				{
					Plugin.Log.LogWarning((object)("Finish effects lookup failed: " + ex.Message));
				}
			}
			if (CfgBubbling)
			{
				GameObject[] array3 = (_doneEffects ?? _smelter.m_produceEffects).Create(((Component)this).transform.position, ((Component)this).transform.rotation, (Transform)null, 1f, -1, default(ZDOID));
				if (_doneEffects != null && array3 != null)
				{
					GameObject[] array2 = array3;
					for (int i = 0; i < array2.Length; i++)
					{
						ShapeSound(array2[i], 1f);
					}
				}
			}
			if (CfgCookingDone && _hissEffects != null)
			{
				GameObject[] array4 = _hissEffects.Create(((Component)this).transform.position, ((Component)this).transform.rotation, (Transform)null, 1f, -1, default(ZDOID));
				if (array4 != null)
				{
					GameObject[] array2 = array4;
					for (int i = 0; i < array2.Length; i++)
					{
						ShapeSound(array2[i], 1f);
					}
				}
			}
			if (PrefabName() != "smelter" && !IsRefinery)
			{
				_smelter.m_produceEffects.Create(((Component)this).transform.position, ((Component)this).transform.rotation, (Transform)null, 1f, -1, default(ZDOID));
				return;
			}
			string cfgHiss = CfgHiss;
			if (cfgHiss.Length > 0 && (Object)(object)ZNetScene.instance != (Object)null)
			{
				GameObject prefab = ZNetScene.instance.GetPrefab(cfgHiss);
				if ((Object)(object)prefab != (Object)null)
				{
					ShapeSound(Object.Instantiate<GameObject>(prefab, ((Component)this).transform.position, ((Component)this).transform.rotation), 1f);
				}
				else if (!_hissWarned)
				{
					_hissWarned = true;
					Plugin.Log.LogWarning((object)("FinishHissPrefab '" + cfgHiss + "' not found."));
				}
			}
		}

		internal static void ShapeSound(GameObject go, float volume)
		{
			if ((Object)(object)go == (Object)null)
			{
				return;
			}
			float num = Mathf.Clamp((_pitchOverride > 0f) ? _pitchOverride : StationConfig.FinishSoundPitch.Value, 0.1f, 1f);
			volume *= ((_gainOverride >= 0f) ? _gainOverride : StationConfig.FinishSoundVolume.Value);
			ZSFX[] componentsInChildren = go.GetComponentsInChildren<ZSFX>(true);
			foreach (ZSFX val in componentsInChildren)
			{
				float num2 = 0f;
				AudioClip[] audioClips = val.m_audioClips;
				foreach (AudioClip val2 in audioClips)
				{
					if ((Object)(object)val2 != (Object)null)
					{
						num2 = Mathf.Max(num2, val2.length);
					}
				}
				if (num2 <= 0f)
				{
					num2 = 1.5f;
				}
				float num3 = num2 / num;
				val.m_fadeInDuration = Mathf.Min(0.3f, num3 * 0.2f);
				val.m_fadeOutDuration = Mathf.Min(0.7f, num3 * 0.35f);
				val.m_fadeOutOnAwake = true;
				val.m_fadeOutDelay = Mathf.Max(0f, num3 - val.m_fadeOutDuration);
				val.m_minPitch = num;
				val.m_maxPitch = num;
				val.m_minVol *= volume;
				val.m_maxVol *= volume;
				AudioSource component = ((Component)val).GetComponent<AudioSource>();
				if ((Object)(object)component != (Object)null && component.spatialBlend < 0.5f)
				{
					component.spatialBlend = 1f;
					component.minDistance = Mathf.Max(component.minDistance, 3f);
					component.maxDistance = Mathf.Max(component.maxDistance, 30f);
					component.rolloffMode = (AudioRolloffMode)1;
				}
			}
		}

		private static string DescribeEffects(EffectList list)
		{
			if (list == null || list.m_effectPrefabs == null || list.m_effectPrefabs.Length == 0)
			{
				return "(none)";
			}
			List<string> list2 = new List<string>();
			EffectData[] effectPrefabs = list.m_effectPrefabs;
			foreach (EffectData val in effectPrefabs)
			{
				list2.Add(((Object)(object)val.m_prefab != (Object)null) ? ((Object)val.m_prefab).name : "null");
			}
			return string.Join(", ", list2.ToArray());
		}

		internal void Tick()
		{
			if ((Object)(object)_nview == (Object)null || !_nview.IsValid() || (Object)(object)_container == (Object)null)
			{
				return;
			}
			_updateRoof.GetValue();
			if ((Object)(object)_smelter.m_smokeSpawner != (Object)null)
			{
				_updateSmoke.GetValue();
			}
			_updateState.GetValue();
			if (StationConfig.WindAnimation.Value)
			{
				ApplySpin();
			}
			if (!_nview.IsOwner())
			{
				return;
			}
			Inventory inventory = _container.GetInventory();
			if (inventory == null)
			{
				return;
			}
			ZDO zDO = _nview.GetZDO();
			DateTime time = ZNet.instance.GetTime();
			long ticks = zDO.GetLong("ep_time", time.Ticks);
			zDO.Set("ep_time", time.Ticks);
			double num = (time - new DateTime(ticks)).TotalSeconds;
			if (num < 0.0)
			{
				num = 0.0;
			}
			if (num > 3600.0)
			{
				num = 3600.0;
			}
			int num2 = Mathf.Max(1, Mathf.FloorToInt((float)num));
			bool flag = false;
			bool changed = false;
			bool flag2 = false;
			for (int i = 0; i < num2; flag = true, i++)
			{
				int num3;
				switch (Step(zDO, inventory, ref changed))
				{
				case StepResult.Progressed:
					continue;
				case StepResult.Idle:
					num3 = (PowerOn ? 1 : 0);
					break;
				default:
					num3 = 0;
					break;
				}
				flag2 = (byte)num3 != 0;
				break;
			}
			if (flag2)
			{
				zDO.Set("ep_on", 0);
				zDO.Set("ep_item", "");
				zDO.Set("ep_prog", 0f);
			}
			zDO.Set("ep_active", flag ? 1 : 0);
			if (flag)
			{
				zDO.Set("ep_hot", time.Ticks + (long)(StationConfig.CoolDownSeconds.Value * 10000000f));
			}
			if (changed)
			{
				Traverse.Create((object)_container).Method("Save", Array.Empty<object>()).GetValue();
			}
		}

		private bool RoofOrSmokeBlocked()
		{
			bool value = _haveRoof.GetValue<bool>();
			bool value2 = _blockedSmoke.GetValue<bool>();
			return (_smelter.m_requiresRoof && !value) || value2;
		}

		private static void UnitCost(string name, out float secs, out float coal)
		{
			if (name.Contains("Scrap"))
			{
				secs = StationConfig.ScrapSeconds.Value;
				coal = StationConfig.ScrapCoal.Value;
			}
			else if (name == "Copper" || name == "Tin")
			{
				secs = StationConfig.BarSeconds.Value;
				coal = StationConfig.BarCoal.Value;
			}
			else
			{
				secs = StationConfig.OreSeconds.Value;
				coal = StationConfig.OreCoal.Value;
			}
		}

		private static ItemDrop BronzePrefab()
		{
			if ((Object)(object)ObjectDB.instance == (Object)null)
			{
				return null;
			}
			GameObject itemPrefab = ObjectDB.instance.GetItemPrefab("Bronze");
			if (!((Object)(object)itemPrefab != (Object)null))
			{
				return null;
			}
			return itemPrefab.GetComponent<ItemDrop>();
		}

		private Run BuildRun(string cur)
		{
			Run run = new Run();
			if (IsKiln)
			{
				ItemConversion val = FindConversion(cur);
				if (val == null || (Object)(object)val.m_to == (Object)null)
				{
					return null;
				}
				int num = ((!IsFrostKiln) ? 1 : Mathf.Max(1, _smelter.m_fuelPerProduct));
				for (int i = 0; i < num; i++)
				{
					run.Inputs.Add(cur);
				}
				run.Product = val.m_to.m_itemData.Clone();
				run.Product.m_dropPrefab = ((Component)val.m_to).gameObject;
				run.Product.m_stack = run.Count;
				run.Seconds = Mathf.Max(1f, _smelter.m_secPerProduct);
				run.Coal = 0;
				return run;
			}
			if (IsRefinery)
			{
				ItemConversion val2 = FindConversion(cur);
				if (val2 == null || (Object)(object)val2.m_to == (Object)null)
				{
					return null;
				}
				run.Inputs.Add(cur);
				run.Product = val2.m_to.m_itemData.Clone();
				run.Product.m_dropPrefab = ((Component)val2.m_to).gameObject;
				run.Product.m_stack = run.Count;
				run.Seconds = StationConfig.RefinerySeconds.Value;
				run.Coal = StationConfig.RefinerySap.Value;
				return run;
			}
			if (cur.StartsWith("BRONZE|"))
			{
				string[] array = cur.Substring("BRONZE|".Length).Split(new char[1] { '|' });
				foreach (string text in array)
				{
					if (text.Length > 0)
					{
						run.Inputs.Add(text);
					}
				}
				ItemDrop val3 = BronzePrefab();
				if ((Object)(object)val3 == (Object)null)
				{
					return null;
				}
				run.Product = val3.m_itemData.Clone();
				run.Product.m_dropPrefab = ((Component)val3).gameObject;
				run.Count = Mathf.Clamp(StationConfig.BronzeOutput.Value, 1, 3);
			}
			else
			{
				ItemConversion val4 = FindConversion(cur);
				if (val4 == null || (Object)(object)val4.m_to == (Object)null)
				{
					return null;
				}
				run.Inputs.Add(cur);
				run.Product = val4.m_to.m_itemData.Clone();
				run.Product.m_dropPrefab = ((Component)val4.m_to).gameObject;
			}
			if (run.Inputs.Count == 0)
			{
				return null;
			}
			run.Product.m_stack = run.Count;
			float num2 = 0f;
			foreach (string input in run.Inputs)
			{
				UnitCost(input, out var secs, out var coal);
				run.Seconds += secs;
				num2 += coal;
			}
			run.Coal = Mathf.CeilToInt(num2 - 0.001f);
			return run;
		}

		private bool TryPlanBronze(Inventory inv, out List<string> units)
		{
			units = new List<string>();
			int num = 0;
			int num2 = 0;
			for (int i = 0; i < 4; i++)
			{
				ItemData itemAt = inv.GetItemAt(i, 0);
				if (itemAt == null)
				{
					continue;
				}
				string item = ItemName(itemAt);
				bool flag = CopperFamily.Contains(item);
				bool flag2 = TinFamily.Contains(item);
				if (!flag && !flag2)
				{
					continue;
				}
				for (int j = 0; j < itemAt.m_stack; j++)
				{
					if (flag && num < 2)
					{
						units.Add(item);
						num++;
					}
					else if (flag2 && num2 < 1)
					{
						units.Add(item);
						num2++;
					}
				}
			}
			if (num == 2)
			{
				return num2 == 1;
			}
			return false;
		}

		private bool HasInputs(Inventory inv, List<string> inputs)
		{
			Dictionary<string, int> dictionary = new Dictionary<string, int>();
			foreach (string input in inputs)
			{
				dictionary.TryGetValue(input, out var value);
				dictionary[input] = value + 1;
			}
			foreach (KeyValuePair<string, int> item in dictionary)
			{
				if (CountIn(inv, 0, item.Key) < item.Value)
				{
					return false;
				}
			}
			return true;
		}

		private int CountIn(Inventory inv, int row, string prefabName)
		{
			int num = 0;
			for (int i = 0; i < 4; i++)
			{
				ItemData itemAt = inv.GetItemAt(i, row);
				if (itemAt != null && ItemName(itemAt) == prefabName)
				{
					num += itemAt.m_stack;
				}
			}
			return num;
		}

		private void TakeFrom(Inventory inv, int row, string prefabName, int amount)
		{
			for (int i = 0; i < 4; i++)
			{
				if (amount <= 0)
				{
					break;
				}
				ItemData itemAt = inv.GetItemAt(i, row);
				if (itemAt != null && !(ItemName(itemAt) != prefabName))
				{
					int num = Mathf.Min(amount, itemAt.m_stack);
					inv.RemoveItem(itemAt, num);
					amount -= num;
				}
			}
		}

		private int OutputRoom(Inventory inv, ItemData sample)
		{
			int num = StationConfig.SlotCap(2, sample.m_shared.m_maxStackSize);
			int num2 = 0;
			for (int i = FirstOutputRow; i <= 2; i++)
			{
				for (int j = 0; j < 4; j++)
				{
					ItemData itemAt = inv.GetItemAt(j, i);
					if (itemAt == null)
					{
						num2 += num;
					}
					else if (itemAt.IsSameType(sample))
					{
						num2 += Mathf.Max(0, num - itemAt.m_stack);
					}
				}
			}
			return num2;
		}

		private bool StartNext(ZDO zdo, Inventory inv)
		{
			int num = CountIn(inv, 1, _fuelName);
			List<string> units;
			bool flag = TryPlanBronze(inv, out units);
			if (flag)
			{
				string text = "BRONZE|" + string.Join("|", units.ToArray());
				Run run = BuildRun(text);
				if (run != null && num >= run.Coal)
				{
					zdo.Set("ep_item", text);
					zdo.Set("ep_prog", 0f);
					return true;
				}
			}
			for (int i = 0; i < 4; i++)
			{
				ItemData itemAt = inv.GetItemAt(i, 0);
				if (itemAt == null)
				{
					continue;
				}
				string text2 = ItemName(itemAt);
				if (!flag || (!CopperFamily.Contains(text2) && !TinFamily.Contains(text2)))
				{
					Run run2 = BuildRun(text2);
					if (run2 != null && num >= run2.Coal && HasInputs(inv, run2.Inputs))
					{
						zdo.Set("ep_item", text2);
						zdo.Set("ep_prog", 0f);
						return true;
					}
				}
			}
			return false;
		}

		private StepResult Step(ZDO zdo, Inventory inv, ref bool changed)
		{
			if (!PowerOn)
			{
				return StepResult.Idle;
			}
			if (RoofOrSmokeBlocked())
			{
				return StepResult.Blocked;
			}
			string text = zdo.GetString("ep_item", "");
			if (text.Length > 0 && !text.StartsWith("BRONZE|") && PowerOn && (CopperFamily.Contains(text) || TinFamily.Contains(text)) && TryPlanBronze(inv, out var _))
			{
				zdo.Set("ep_item", "");
				zdo.Set("ep_prog", 0f);
				text = "";
			}
			if (text.Length == 0)
			{
				if (!PowerOn || !StartNext(zdo, inv))
				{
					return StepResult.Idle;
				}
				text = zdo.GetString("ep_item", "");
			}
			Run run = BuildRun(text);
			if (run == null || !HasInputs(inv, run.Inputs) || CountIn(inv, 1, _fuelName) < run.Coal)
			{
				zdo.Set("ep_item", "");
				zdo.Set("ep_prog", 0f);
				return StepResult.Idle;
			}
			float num = zdo.GetFloat("ep_prog", 0f);
			if (num >= run.Seconds && OutputRoom(inv, run.Product) < run.Count)
			{
				return StepResult.Blocked;
			}
			num += 1f;
			if (num < run.Seconds)
			{
				zdo.Set("ep_prog", num);
				return StepResult.Progressed;
			}
			if (OutputRoom(inv, run.Product) < run.Count)
			{
				zdo.Set("ep_prog", run.Seconds);
				return StepResult.Blocked;
			}
			foreach (string input in run.Inputs)
			{
				TakeFrom(inv, 0, input, 1);
			}
			TakeFrom(inv, 1, _fuelName, run.Coal);
			AddToOutput(inv, run.Product);
			PlayDoneEffects();
			zdo.Set("ep_item", "");
			zdo.Set("ep_prog", 0f);
			changed = true;
			return StepResult.Progressed;
		}

		internal static string ItemName(ItemData item)
		{
			if (item == null || !((Object)(object)item.m_dropPrefab != (Object)null))
			{
				return "";
			}
			return ((Object)item.m_dropPrefab).name;
		}

		internal int RowFor(ItemData item)
		{
			string text = ItemName(item);
			if (text.Length == 0)
			{
				return -1;
			}
			if (!IsKiln && text == _fuelName)
			{
				return 1;
			}
			if (_inputs.Contains(text))
			{
				return 0;
			}
			return -1;
		}

		internal int FreeRoom(Inventory inv, ItemData item)
		{
			int num = RowFor(item);
			if (num < 0)
			{
				return 0;
			}
			int num2 = ViewRow();
			if (num2 >= 0 && num != num2)
			{
				return 0;
			}
			if (MainLocked(num))
			{
				return 0;
			}
			int num3 = CapFor(num, item.m_shared.m_maxStackSize);
			int num4 = 0;
			for (int i = 0; i < 4; i++)
			{
				ItemData itemAt = inv.GetItemAt(i, num);
				if (itemAt == null)
				{
					num4 += num3;
				}
				else if (itemAt.IsSameType(item))
				{
					num4 += Mathf.Max(0, num3 - itemAt.m_stack);
				}
			}
			return num4;
		}

		internal bool AddToRow(Inventory inv, ItemData item)
		{
			int num = RowFor(item);
			if (num < 0)
			{
				return false;
			}
			int num2 = ViewRow();
			if (num2 >= 0 && num != num2)
			{
				return false;
			}
			if (MainLocked(num))
			{
				return false;
			}
			for (int i = 0; i < 2; i++)
			{
				for (int j = 0; j < 4; j++)
				{
					if (item.m_stack <= 0)
					{
						break;
					}
					ItemData itemAt = inv.GetItemAt(j, num);
					if ((i != 0 || (itemAt != null && itemAt.IsSameType(item))) && (i != 1 || itemAt == null))
					{
						StationPatches.AddAt(inv, item, item.m_stack, j, num);
					}
				}
			}
			return item.m_stack <= 0;
		}
	}
	internal static class StationPatches
	{
		private static readonly Func<Inventory, ItemData, int, int, int, bool, bool> _addAt = AccessTools.MethodDelegate<Func<Inventory, ItemData, int, int, int, bool, bool>>(AccessTools.Method(typeof(Inventory), "AddItem", new Type[5]
		{
			typeof(ItemData),
			typeof(int),
			typeof(int),
			typeof(int),
			typeof(bool)
		}, (Type[])null), (object)null, true);

		internal static bool AddAt(Inventory inv, ItemData item, int amount, int x, int y)
		{
			return _addAt(inv, item, amount, x, y, arg6: false);
		}
	}
	[HarmonyPatch(typeof(ZNetScene), "Awake")]
	internal static class ZNetSceneAwakePatch
	{
		private static void Postfix(ZNetScene __instance)
		{
			//IL_0160: Unknown result type (might be due to invalid IL or missing references)
			if (StationConfig.SmelterScreen == null || !StationConfig.SmelterScreen.Value)
			{
				return;
			}
			try
			{
				Sprite bkg = null;
				foreach (GameObject prefab in __instance.m_prefabs)
				{
					if (!((Object)(object)prefab == (Object)null))
					{
						Container component = prefab.GetComponent<Container>();
						if ((Object)(object)component != (Object)null && (Object)(object)component.m_bkg != (Object)null)
						{
							bkg = component.m_bkg;
							break;
						}
					}
				}
				foreach (GameObject prefab2 in __instance.m_prefabs)
				{
					if (!((Object)(object)prefab2 == (Object)null) && (!(((Object)prefab2).name != "smelter") || !(((Object)prefab2).name != "blastfurnace") || !(((Object)prefab2).name != "charcoal_kiln") || !(((Object)prefab2).name != "eitrrefinery") || !(((Object)prefab2).name != "piece_FrostKiln")) && !((Object)(object)prefab2.GetComponent<Container>() != (Object)null))
					{
						Container obj = prefab2.AddComponent<Container>();
						Smelter component2 = prefab2.GetComponent<Smelter>();
						obj.m_name = (((Object)(object)component2 != (Object)null && !string.IsNullOrEmpty(component2.m_name)) ? component2.m_name : "$piece_smelter");
						obj.m_width = 4;
						obj.m_height = 3;
						obj.m_bkg = bkg;
						obj.m_privacy = (PrivacySetting)2;
						prefab2.AddComponent<StationContainer>();
						Plugin.Log.LogInfo((object)("Station screen added to " + ((Object)prefab2).name));
					}
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("Station screen setup failed: " + ex));
			}
		}
	}
	[HarmonyPatch(typeof(Smelter), "UpdateSmelter")]
	internal static class SmelterUpdatePatch
	{
		private static bool Prefix(Smelter __instance)
		{
			StationContainer component = ((Component)__instance).GetComponent<StationContainer>();
			if ((Object)(object)component == (Object)null || !component.Ready)
			{
				return true;
			}
			component.Tick();
			return false;
		}
	}
	[HarmonyPatch(typeof(Smelter), "IsActive")]
	internal static class SmelterIsActivePatch
	{
		private static void Postfix(Smelter __instance, ref bool __result)
		{
			StationContainer component = ((Component)__instance).GetComponent<StationContainer>();
			if (!((Object)(object)component == (Object)null) && component.Ready)
			{
				__result = component.IsRunning() || component.HeatLevel() > 0f;
			}
		}
	}
	[HarmonyPatch(typeof(Inventory), "MoveAll")]
	[HarmonyPriority(800)]
	internal static class HotMoveAllPatch
	{
		private static bool Prefix(Inventory __instance, Inventory fromInventory)
		{
			if (StationContainer.HotBlock(fromInventory, __instance))
			{
				return false;
			}
			if (StationContainer.ViewMoveAll(fromInventory, __instance))
			{
				return false;
			}
			return !StationContainer.MoveEverything(fromInventory, __instance);
		}
	}
	[HarmonyPatch(typeof(Inventory), "MoveItemToThis", new Type[]
	{
		typeof(Inventory),
		typeof(ItemData)
	})]
	internal static class HotQuickMovePatch
	{
		private static bool Prefix(Inventory __instance, Inventory fromInventory, ItemData item)
		{
			if (StationContainer.LockBlock(fromInventory, item))
			{
				return false;
			}
			return !StationContainer.HotBlock(fromInventory, __instance);
		}
	}
	[HarmonyPatch(typeof(Inventory), "MoveItemToThis", new Type[]
	{
		typeof(Inventory),
		typeof(ItemData),
		typeof(int),
		typeof(int),
		typeof(int)
	})]
	internal static class HotMoveToSlotPatch
	{
		private static bool Prefix(Inventory __instance, Inventory fromInventory, ItemData item, ref bool __result)
		{
			if (!StationContainer.LockBlock(fromInventory, item) && !StationContainer.HotBlock(fromInventory, __instance))
			{
				return true;
			}
			__result = false;
			return false;
		}
	}
	[HarmonyPatch(typeof(Humanoid), "DropItem")]
	internal static class HotDropPatch
	{
		private static bool Prefix(Inventory inventory, ItemData item, ref bool __result)
		{
			if (!StationContainer.LockBlock(inventory, item) && !StationContainer.HotBlock(inventory, null))
			{
				return true;
			}
			__result = false;
			return false;
		}
	}
	[HarmonyPatch(typeof(Inventory), "CanAddItem", new Type[]
	{
		typeof(ItemData),
		typeof(int)
	})]
	internal static class CanAddItemPatch
	{
		private static bool Prefix(Inventory __instance, ItemData item, int stack, ref bool __result)
		{
			if (!StationContainer.Registry.TryGetValue(__instance, out var value))
			{
				return true;
			}
			__result = value.FreeRoom(__instance, item) >= ((stack <= 0) ? item.m_stack : stack);
			return false;
		}
	}
	[HarmonyPatch(typeof(Inventory), "AddItem", new Type[] { typeof(ItemData) })]
	internal static class AddItemAutoPatch
	{
		private static bool Prefix(Inventory __instance, ItemData item, ref bool __result)
		{
			if (!StationContainer.Registry.TryGetValue(__instance, out var value))
			{
				return true;
			}
			__result = value.AddToRow(__instance, item);
			return false;
		}
	}
	[HarmonyPatch(typeof(Inventory), "AddItem", new Type[]
	{
		typeof(ItemData),
		typeof(Vector2i)
	})]
	internal static class AddItemPosPatch
	{
		private static bool Prefix(Inventory __instance, ItemData item, ref bool __result)
		{
			if (!StationContainer.Registry.TryGetValue(__instance, out var value))
			{
				return true;
			}
			__result = value.AddToRow(__instance, item);
			return false;
		}
	}
	[HarmonyPatch(typeof(Inventory), "AddItem", new Type[]
	{
		typeof(ItemData),
		typeof(int),
		typeof(int),
		typeof(int),
		typeof(bool)
	})]
	internal static class AddItemAtPatch
	{
		private static bool Prefix(Inventory __instance, ItemData item, ref int amount, int x, int y, bool skipValidPositionCheck, ref bool __result)
		{
			if (skipValidPositionCheck || StationContainer.MachineWrite)
			{
				return true;
			}
			if (!StationContainer.Registry.TryGetValue(__instance, out var value))
			{
				return true;
			}
			int num = value.ViewRow();
			if (y != value.RowFor(item) || (num >= 0 && y != num) || value.MainLocked(y))
			{
				__result = false;
				return false;
			}
			int num2 = value.CapFor(y, item.m_shared.m_maxStackSize) - (__instance.GetItemAt(x, y)?.m_stack ?? 0);
			if (num2 <= 0)
			{
				__result = false;
				return false;
			}
			if (amount > num2)
			{
				amount = num2;
			}
			return true;
		}
	}
	[HarmonyPatch(typeof(InventoryGrid), "DropItem")]
	internal static class DropItemSwapPatch
	{
		private static bool Prefix(InventoryGrid __instance, Inventory fromInventory, ItemData item, int amount, Vector2i pos, ref bool __result)
		{
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			Inventory inventory = __instance.GetInventory();
			if (inventory == null || fromInventory == null)
			{
				return true;
			}
			if (!StationContainer.Registry.TryGetValue(inventory, out var value) && !StationContainer.Registry.TryGetValue(fromInventory, out value))
			{
				return true;
			}
			ItemData itemAt = inventory.GetItemAt(pos.x, pos.y);
			if (itemAt == null || itemAt == item)
			{
				return true;
			}
			if ((!(itemAt.m_shared.m_name != item.m_shared.m_name) && (item.m_shared.m_maxQuality <= 1 || itemAt.m_quality == item.m_quality) && itemAt.m_shared.m_maxStackSize != 1) || item.m_stack != amount)
			{
				return true;
			}
			__result = false;
			return false;
		}
	}
	[HarmonyPatch(typeof(Inventory), "StackAll")]
	internal static class StackAllPatch
	{
		private static bool Prefix(Inventory __instance, Inventory fromInventory, bool message, ref int __result)
		{
			if (!StationContainer.Registry.TryGetValue(__instance, out var value))
			{
				return true;
			}
			if ((Object)(object)Player.m_localPlayer == (Object)null)
			{
				return true;
			}