Decompiled source of Schedule Maintenance v1.0.0

Mods/MixingStationMkIII.dll

Decompiled 11 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Text.Json;
using System.Text.RegularExpressions;
using HarmonyLib;
using Il2CppInterop.Runtime;
using Il2CppInterop.Runtime.InteropTypes;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using Il2CppScheduleOne;
using Il2CppScheduleOne.Building;
using Il2CppScheduleOne.Core.Items.Framework;
using Il2CppScheduleOne.DevUtilities;
using Il2CppScheduleOne.Effects;
using Il2CppScheduleOne.EntityFramework;
using Il2CppScheduleOne.ItemFramework;
using Il2CppScheduleOne.Levelling;
using Il2CppScheduleOne.Misc;
using Il2CppScheduleOne.NPCs.Behaviour;
using Il2CppScheduleOne.ObjectScripts;
using Il2CppScheduleOne.Persistence;
using Il2CppScheduleOne.Persistence.Datas;
using Il2CppScheduleOne.Persistence.Loaders;
using Il2CppScheduleOne.PlayerScripts;
using Il2CppScheduleOne.Product;
using Il2CppScheduleOne.StationFramework;
using Il2CppScheduleOne.Tiles;
using Il2CppScheduleOne.UI;
using Il2CppScheduleOne.UI.Items;
using Il2CppScheduleOne.UI.Shop;
using Il2CppScheduleOne.UI.Stations;
using Il2CppSystem.Collections.Generic;
using Il2CppTMPro;
using MelonLoader;
using MelonLoader.Preferences;
using MelonLoader.Utils;
using Microsoft.CodeAnalysis;
using MixingStationMkIII;
using MixingStationMkIII.Model;
using MixingStationMkIII.UI;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Controls;
using UnityEngine.Rendering;
using UnityEngine.SceneManagement;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: MelonInfo(typeof(Mod), "Schedule Maintenance", "1.0.0", "beagl", null)]
[assembly: MelonGame("TVGS", "Schedule I")]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: AssemblyCompany("MixingStationMkIII")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("MixingStationMkIII")]
[assembly: AssemblyTitle("MixingStationMkIII")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace MixingStationMkIII
{
	internal sealed class LightSpec
	{
		public string Id;

		public string DisplayName;

		public string Description;

		public string ModelResource;

		public string IconResource;

		public Func<int> Price;

		public Func<int> Tier;

		public Func<string> Rank;

		public Func<float> Bonus;

		public LightModel Model;

		public BuildableItemDefinition Def;

		public float GrowSpeed => 1f + Math.Max(0f, Bonus());
	}
	internal static class CustomLights
	{
		private sealed class Placed
		{
			public GrowLight Light;

			public LightSpec Spec;

			public MeshRenderer Model;

			public bool? On;
		}

		private const string SourceName = "Full Spectrum";

		public static readonly LightSpec[] Specs;

		private static string _sourceId;

		private static readonly List<Placed> Lights;

		private static float _poll;

		static CustomLights()
		{
			Specs = new LightSpec[2]
			{
				new LightSpec
				{
					Id = "highintensitygrowlight",
					DisplayName = "High-Intensity Grow Light",
					Description = "Industrial three-tube grow light. Plants under it grow {0}% faster.",
					ModelResource = "hilight.bin",
					IconResource = "hilighticon.png",
					Price = () => ModConfig.HiLightPrice,
					Rank = () => ModConfig.HiLightRank,
					Tier = () => ModConfig.HiLightRankTier,
					Bonus = () => ModConfig.HiLightGrowBonus
				},
				new LightSpec
				{
					Id = "ultraspectrumledarray",
					DisplayName = "UltraSpectrum LED Array",
					Description = "Five-bar full-spectrum LED array. Plants under it grow {0}% faster.",
					ModelResource = "uslight.bin",
					IconResource = "uslighticon.png",
					Price = () => ModConfig.UltraLightPrice,
					Rank = () => ModConfig.UltraLightRank,
					Tier = () => ModConfig.UltraLightRankTier,
					Bonus = () => ModConfig.UltraLightGrowBonus
				}
			};
			Lights = new List<Placed>();
			LightSpec[] specs = Specs;
			foreach (LightSpec lightSpec in specs)
			{
				lightSpec.Model = new LightModel(lightSpec.ModelResource, lightSpec.DisplayName);
			}
		}

		public static LightSpec Get(string id)
		{
			if (id != null)
			{
				return Specs.FirstOrDefault((LightSpec s) => s.Id == id);
			}
			return null;
		}

		public static LightSpec Get(ItemInstance item)
		{
			return Get((item != null) ? ((BaseItemInstance)item).ID : null);
		}

		public static void EnsureRegistered()
		{
			//IL_019c: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e5: Unknown result type (might be due to invalid IL or missing references)
			Registry instance = Singleton<Registry>.Instance;
			if ((Object)(object)instance == (Object)null)
			{
				return;
			}
			BuildableItemDefinition val = null;
			LightSpec[] specs = Specs;
			foreach (LightSpec lightSpec in specs)
			{
				try
				{
					if (Registry.ItemExists(lightSpec.Id))
					{
						LightSpec lightSpec2 = lightSpec;
						if (lightSpec2.Def == null)
						{
							ItemDefinition item = Registry.GetItem(lightSpec.Id);
							lightSpec2.Def = ((item != null) ? ((Il2CppObjectBase)item).TryCast<BuildableItemDefinition>() : null);
						}
						continue;
					}
					if (val == null)
					{
						val = FindSource(instance);
					}
					if ((Object)(object)val == (Object)null)
					{
						Mod.Log.Warning("Full Spectrum grow light definition not found; " + lightSpec.DisplayName + " not registered.");
						break;
					}
					if ((Object)(object)lightSpec.Def == (Object)null || ((Il2CppObjectBase)lightSpec.Def).WasCollected)
					{
						lightSpec.Def = Object.Instantiate<BuildableItemDefinition>(val);
						((Object)lightSpec.Def).name = lightSpec.DisplayName.Replace(" ", "");
						((Object)lightSpec.Def).hideFlags = (HideFlags)32;
					}
					BuildableItemDefinition def = lightSpec.Def;
					((BaseItemDefinition)def).ID = lightSpec.Id;
					((BaseItemDefinition)def).Name = lightSpec.DisplayName;
					((BaseItemDefinition)def).Description = string.Format(lightSpec.Description, Math.Round(lightSpec.Bonus() * 100f));
					Sprite val2 = ModIcons.Load(lightSpec.IconResource, lightSpec.DisplayName + " Icon");
					if ((Object)(object)val2 != (Object)null)
					{
						((BaseItemDefinition)def).Icon = val2;
					}
					((StorableItemDefinition)def).BasePurchasePrice = lightSpec.Price();
					((StorableItemDefinition)def).RequiresLevelToPurchase = true;
					((StorableItemDefinition)def).RequiredRank = new FullRank
					{
						Rank = (ERank)((!Enum.TryParse<ERank>(lightSpec.Rank().Trim().Replace(' ', '_'), ignoreCase: true, out ERank result)) ? 3 : ((int)result)),
						Tier = lightSpec.Tier()
					};
					instance.AddToRegistry((ItemDefinition)(object)def);
					Mod.Log.Msg($"Registered item '{lightSpec.Id}' (cloned from '{_sourceId}'), grow speed x{lightSpec.GrowSpeed:0.##}.");
				}
				catch (Exception value)
				{
					Mod.Log.Error($"Failed to register {lightSpec.DisplayName}: {value}");
				}
			}
		}

		private static BuildableItemDefinition FindSource(Registry registry)
		{
			BuildableItemDefinition val = null;
			List<ItemDefinition> allItems = registry.GetAllItems();
			for (int i = 0; i < allItems.Count; i++)
			{
				ItemDefinition obj = allItems[i];
				BuildableItemDefinition val2 = ((obj != null) ? ((Il2CppObjectBase)obj).TryCast<BuildableItemDefinition>() : null);
				GrowLight val3 = (((Object)(object)((val2 != null) ? val2.BuiltItem : null) != (Object)null) ? ((Component)val2.BuiltItem).GetComponent<GrowLight>() : null);
				if (!((Object)(object)val3 == (Object)null) && Get(((BaseItemDefinition)val2).ID) == null)
				{
					float value = (((Object)(object)val3.usableLightSource != (Object)null) ? val3.usableLightSource.GrowSpeedMultiplier : (-1f));
					Mod.Log.Msg($"Grow light '{((BaseItemDefinition)val2).ID}' ({((BaseItemDefinition)val2).Name}): ${((StorableItemDefinition)val2).BasePurchasePrice}, grow speed x{value:0.##}.");
					if ((Object)(object)val == (Object)null && ((BaseItemDefinition)val2).Name != null && ((BaseItemDefinition)val2).Name.Contains("Full Spectrum", StringComparison.OrdinalIgnoreCase))
					{
						val = val2;
					}
				}
			}
			_sourceId = ((val != null) ? ((BaseItemDefinition)val).ID : null);
			return val;
		}

		public static void AddShopListings(ShopInterface shop)
		{
			//IL_00ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_0104: Unknown result type (might be due to invalid IL or missing references)
			//IL_0111: Unknown result type (might be due to invalid IL or missing references)
			//IL_011e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0125: Unknown result type (might be due to invalid IL or missing references)
			//IL_0138: Unknown result type (might be due to invalid IL or missing references)
			//IL_013f: Unknown result type (might be due to invalid IL or missing references)
			//IL_014b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0152: Unknown result type (might be due to invalid IL or missing references)
			//IL_015b: Expected O, but got Unknown
			try
			{
				if (((shop != null) ? shop.Listings : null) == null)
				{
					return;
				}
				EnsureRegistered();
				if (_sourceId == null)
				{
					return;
				}
				ShopListing val = null;
				for (int i = 0; i < shop.Listings.Count; i++)
				{
					ShopListing obj = shop.Listings[i];
					object obj2;
					if (obj == null)
					{
						obj2 = null;
					}
					else
					{
						StorableItemDefinition item = obj.Item;
						obj2 = ((item != null) ? ((BaseItemDefinition)item).ID : null);
					}
					if ((string?)obj2 == _sourceId)
					{
						val = shop.Listings[i];
					}
				}
				if (val == null)
				{
					return;
				}
				LightSpec[] specs = Specs;
				foreach (LightSpec lightSpec in specs)
				{
					if ((Object)(object)lightSpec.Def == (Object)null)
					{
						continue;
					}
					bool flag = false;
					for (int k = 0; k < shop.Listings.Count; k++)
					{
						bool num = flag;
						ShopListing obj3 = shop.Listings[k];
						object obj4;
						if (obj3 == null)
						{
							obj4 = null;
						}
						else
						{
							StorableItemDefinition item2 = obj3.Item;
							obj4 = ((item2 != null) ? ((BaseItemDefinition)item2).ID : null);
						}
						flag = num | ((string?)obj4 == lightSpec.Id);
					}
					if (!flag)
					{
						ShopListing val2 = new ShopListing
						{
							name = lightSpec.DisplayName,
							Item = (StorableItemDefinition)(object)lightSpec.Def,
							OverridePrice = true,
							OverriddenPrice = lightSpec.Price(),
							LimitedStock = false,
							CanBeDelivered = val.CanBeDelivered,
							UseIconTint = false,
							EnforceMinimumGameCreationVersion = false
						};
						shop.Listings.Add(val2);
						shop.CreateListingUI(val2);
						Mod.Log.Msg($"Added {lightSpec.DisplayName} (${lightSpec.Price()}, rank {lightSpec.Rank()} {lightSpec.Tier()}) to shop '{shop.ShopName}'.");
					}
				}
			}
			catch (Exception value)
			{
				Mod.Log.Error($"Failed to add the custom light listings: {value}");
			}
		}

		public static void Attach(GrowLight light, LightSpec spec)
		{
			try
			{
				if ((Object)(object)light.usableLightSource != (Object)null)
				{
					light.usableLightSource.GrowSpeedMultiplier = spec.GrowSpeed;
				}
				LightModel.HideVanilla(((Component)light).transform);
				MeshRenderer val = spec.Model.Build(((Component)light).transform, (Object)(object)light.Light != (Object)null && light.Light.isOn);
				if (!((Object)(object)val == (Object)null))
				{
					Lights.RemoveAll((Placed p) => (Object)(object)p.Light == (Object)null || ((Il2CppObjectBase)p.Light).WasCollected || ((Il2CppObjectBase)p.Light).Pointer == ((Il2CppObjectBase)light).Pointer);
					Lights.Add(new Placed
					{
						Light = light,
						Spec = spec,
						Model = val
					});
				}
			}
			catch (Exception value)
			{
				Mod.Log.Error($"{spec.DisplayName} setup failed: {value}");
			}
		}

		public static void ApplyToGhost(GameObject ghost, LightSpec spec)
		{
			LightModel.HideVanilla(ghost.transform);
			spec.Model.Build(ghost.transform);
		}

		public static void Update(float dt)
		{
			_poll -= dt;
			if (_poll > 0f)
			{
				return;
			}
			_poll = 0.2f;
			for (int num = Lights.Count - 1; num >= 0; num--)
			{
				Placed placed = Lights[num];
				if ((Object)(object)placed.Light == (Object)null || ((Il2CppObjectBase)placed.Light).WasCollected || (Object)(object)placed.Model == (Object)null || ((Il2CppObjectBase)placed.Model).WasCollected)
				{
					Lights.RemoveAt(num);
				}
				else
				{
					bool flag = (Object)(object)placed.Light.Light != (Object)null && placed.Light.Light.isOn;
					if (placed.On != flag)
					{
						placed.On = flag;
						placed.Spec.Model.SetGlow(placed.Model, flag);
					}
				}
			}
		}

		public static void Clear()
		{
			Lights.Clear();
		}
	}
	internal static class Items
	{
		public static string ToJson(ItemInstance item)
		{
			return ((SaveData)item.GetItemData()).GetJson(false);
		}

		public static ItemInstance FromJson(string json)
		{
			if (string.IsNullOrEmpty(json))
			{
				return null;
			}
			try
			{
				ItemInstance val = ItemDeserializer.LoadItem(json);
				return (val == null || string.IsNullOrEmpty(((BaseItemInstance)val).ID) || ((BaseItemInstance)val).Quantity <= 0) ? null : val;
			}
			catch (Exception ex)
			{
				Mod.Log.Warning("Could not restore item from save: " + ex.Message);
				return null;
			}
		}

		public static T As<T>(Il2CppObjectBase o) where T : Il2CppObjectBase
		{
			if (o == null)
			{
				return default(T);
			}
			return o.TryCast<T>();
		}

		public static bool IsMixableProduct(ItemInstance item)
		{
			ProductItemInstance val = Items.As<ProductItemInstance>((Il2CppObjectBase)(object)item);
			if (val != null)
			{
				return (Object)(object)val.AppliedPackaging == (Object)null;
			}
			return false;
		}
	}
	internal sealed class Machine
	{
		private sealed class SaveDto
		{
			public int Version { get; set; } = 3;

			public string RecipeTarget { get; set; }

			public string RecipeBase { get; set; }

			public List<string> RecipeMixers { get; set; }

			public bool Running { get; set; }

			public int Stage { get; set; }

			public int Quantity { get; set; }

			public int TimedStage { get; set; } = -1;

			public List<string> ExtraSlots { get; set; }
		}

		public MixingStation Station;

		public string Guid;

		public readonly List<ItemSlot> ExtraSlots = new List<ItemSlot>();

		public RecipeChain Recipe;

		public MkIIIVisual Visual;

		public bool Running;

		public int Stage;

		public int Quantity;

		public string Status = "";

		public static bool SendingOwnMix;

		public bool UiOpen;

		private bool _locked;

		public int TimedStage = -1;

		private float _waitForOutput;

		public int IngredientSlotCount => 1 + ExtraSlots.Count;

		public bool ExtraSlotsEmpty => ExtraSlots.All((ItemSlot s) => !Has(s));

		private int Timed
		{
			get
			{
				if (TimedStage < 0 || Recipe == null || TimedStage >= Recipe.Stages)
				{
					return (Recipe?.Stages ?? 1) - 1;
				}
				return TimedStage;
			}
		}

		public int DisplayStage
		{
			get
			{
				if (!Running || Recipe == null)
				{
					return Stage;
				}
				int num = MinutesElapsed();
				int val = Stage;
				for (int i = 1; i < Recipe.Stages; i++)
				{
					if (num >= ChainMinutes(i))
					{
						val = Math.Max(val, i);
					}
				}
				return Math.Min(val, Recipe.Stages - 1);
			}
		}

		public string StatusText
		{
			get
			{
				if (!Running || Recipe == null || !(Status ?? "").StartsWith("Mixing stage"))
				{
					return Status;
				}
				return $"Mixing stage {DisplayStage + 1} / {Recipe.Stages}";
			}
		}

		public bool IsFirstStage
		{
			get
			{
				if (Running)
				{
					return Stage == 0;
				}
				return false;
			}
		}

		public bool IsFinishingStage
		{
			get
			{
				if (Running && Recipe != null)
				{
					return Stage == Timed;
				}
				return false;
			}
		}

		public int TimedStageIndex => Timed;

		public int NewMixesLeft
		{
			get
			{
				if (!Running || Recipe == null)
				{
					return 0;
				}
				return Math.Max(1, Recipe.Stages - Stage);
			}
		}

		public bool AwaitingName { get; private set; }

		public Machine(MixingStation station, string guid)
		{
			Station = station;
			Guid = guid;
			EnsureExtraSlots();
		}

		public ItemSlot IngredientSlot(int i)
		{
			if (i != 0)
			{
				if (i - 1 >= ExtraSlots.Count)
				{
					return null;
				}
				return ExtraSlots[i - 1];
			}
			return Station.MixerSlot;
		}

		public static bool Has(ItemSlot s)
		{
			if (((s != null) ? s.ItemInstance : null) != null)
			{
				return s.Quantity > 0;
			}
			return false;
		}

		public void EnsureExtraSlots()
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Expected O, but got Unknown
			int num = Math.Max(0, ModConfig.MaxMixSteps - 1);
			while (ExtraSlots.Count < num)
			{
				ItemSlot val = new ItemSlot();
				val.SetSlotOwner(((Il2CppObjectBase)Station).Cast<IItemSlotOwner>());
				ItemSlot mixerSlot = Station.MixerSlot;
				List<ItemFilter> val2 = ((mixerSlot != null) ? mixerSlot.HardFilters : null);
				if (val2 != null)
				{
					for (int i = 0; i < val2.Count; i++)
					{
						val.AddFilter(val2[i]);
					}
				}
				ExtraSlots.Add(val);
			}
			List<ItemSlot> inputSlots = Station.InputSlots;
			if (inputSlots == null)
			{
				return;
			}
			foreach (ItemSlot extraSlot in ExtraSlots)
			{
				if (!inputSlots.Contains(extraSlot))
				{
					inputSlots.Add(extraSlot);
				}
			}
		}

		public bool EmployeeCanStart(out int quantity)
		{
			quantity = 0;
			if (Running || Recipe == null || Recipe.TargetId == null || Station.CurrentMixOperation != null)
			{
				return false;
			}
			quantity = GetRunQuantity(Recipe, out var _);
			return quantity > 0;
		}

		public void ApplyRecipeFilters()
		{
			if (UiOpen)
			{
				return;
			}
			if (Recipe == null || Recipe.TargetId == null)
			{
				ClearRecipeFilters();
				return;
			}
			SetFilter(Station.ProductSlot, new string[1] { Recipe.BaseId });
			for (int i = 0; i < IngredientSlotCount; i++)
			{
				SetFilter(IngredientSlot(i), (i < Recipe.Mixers.Count) ? new string[1] { Recipe.Mixers[i] } : new string[0]);
			}
		}

		public void ClearRecipeFilters()
		{
			SetFilter(Station.ProductSlot, null);
			for (int i = 0; i < IngredientSlotCount; i++)
			{
				SetFilter(IngredientSlot(i), null);
			}
		}

		private static void SetFilter(ItemSlot slot, string[] whitelist)
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			if (slot == null)
			{
				return;
			}
			try
			{
				SlotFilter val = (SlotFilter)((slot.PlayerFilter != null) ? ((object)slot.PlayerFilter.Clone()) : ((object)new SlotFilter()));
				val.ItemIDs = new List<string>();
				if (whitelist == null)
				{
					val.Type = (EType)0;
				}
				else
				{
					val.Type = (EType)1;
					foreach (string text in whitelist)
					{
						val.ItemIDs.Add(text);
					}
				}
				slot.SetPlayerFilter(val, false);
			}
			catch (Exception ex)
			{
				Mod.Log.Warning("MkIII slot filter failed: " + ex.Message);
			}
		}

		public void SetLocks(bool locked)
		{
			_locked = locked;
			Lock(Station.ProductSlot, add: true);
			for (int i = 0; i < IngredientSlotCount; i++)
			{
				Lock(IngredientSlot(i), add: true);
			}
			Lock(Station.OutputSlot, add: false);
			void Lock(ItemSlot s, bool add)
			{
				if (s != null)
				{
					s.SetIsRemovalLocked(locked);
					if (add)
					{
						s.SetIsAddLocked(locked);
					}
				}
			}
		}

		private void Unlocked(Action change)
		{
			bool locked = _locked;
			if (locked)
			{
				SetLocks(locked: false);
			}
			try
			{
				change();
			}
			finally
			{
				if (locked)
				{
					SetLocks(locked: true);
				}
			}
		}

		public (string baseId, List<string> mixers) LoadedSequence()
		{
			ItemSlot productSlot = Station.ProductSlot;
			ItemInstance val = ((productSlot != null) ? productSlot.ItemInstance : null);
			string item = ((val != null && Station.ProductSlot.Quantity > 0 && Items.IsMixableProduct(val)) ? ((BaseItemInstance)val).ID : null);
			List<string> list = new List<string>();
			for (int i = 0; i < IngredientSlotCount; i++)
			{
				ItemSlot val2 = IngredientSlot(i);
				if (!Has(val2))
				{
					break;
				}
				list.Add(((BaseItemInstance)val2.ItemInstance).ID);
			}
			return (baseId: item, mixers: list);
		}

		public int GetRunQuantity(RecipeChain chain, out string problem)
		{
			problem = null;
			if (chain == null || chain.Stages == 0)
			{
				problem = "Load a product and an ingredient";
				return 0;
			}
			if (chain.Stages > ModConfig.MaxMixSteps)
			{
				problem = $"Needs more than {ModConfig.MaxMixSteps} mixes";
				return 0;
			}
			ItemSlot productSlot = Station.ProductSlot;
			ItemInstance val = ((productSlot != null) ? productSlot.ItemInstance : null);
			if (val == null || Station.ProductSlot.Quantity <= 0 || ((BaseItemInstance)val).ID != chain.BaseId)
			{
				problem = "Insert " + RecipeCatalog.Name(chain.BaseId);
				return 0;
			}
			if (!Items.IsMixableProduct(val))
			{
				problem = "Product must be unpackaged";
				return 0;
			}
			if (Has(Station.OutputSlot))
			{
				problem = "Take the output first";
				return 0;
			}
			int num = Math.Min(Station.ProductSlot.Quantity, Math.Max(1, Station.MaxMixQuantity));
			for (int i = 0; i < chain.Stages; i++)
			{
				ItemSlot val2 = IngredientSlot(i);
				if (!Has(val2) || ((BaseItemInstance)val2.ItemInstance).ID != chain.Mixers[i])
				{
					problem = $"Mixer {i + 1}: insert {RecipeCatalog.Name(chain.Mixers[i])}";
					return 0;
				}
				num = Math.Min(num, val2.Quantity);
			}
			return num;
		}

		public static int ChainMinutes(int stages)
		{
			if (stages > 0)
			{
				return Math.Max(stages, (int)Math.Round((float)(ModConfig.FirstMixMinutes + ModConfig.ExtraMixMinutes * (stages - 1)) * ModConfig.MixTimeMultiplier));
			}
			return 0;
		}

		public int StageMinutes(int stage)
		{
			int stages = Recipe?.Stages ?? 1;
			int timed = Timed;
			if (stage < timed)
			{
				return Math.Max(1, ChainMinutes(stage + 1) - ChainMinutes(stage));
			}
			if (stage > timed)
			{
				return 0;
			}
			return Math.Max(1, ChainMinutes(stages) - ChainMinutes(stage));
		}

		private static int FirstUnknownStage(RecipeChain chain)
		{
			string productId = chain.BaseId;
			for (int i = 0; i < chain.Mixers.Count; i++)
			{
				ProductDefinition val = RecipeCatalog.MixOnce(productId, chain.Mixers[i]);
				if ((Object)(object)val == (Object)null)
				{
					return i;
				}
				productId = ((BaseItemDefinition)val).ID;
			}
			return chain.Mixers.Count - 1;
		}

		public int MinutesElapsed()
		{
			if (!Running || Recipe == null)
			{
				return 0;
			}
			return Math.Max(0, ChainMinutes(Recipe.Stages) - MinutesLeft());
		}

		public int DisplayQuantity(int index, out string itemId)
		{
			ItemSlot val = IngredientSlot(index);
			itemId = (Has(val) ? ((BaseItemInstance)val.ItemInstance).ID : null);
			int num = (Has(val) ? val.Quantity : 0);
			if (!Running || Recipe == null || index == 0 || index >= Recipe.Stages)
			{
				return num;
			}
			if (itemId == null)
			{
				itemId = Recipe.Mixers[index];
			}
			if (Stage >= index)
			{
				num += Quantity;
			}
			if (MinutesElapsed() >= ChainMinutes(index))
			{
				num -= Quantity;
			}
			return Math.Max(0, num);
		}

		public string Begin(RecipeChain chain, bool isNewMix)
		{
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_0130: Unknown result type (might be due to invalid IL or missing references)
			if (Running)
			{
				return "Already mixing";
			}
			if (Station.CurrentMixOperation != null)
			{
				return "Station is busy";
			}
			string problem;
			int q = GetRunQuantity(chain, out problem);
			if (q <= 0)
			{
				return problem;
			}
			if (!isNewMix)
			{
				string text = RecipeCatalog.Validate(chain);
				if (text != null)
				{
					return "Recipe invalid: " + text;
				}
			}
			ItemInstance itemInstance = Station.ProductSlot.ItemInstance;
			QualityItemInstance obj = Items.As<QualityItemInstance>((Il2CppObjectBase)(object)itemInstance);
			EQuality quality = (EQuality)((obj == null) ? 2 : ((int)obj.Quality));
			string iD = ((BaseItemInstance)itemInstance).ID;
			Unlocked(delegate
			{
				Station.ProductSlot.ChangeQuantity(-q, false);
				IngredientSlot(0).ChangeQuantity(-q, false);
			});
			Recipe = new RecipeChain
			{
				TargetId = (isNewMix ? null : chain.TargetId),
				BaseId = chain.BaseId,
				Mixers = new List<string>(chain.Mixers)
			};
			ApplyRecipeFilters();
			Quantity = q;
			Stage = 0;
			TimedStage = (isNewMix ? FirstUnknownStage(Recipe) : (Recipe.Stages - 1));
			Running = true;
			_waitForOutput = 0f;
			SetLocks(locked: true);
			SendMix(iD, quality, Recipe.Mixers[0], q);
			Mod.Log.Msg($"MkIII {Guid}: started {q}x {(isNewMix ? "NEW MIX" : RecipeCatalog.Name(chain.TargetId))} ({chain.Stages} stages, {ChainMinutes(chain.Stages)} min).");
			return null;
		}

		private void SendMix(string productId, EQuality quality, string mixerId, int quantity)
		{
			//IL_005d: 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_006c: Expected O, but got Unknown
			Status = $"Mixing stage {Stage + 1} / {Recipe.Stages}";
			SendingOwnMix = true;
			try
			{
				Station.SendMixingOperation(new MixOperation(productId, quality, mixerId, quantity), 0);
			}
			finally
			{
				SendingOwnMix = false;
			}
			int mixTimeForCurrentOperation = Station.GetMixTimeForCurrentOperation();
			Station.CurrentMixTime = mixTimeForCurrentOperation - StageMinutes(Stage);
			Mod.Log.Msg($"MkIII {Guid}: stage {Stage + 1}/{Recipe.Stages} {productId} + {mixerId} x{quantity} ({StageMinutes(Stage)} min, station total {mixTimeForCurrentOperation}).");
		}

		public int MinutesLeft()
		{
			if (!Running || Recipe == null)
			{
				return 0;
			}
			return ((Station.CurrentMixOperation != null) ? Math.Max(0, Station.GetMixTimeForCurrentOperation() - Station.CurrentMixTime) : 0) + Enumerable.Range(Stage + 1, Math.Max(0, Recipe.Stages - Stage - 1)).Sum((Func<int, int>)StageMinutes);
		}

		public void Tick(float dt)
		{
			//IL_035b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0360: Unknown result type (might be due to invalid IL or missing references)
			//IL_0392: Unknown result type (might be due to invalid IL or missing references)
			AwaitingName = false;
			if (!Running || Recipe == null)
			{
				return;
			}
			MixOperation currentMixOperation = Station.CurrentMixOperation;
			if (currentMixOperation != null && !Station.IsMixingDone)
			{
				return;
			}
			ItemSlot outputSlot = Station.OutputSlot;
			ItemInstance val = ((outputSlot != null) ? outputSlot.ItemInstance : null);
			if (!Has(Station.OutputSlot))
			{
				ProductDefinition val2 = default(ProductDefinition);
				if (currentMixOperation != null && !currentMixOperation.IsOutputKnown(ref val2))
				{
					AwaitingName = true;
					Status = "NEW MIX discovered! Open the station to name it";
					return;
				}
				if (_waitForOutput == 0f)
				{
					LogStageState("stage ended, no output yet");
				}
				_waitForOutput += dt;
				Status = "Waiting for stage output";
				if (_waitForOutput > 30f)
				{
					Running = false;
					SetLocks(locked: false);
					Status = "Stopped: stage output missing";
					LogStageState("giving up");
					Mod.Log.Warning($"MkIII {Guid}: no output after stage {Stage + 1}; stopping.");
				}
				return;
			}
			_waitForOutput = 0f;
			int num = Stage + 1;
			if (num >= Recipe.Stages)
			{
				Running = false;
				SetLocks(locked: false);
				RecipeChain recipe = Recipe;
				if (recipe.TargetId == null)
				{
					recipe.TargetId = ((BaseItemInstance)val).ID;
				}
				ApplyRecipeFilters();
				Status = $"Done: {Station.OutputSlot.Quantity}x {((BaseItemInstance)val).Name}";
				Mod.Log.Msg($"MkIII {Guid}: finished {Station.OutputSlot.Quantity}x {((BaseItemInstance)val).ID}.");
				return;
			}
			string text = Recipe.Mixers[num];
			ItemSlot slot = IngredientSlot(num);
			int q = Math.Min(Quantity, Station.OutputSlot.Quantity);
			if (!Has(slot) || ((BaseItemInstance)slot.ItemInstance).ID != text || slot.Quantity < q)
			{
				Status = $"Waiting for {q}x {RecipeCatalog.Name(text)} in mixer {num + 1}";
				return;
			}
			QualityItemInstance obj = Items.As<QualityItemInstance>((Il2CppObjectBase)(object)val);
			EQuality quality = (EQuality)((obj == null) ? 2 : ((int)obj.Quality));
			string iD = ((BaseItemInstance)val).ID;
			Unlocked(delegate
			{
				Station.OutputSlot.ChangeQuantity(-q, false);
				slot.ChangeQuantity(-q, false);
			});
			Stage = num;
			Quantity = q;
			SendMix(iD, quality, text, q);
		}

		private void LogStageState(string why)
		{
			try
			{
				MixOperation currentMixOperation = Station.CurrentMixOperation;
				ProductDefinition val = default(ProductDefinition);
				string value = ((currentMixOperation == null) ? "null" : $"{currentMixOperation.ProductID}+{currentMixOperation.IngredientID} x{currentMixOperation.Quantity} known={currentMixOperation.IsOutputKnown(ref val)}");
				ProductManager instance = NetworkSingleton<ProductManager>.Instance;
				NewMixOperation val2 = ((instance != null) ? instance.CurrentMixOperation : null);
				IEnumerable<string> values;
				if (Station.ItemSlots != null)
				{
					values = Enumerable.Range(0, Station.ItemSlots.Count).Select(delegate(int i)
					{
						ItemSlot val3 = Station.ItemSlots[i];
						return Has(val3) ? $"[{i}]{((BaseItemInstance)val3.ItemInstance).ID}x{val3.Quantity}" : $"[{i}]-";
					});
				}
				else
				{
					IEnumerable<string> enumerable = new List<string>();
					values = enumerable;
				}
				string value2 = string.Join(", ", values);
				Mod.Log.Msg($"MkIII {Guid} {why}: stage {Stage + 1}/{Recipe?.Stages}, op={value}, done={Station.IsMixingDone}, time={Station.CurrentMixTime}/{((currentMixOperation != null) ? Station.GetMixTimeForCurrentOperation() : 0)}, output={(Has(Station.OutputSlot) ? (((BaseItemInstance)Station.OutputSlot.ItemInstance).ID + "x" + Station.OutputSlot.Quantity) : "-")}, pm.mix={((val2 == null) ? "null" : (val2.ProductID + "+" + val2.IngredientID))} pm.complete={((instance != null) ? new bool?(instance.IsMixComplete) : ((bool?)null))}, slots: {value2}");
			}
			catch (Exception ex)
			{
				Mod.Log.Warning("MkIII state log failed: " + ex.Message);
			}
		}

		public string ToJson()
		{
			return JsonSerializer.Serialize(new SaveDto
			{
				RecipeTarget = Recipe?.TargetId,
				RecipeBase = Recipe?.BaseId,
				RecipeMixers = Recipe?.Mixers,
				Running = Running,
				Stage = Stage,
				Quantity = Quantity,
				TimedStage = TimedStage,
				ExtraSlots = ExtraSlots.Select((ItemSlot s) => (!Has(s)) ? null : Items.ToJson(s.ItemInstance)).ToList()
			});
		}

		public void LoadJson(string json)
		{
			SaveDto saveDto = JsonSerializer.Deserialize<SaveDto>(json);
			if (saveDto == null)
			{
				return;
			}
			Recipe = ((saveDto.RecipeMixers == null || saveDto.RecipeMixers.Count == 0 || string.IsNullOrEmpty(saveDto.RecipeBase)) ? null : new RecipeChain
			{
				TargetId = saveDto.RecipeTarget,
				BaseId = saveDto.RecipeBase,
				Mixers = saveDto.RecipeMixers
			});
			Stage = Math.Max(0, saveDto.Stage);
			Quantity = Math.Max(0, saveDto.Quantity);
			TimedStage = saveDto.TimedStage;
			Running = saveDto.Running && Recipe != null && Stage < Recipe.Stages;
			EnsureExtraSlots();
			if (saveDto.ExtraSlots != null)
			{
				for (int i = 0; i < saveDto.ExtraSlots.Count; i++)
				{
					ItemInstance val = Items.FromJson(saveDto.ExtraSlots[i]);
					if (val != null)
					{
						if (i < ExtraSlots.Count)
						{
							ExtraSlots[i].SetStoredItem(val, false);
							continue;
						}
						Mod.Log.Warning($"Dropped {((BaseItemInstance)val).Quantity}x {((BaseItemInstance)val).ID} from a MkIII (MaxMixSteps was lowered).");
					}
				}
			}
			if (Running)
			{
				Status = $"Mixing stage {Stage + 1} / {Recipe.Stages}";
			}
			SetLocks(Running);
			ApplyRecipeFilters();
		}
	}
	internal static class MkIIIItem
	{
		public const string Id = "mixingstationmk3";

		public const string SourceId = "mixingstationmk2";

		public const string DisplayName = "Mixing Station MkIII";

		private static BuildableItemDefinition _def;

		private static Sprite _icon;

		private static readonly string[] IllegalShopMarkers = new string[2] { "phosphorus", "acid" };

		public static BuildableItemDefinition Definition => _def;

		public static bool IsMkIII(ItemInstance item)
		{
			if (item != null)
			{
				return ((BaseItemInstance)item).ID == "mixingstationmk3";
			}
			return false;
		}

		public static void EnsureRegistered()
		{
			//IL_013a: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				Registry instance = Singleton<Registry>.Instance;
				if ((Object)(object)instance == (Object)null)
				{
					return;
				}
				if (Registry.ItemExists("mixingstationmk3"))
				{
					if (_def == null)
					{
						ItemDefinition item = Registry.GetItem("mixingstationmk3");
						_def = ((item != null) ? ((Il2CppObjectBase)item).TryCast<BuildableItemDefinition>() : null);
					}
				}
				else
				{
					if (!Registry.ItemExists("mixingstationmk2"))
					{
						return;
					}
					ItemDefinition item2 = Registry.GetItem("mixingstationmk2");
					BuildableItemDefinition val = ((item2 != null) ? ((Il2CppObjectBase)item2).TryCast<BuildableItemDefinition>() : null);
					if (!((Object)(object)val == (Object)null))
					{
						if ((Object)(object)_def == (Object)null || ((Il2CppObjectBase)_def).WasCollected)
						{
							_def = Object.Instantiate<BuildableItemDefinition>(val);
							((Object)_def).name = "MixingStationMkIII";
							((Object)_def).hideFlags = (HideFlags)32;
						}
						((BaseItemDefinition)_def).ID = "mixingstationmk3";
						((BaseItemDefinition)_def).Name = "Mixing Station MkIII";
						((BaseItemDefinition)_def).Description = "Industrial automated mixer. Load a product and up to " + ModConfig.MaxMixSteps + " ingredients in order and it performs every mix stage by itself. Recognises your discovered recipes.";
						Sprite val2 = LoadIcon();
						if ((Object)(object)val2 != (Object)null)
						{
							((BaseItemDefinition)_def).Icon = val2;
						}
						((StorableItemDefinition)_def).BasePurchasePrice = ModConfig.MachinePrice;
						((StorableItemDefinition)_def).RequiresLevelToPurchase = true;
						((StorableItemDefinition)_def).RequiredRank = ParseRank();
						instance.AddToRegistry((ItemDefinition)(object)_def);
						Mod.Log.Msg("Registered item 'mixingstationmk3' (cloned from 'mixingstationmk2').");
					}
				}
			}
			catch (Exception value)
			{
				Mod.Log.Error($"Failed to register the MkIII item: {value}");
			}
		}

		private static FullRank ParseRank()
		{
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: 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_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			ERank result;
			ERank rank = (ERank)((!Enum.TryParse<ERank>(ModConfig.RequiredRank.Trim().Replace(' ', '_'), ignoreCase: true, out result)) ? 6 : ((int)result));
			return new FullRank
			{
				Rank = rank,
				Tier = ModConfig.RequiredRankTier
			};
		}

		private static Sprite LoadIcon()
		{
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Expected O, but got Unknown
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_icon != (Object)null && !((Il2CppObjectBase)_icon).WasCollected)
			{
				return _icon;
			}
			try
			{
				using Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("mk3icon.png");
				if (stream == null)
				{
					return null;
				}
				using MemoryStream memoryStream = new MemoryStream();
				stream.CopyTo(memoryStream);
				byte[] array = memoryStream.ToArray();
				Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false)
				{
					name = "MkIII Icon",
					hideFlags = (HideFlags)32
				};
				if (!ImageConversion.LoadImage(val, new Il2CppStructArray<byte>(array)))
				{
					return null;
				}
				_icon = Sprite.Create(val, new Rect(0f, 0f, (float)((Texture)val).width, (float)((Texture)val).height), new Vector2(0.5f, 0.5f), 100f);
				((Object)_icon).name = "MkIII Icon";
				((Object)_icon).hideFlags = (HideFlags)32;
				return _icon;
			}
			catch (Exception ex)
			{
				Mod.Log.Warning("Could not load the MkIII icon: " + ex.Message);
				return null;
			}
		}

		public static void AddShopListing(ShopInterface shop)
		{
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ca: 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_00e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_010c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0113: Unknown result type (might be due to invalid IL or missing references)
			//IL_011b: Expected O, but got Unknown
			try
			{
				if (((shop != null) ? shop.Listings : null) == null)
				{
					return;
				}
				EnsureRegistered();
				if ((Object)(object)_def == (Object)null)
				{
					return;
				}
				bool flag = false;
				ShopListing val = null;
				for (int i = 0; i < shop.Listings.Count; i++)
				{
					ShopListing val2 = shop.Listings[i];
					if (!((Object)(object)((val2 != null) ? val2.Item : null) == (Object)null))
					{
						if (((BaseItemDefinition)val2.Item).ID == "mixingstationmk3")
						{
							return;
						}
						if (IllegalShopMarkers.Contains(((BaseItemDefinition)val2.Item).ID))
						{
							flag = true;
						}
						if (((BaseItemDefinition)val2.Item).ID == "mixingstationmk2")
						{
							val = val2;
						}
					}
				}
				if (flag)
				{
					ShopListing val3 = new ShopListing
					{
						name = "Mixing Station MkIII",
						Item = (StorableItemDefinition)(object)_def,
						OverridePrice = true,
						OverriddenPrice = ModConfig.MachinePrice,
						LimitedStock = false,
						CanBeDelivered = (val == null || val.CanBeDelivered),
						UseIconTint = false,
						EnforceMinimumGameCreationVersion = false
					};
					shop.Listings.Add(val3);
					shop.CreateListingUI(val3);
					Mod.Log.Msg($"Added {"Mixing Station MkIII"} (${ModConfig.MachinePrice}, rank {ModConfig.RequiredRank} {ModConfig.RequiredRankTier}) to shop '{shop.ShopName}'.");
				}
			}
			catch (Exception value)
			{
				Mod.Log.Error($"Failed to add the MkIII shop listing: {value}");
			}
		}
	}
	internal static class Machines
	{
		private static readonly Dictionary<IntPtr, Machine> ByStation = new Dictionary<IntPtr, Machine>();

		public static readonly Dictionary<string, string> PendingLoad = new Dictionary<string, string>();

		public static IEnumerable<Machine> All => ByStation.Values;

		public static Machine Get(MixingStation station)
		{
			if (!((Object)(object)station != (Object)null) || !ByStation.TryGetValue(((Il2CppObjectBase)station).Pointer, out var value))
			{
				return null;
			}
			return value;
		}

		public static Machine GetOrCreate(MixingStation station, string guid)
		{
			Machine machine = Get(station);
			if (machine != null)
			{
				return machine;
			}
			machine = new Machine(station, guid);
			ByStation[((Il2CppObjectBase)station).Pointer] = machine;
			return machine;
		}

		public static void Remove(MixingStation station)
		{
			if ((Object)(object)station != (Object)null)
			{
				ByStation.Remove(((Il2CppObjectBase)station).Pointer);
			}
		}

		public static void Clear()
		{
			ByStation.Clear();
			PendingLoad.Clear();
		}

		public static void Prune()
		{
			foreach (IntPtr item in (from kv in ByStation
				where (Object)(object)kv.Value.Station == (Object)null || ((Il2CppObjectBase)kv.Value.Station).WasCollected
				select kv.Key).ToList())
			{
				ByStation.Remove(item);
			}
		}
	}
	public sealed class Mod : MelonMod
	{
		internal static Instance Log;

		private bool _inGame;

		public override void OnInitializeMelon()
		{
			Log = ((MelonBase)this).LoggerInstance;
			ModConfig.Init();
			Log.Msg($"Loaded. MaxMixSteps={ModConfig.MaxMixSteps}, mix time x{ModConfig.MixTimeMultiplier:0.##}, price ${ModConfig.MachinePrice}, debug spawn {(ModConfig.EnableDebugSpawn ? ModConfig.DebugSpawnKey : "off")}.");
		}

		public override void OnSceneWasLoaded(int buildIndex, string sceneName)
		{
			_inGame = sceneName == "Main";
			if (!_inGame)
			{
				Machines.Clear();
				CustomLights.Clear();
			}
			MkIIIItem.EnsureRegistered();
			CustomLights.EnsureRegistered();
		}

		public override void OnUpdate()
		{
			SelfTest.Update();
			if (!_inGame)
			{
				return;
			}
			try
			{
				Machines.Prune();
				float deltaTime = Time.deltaTime;
				foreach (Machine item in Machines.All.ToList())
				{
					item.Tick(deltaTime);
					item.Visual?.Update(deltaTime);
				}
				CustomLights.Update(deltaTime);
				if (ModConfig.EnableDebugSpawn && Input.KeyDown(ModConfig.DebugSpawnKey))
				{
					DebugSpawn();
				}
			}
			catch (Exception value)
			{
				Log.Error($"Update error: {value}");
			}
		}

		private static void DebugSpawn()
		{
			MkIIIItem.EnsureRegistered();
			PlayerInventory instance = PlayerSingleton<PlayerInventory>.Instance;
			if (!((Object)(object)MkIIIItem.Definition == (Object)null) && !((Object)(object)instance == (Object)null))
			{
				ItemInstance defaultInstance = ((ItemDefinition)MkIIIItem.Definition).GetDefaultInstance(1);
				if (!instance.CanItemFitInInventory(defaultInstance, 1))
				{
					Log.Warning("Debug spawn: inventory full.");
					return;
				}
				instance.AddItemToInventory(defaultInstance);
				Log.Msg("Debug spawn: added a Mixing Station MkIII to your inventory.");
			}
		}
	}
	internal static class Input
	{
		private static bool _legacyBroken;

		public static bool KeyDown(string keyName)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			if (!Enum.TryParse<KeyCode>(keyName, ignoreCase: true, out KeyCode result))
			{
				return false;
			}
			if (!_legacyBroken)
			{
				try
				{
					return Input.GetKeyDown(result);
				}
				catch
				{
					_legacyBroken = true;
				}
			}
			try
			{
				Keyboard current = Keyboard.current;
				if (current == null)
				{
					return false;
				}
				if (!Enum.TryParse<Key>(keyName, ignoreCase: true, out Key result2))
				{
					return false;
				}
				return ((ButtonControl)current[result2]).wasPressedThisFrame;
			}
			catch
			{
				return false;
			}
		}
	}
	internal static class ModConfig
	{
		private static MelonPreferences_Category _cat;

		private static MelonPreferences_Entry<int> _maxMixSteps;

		private static MelonPreferences_Entry<float> _mixTimeMultiplier;

		private static MelonPreferences_Entry<int> _firstMixMinutes;

		private static MelonPreferences_Entry<int> _extraMixMinutes;

		private static MelonPreferences_Entry<int> _machinePrice;

		private static MelonPreferences_Entry<string> _requiredRank;

		private static MelonPreferences_Entry<int> _requiredRankTier;

		private static MelonPreferences_Entry<bool> _enableDebugSpawn;

		private static MelonPreferences_Entry<string> _debugSpawnKey;

		private static MelonPreferences_Entry<int> _hiPrice;

		private static MelonPreferences_Entry<string> _hiRank;

		private static MelonPreferences_Entry<int> _hiRankTier;

		private static MelonPreferences_Entry<float> _hiGrowBonus;

		private static MelonPreferences_Entry<int> _usPrice;

		private static MelonPreferences_Entry<string> _usRank;

		private static MelonPreferences_Entry<int> _usRankTier;

		private static MelonPreferences_Entry<float> _usGrowBonus;

		public static int MaxMixSteps => Math.Clamp(_maxMixSteps.Value, 1, 16);

		public static float MixTimeMultiplier => Math.Max(0.01f, _mixTimeMultiplier.Value);

		public static int FirstMixMinutes => Math.Max(1, _firstMixMinutes.Value);

		public static int ExtraMixMinutes => Math.Max(1, _extraMixMinutes.Value);

		public static int MachinePrice => Math.Max(0, _machinePrice.Value);

		public static string RequiredRank => _requiredRank.Value;

		public static int RequiredRankTier => Math.Clamp(_requiredRankTier.Value, 1, 5);

		public static bool EnableDebugSpawn => _enableDebugSpawn.Value;

		public static string DebugSpawnKey => _debugSpawnKey.Value;

		public static int HiLightPrice => Math.Max(0, _hiPrice.Value);

		public static string HiLightRank => _hiRank.Value;

		public static int HiLightRankTier => Math.Clamp(_hiRankTier.Value, 1, 5);

		public static float HiLightGrowBonus => _hiGrowBonus.Value;

		public static int UltraLightPrice => Math.Max(0, _usPrice.Value);

		public static string UltraLightRank => _usRank.Value;

		public static int UltraLightRankTier => Math.Clamp(_usRankTier.Value, 1, 5);

		public static float UltraLightGrowBonus => _usGrowBonus.Value;

		public static void Init()
		{
			_cat = MelonPreferences.CreateCategory("MixingStationMkIII", "Mixing Station MkIII");
			_maxMixSteps = _cat.CreateEntry<int>("MaxMixSteps", 3, "Max mix steps", "Maximum number of sequential mix stages (= ingredient slots) on the MkIII. Takes effect after a game restart.", false, false, (ValueValidator)null, (string)null);
			_mixTimeMultiplier = _cat.CreateEntry<float>("MixTimeMultiplier", 1f, "Mix time multiplier", "Multiplies the MkIII stage times (FirstMixMinutes / ExtraMixMinutes). 0.5 = twice as fast, 2.0 = half speed.", false, false, (ValueValidator)null, (string)null);
			_firstMixMinutes = _cat.CreateEntry<int>("FirstMixMinutes", 40, "First mix minutes", "In-game minutes for the first mix stage (1 in-game minute is about 1 real second). Independent of quantity.", false, false, (ValueValidator)null, (string)null);
			_extraMixMinutes = _cat.CreateEntry<int>("ExtraMixMinutes", 20, "Extra mix minutes", "In-game minutes each additional mix stage adds, so longer chains are rewarded.", false, false, (ValueValidator)null, (string)null);
			_machinePrice = _cat.CreateEntry<int>("MachinePrice", 8500, "Machine price", "Shop price of the Mixing Station MkIII (the Mk2 costs 2000).", false, false, (ValueValidator)null, (string)null);
			_requiredRank = _cat.CreateEntry<string>("RequiredRank", "Shot_Caller", "Required rank", "Rank needed to buy it: Street_Rat, Hoodlum, Peddler, Hustler, Bagman, Enforcer, Shot_Caller, Block_Boss, Underlord, Baron, Kingpin.", false, false, (ValueValidator)null, (string)null);
			_requiredRankTier = _cat.CreateEntry<int>("RequiredRankTier", 1, "Required rank tier", "Tier (1-5) of the required rank.", false, false, (ValueValidator)null, (string)null);
			_enableDebugSpawn = _cat.CreateEntry<bool>("EnableDebugSpawn", false, "Enable debug spawn", "If true, pressing the debug key in-game puts a Mixing Station MkIII in your inventory for free.", false, false, (ValueValidator)null, (string)null);
			_debugSpawnKey = _cat.CreateEntry<string>("DebugSpawnKey", "F8", "Debug spawn key", "Key for the debug spawn (UnityEngine KeyCode name).", false, false, (ValueValidator)null, (string)null);
			_hiPrice = _cat.CreateEntry<int>("HiLightPrice", 600, "High-Intensity light price", "Shop price of the High-Intensity Grow Light.", false, false, (ValueValidator)null, (string)null);
			_hiRank = _cat.CreateEntry<string>("HiLightRank", "Hustler", "High-Intensity light rank", "Rank needed to buy the High-Intensity Grow Light (same names as RequiredRank).", false, false, (ValueValidator)null, (string)null);
			_hiRankTier = _cat.CreateEntry<int>("HiLightRankTier", 5, "High-Intensity light rank tier", "Tier (1-5) of that rank.", false, false, (ValueValidator)null, (string)null);
			_hiGrowBonus = _cat.CreateEntry<float>("HiLightGrowBonus", 0.4f, "High-Intensity light grow bonus", "Extra growth speed under a High-Intensity Grow Light (0.4 = 40% faster than a normal grow light).", false, false, (ValueValidator)null, (string)null);
			_usPrice = _cat.CreateEntry<int>("UltraLightPrice", 1500, "UltraSpectrum price", "Shop price of the UltraSpectrum LED Array.", false, false, (ValueValidator)null, (string)null);
			_usRank = _cat.CreateEntry<string>("UltraLightRank", "Bagman", "UltraSpectrum rank", "Rank needed to buy the UltraSpectrum LED Array (same names as RequiredRank).", false, false, (ValueValidator)null, (string)null);
			_usRankTier = _cat.CreateEntry<int>("UltraLightRankTier", 5, "UltraSpectrum rank tier", "Tier (1-5) of that rank.", false, false, (ValueValidator)null, (string)null);
			_usGrowBonus = _cat.CreateEntry<float>("UltraLightGrowBonus", 0.5f, "UltraSpectrum grow bonus", "Extra growth speed under an UltraSpectrum LED Array (0.5 = 50% faster than a normal grow light).", false, false, (ValueValidator)null, (string)null);
			_cat.SaveToFile(false);
		}
	}
	internal static class ModIcons
	{
		private static readonly Dictionary<string, Sprite> Cache = new Dictionary<string, Sprite>();

		public static Sprite Load(string resource, string name)
		{
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Expected O, but got Unknown
			//IL_0094: 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)
			if (Cache.TryGetValue(resource, out var value) && (Object)(object)value != (Object)null && !((Il2CppObjectBase)value).WasCollected)
			{
				return value;
			}
			try
			{
				using Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resource);
				if (stream == null)
				{
					return null;
				}
				using MemoryStream memoryStream = new MemoryStream();
				stream.CopyTo(memoryStream);
				Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false)
				{
					name = name,
					hideFlags = (HideFlags)32
				};
				if (!ImageConversion.LoadImage(val, new Il2CppStructArray<byte>(memoryStream.ToArray())))
				{
					return null;
				}
				Sprite val2 = Sprite.Create(val, new Rect(0f, 0f, (float)((Texture)val).width, (float)((Texture)val).height), new Vector2(0.5f, 0.5f), 100f);
				((Object)val2).name = name;
				((Object)val2).hideFlags = (HideFlags)32;
				Cache[resource] = val2;
				return val2;
			}
			catch (Exception ex)
			{
				Mod.Log.Warning("Could not load icon " + resource + ": " + ex.Message);
				return null;
			}
		}
	}
	internal static class SaveKeys
	{
		public const string AdditionalDataName = "MixingStationMkIII";
	}
	[HarmonyPatch(typeof(ShopInterface), "Start")]
	internal static class ShopStartPatch
	{
		private static void Prefix(ShopInterface __instance)
		{
			MkIIIItem.AddShopListing(__instance);
			CustomLights.AddShopListings(__instance);
		}
	}
	[HarmonyPatch(typeof(MixingStation), "InitializeGridItem")]
	internal static class InitializeGridItemPatch
	{
		private static void Prefix(MixingStation __instance, ref ItemInstance instance, string GUID)
		{
			if (GUID != null && Machines.PendingLoad.ContainsKey(GUID) && !MkIIIItem.IsMkIII(instance) && (Object)(object)MkIIIItem.Definition != (Object)null)
			{
				instance = ((ItemDefinition)MkIIIItem.Definition).GetDefaultInstance(1);
			}
			if (MkIIIItem.IsMkIII(instance))
			{
				try
				{
					MkIIIVisual.PrepareShape(((Component)__instance).gameObject);
				}
				catch (Exception value)
				{
					Mod.Log.Error($"MkIII shape setup failed: {value}");
				}
			}
		}

		private static void Postfix(MixingStation __instance, ItemInstance instance, string GUID)
		{
			try
			{
				if (MkIIIItem.IsMkIII(instance))
				{
					Machine orCreate = Machines.GetOrCreate(__instance, GUID);
					if (GUID != null && Machines.PendingLoad.Remove(GUID, out var value))
					{
						orCreate.LoadJson(value);
						Mod.Log.Msg($"Restored MkIII {GUID}: recipe={orCreate.Recipe?.TargetId ?? "none"}, stage {orCreate.Stage + 1}, qty {orCreate.Quantity}, running={orCreate.Running}");
					}
					Machine machine = orCreate;
					if (machine.Visual == null)
					{
						machine.Visual = new MkIIIVisual(orCreate);
					}
					orCreate.Visual.Attach();
				}
			}
			catch (Exception value2)
			{
				Mod.Log.Error($"MkIII init failed: {value2}");
			}
		}
	}
	[HarmonyPatch(typeof(BuildStart_Grid), "CreateGhostModel")]
	internal static class GhostPatch
	{
		private static void Postfix(BuildableItemDefinition itemDefinition, GridItem __result)
		{
			if (!((Object)(object)itemDefinition == (Object)null) && !((Object)(object)__result == (Object)null) && !(((BaseItemDefinition)itemDefinition).ID != "mixingstationmk3"))
			{
				MkIIIVisual.ApplyToGhost(((Component)__result).gameObject);
			}
		}
	}
	[HarmonyPatch(typeof(BuildManager), "ApplyMaterial")]
	internal static class GhostMaterialPatch
	{
		private static void Postfix(GameObject obj, Material mat)
		{
			Transform val = (((Object)(object)obj == (Object)null) ? null : (obj.transform.Find("MkIII Model") ?? obj.transform.Find("Custom Light Model")));
			if ((Object)(object)val == (Object)null || (Object)(object)mat == (Object)null)
			{
				return;
			}
			foreach (MeshRenderer componentsInChild in ((Component)val).GetComponentsInChildren<MeshRenderer>(true))
			{
				Il2CppReferenceArray<Material> val2 = new Il2CppReferenceArray<Material>((long)((Il2CppArrayBase<Material>)(object)((Renderer)componentsInChild).sharedMaterials).Length);
				for (int i = 0; i < ((Il2CppArrayBase<Material>)(object)val2).Length; i++)
				{
					((Il2CppArrayBase<Material>)(object)val2)[i] = mat;
				}
				((Renderer)componentsInChild).sharedMaterials = val2;
			}
		}
	}
	[HarmonyPatch(typeof(MixingStation), "CanStartMix")]
	internal static class CanStartMixPatch
	{
		private static void Postfix(MixingStation __instance, ref bool __result)
		{
			Machine machine = Machines.Get(__instance);
			if (machine != null)
			{
				__result = machine.EmployeeCanStart(out var _);
			}
		}
	}
	[HarmonyPatch(typeof(MixingStation), "GetMixQuantity")]
	internal static class GetMixQuantityPatch
	{
		private static void Postfix(MixingStation __instance, ref int __result)
		{
			Machine machine = Machines.Get(__instance);
			if (machine != null)
			{
				__result = (machine.EmployeeCanStart(out var quantity) ? quantity : 0);
			}
		}
	}
	[HarmonyPatch(typeof(StartMixingStationBehaviour), "CanCookStart")]
	internal static class ChemistCanCookPatch
	{
		private static void Postfix(StartMixingStationBehaviour __instance, ref bool __result)
		{
			Machine machine = Machines.Get(__instance.targetStation);
			if (machine != null)
			{
				__result = machine.EmployeeCanStart(out var _);
			}
		}
	}
	[HarmonyPatch(typeof(StartMixingStationBehaviour), "StartCook")]
	internal static class ChemistStartCookPatch
	{
		private static bool Prefix(StartMixingStationBehaviour __instance)
		{
			Machine machine = Machines.Get(__instance.targetStation);
			if (machine == null)
			{
				return true;
			}
			try
			{
				string text = machine.Begin(machine.Recipe, isNewMix: false);
				if (text != null)
				{
					Mod.Log.Msg("MkIII " + machine.Guid + ": employee could not start: " + text);
				}
				__instance.StopCook();
				((Behaviour)__instance).Deactivate();
			}
			catch (Exception value)
			{
				Mod.Log.Error($"MkIII employee start failed: {value}");
			}
			return false;
		}
	}
	[HarmonyPatch(typeof(MixingStationLoader), "Load", new Type[] { typeof(DynamicSaveData) })]
	internal static class MixingStationLoaderPatch
	{
		private static readonly Regex GuidRx = new Regex("\"GUID\"\\s*:\\s*\"([^\"]+)\"");

		private static void Prefix(DynamicSaveData data)
		{
			try
			{
				if (((data != null) ? data.AdditionalDatas : null) == null)
				{
					return;
				}
				for (int i = 0; i < data.AdditionalDatas.Count; i++)
				{
					AdditionalData val = data.AdditionalDatas[i];
					if (!(((val != null) ? val.Name : null) != "MixingStationMkIII"))
					{
						Match match = GuidRx.Match(data.BaseData ?? "");
						if (!match.Success)
						{
							break;
						}
						MkIIIItem.EnsureRegistered();
						Machines.PendingLoad[match.Groups[1].Value] = val.Contents;
					}
				}
			}
			catch (Exception value)
			{
				Mod.Log.Error($"MkIII load hook failed: {value}");
			}
		}
	}
	[HarmonyPatch(typeof(MixingStation), "GetSaveData")]
	internal static class GetSaveDataPatch
	{
		private static void Postfix(MixingStation __instance, DynamicSaveData __result)
		{
			try
			{
				Machine machine = Machines.Get(__instance);
				if (machine != null && __result != null)
				{
					__result.BaseData = __result.BaseData?.Replace("mixingstationmk3", "mixingstationmk2");
					__result.AddData("MixingStationMkIII", machine.ToJson());
				}
			}
			catch (Exception value)
			{
				Mod.Log.Error($"MkIII save failed: {value}");
			}
		}
	}
	[HarmonyPatch(typeof(MixingStation), "Hovered")]
	internal static class HoveredPatch
	{
		private static void Postfix(MixingStation __instance)
		{
			if (Machines.Get(__instance) != null && !((Object)(object)__instance.IntObj == (Object)null))
			{
				__instance.IntObj.SetMessage("Use Mixing Station MkIII");
			}
		}
	}
	[HarmonyPatch(typeof(MixingStation), "GetManagementName")]
	internal static class ManagementNamePatch
	{
		private static void Postfix(MixingStation __instance, ref string __result)
		{
			if (Machines.Get(__instance) != null)
			{
				__result = "Mixing Station MkIII";
			}
		}
	}
	[HarmonyPatch(typeof(BuildableItem), "CanBePickedUp")]
	internal static class CanBePickedUpPatch
	{
		private static void Postfix(BuildableItem __instance, ref string reason, ref bool __result)
		{
			if (__result)
			{
				MixingStation val = ((Il2CppObjectBase)__instance).TryCast<MixingStation>();
				Machine machine = (((Object)(object)val == (Object)null) ? null : Machines.Get(val));
				if (machine != null && (machine.Running || !machine.ExtraSlotsEmpty))
				{
					__result = false;
					reason = (machine.Running ? "The MkIII is mixing" : "Empty the MkIII ingredient slots first");
				}
			}
		}
	}
	[HarmonyPatch(typeof(MixingStation), "CanBeDestroyed")]
	internal static class CanBeDestroyedPatch
	{
		private static void Postfix(MixingStation __instance, ref string reason, ref bool __result)
		{
			Machine machine = Machines.Get(__instance);
			if (__result && machine != null && (machine.Running || !machine.ExtraSlotsEmpty))
			{
				__result = false;
				reason = (machine.Running ? "The MkIII is mixing" : "Empty the MkIII ingredient slots first");
			}
		}
	}
	[HarmonyPatch(typeof(MixingStation), "Destroy")]
	internal static class DestroyPatch
	{
		private static void Prefix(MixingStation __instance)
		{
			Machine machine = Machines.Get(__instance);
			if (machine != null)
			{
				if (!machine.ExtraSlotsEmpty)
				{
					Mod.Log.Warning("MkIII " + machine.Guid + " destroyed while holding items.");
				}
				machine.Visual?.Detach();
				Machines.Remove(__instance);
			}
		}
	}
	[HarmonyPatch(typeof(MixingStation), "SendMixingOperation")]
	internal static class BlockVanillaMixPatch
	{
		private static bool Prefix(MixingStation __instance)
		{
			if (!Machine.SendingOwnMix)
			{
				return Machines.Get(__instance) == null;
			}
			return true;
		}
	}
	[HarmonyPatch(typeof(MixingStationInterface), "Open")]
	internal static class InterfaceOpenPatch
	{
		private static void Postfix(MixingStationInterface __instance, MixingStation station)
		{
			MkIIIInterface.OnOpen(__instance, station);
		}
	}
	[HarmonyPatch(typeof(MixingStationInterface), "Close")]
	internal static class InterfaceClosePatch
	{
		private static void Prefix()
		{
			MkIIIInterface.OnClose();
		}
	}
	[HarmonyPatch]
	internal static class InterfaceRefreshPatch
	{
		private static IEnumerable<MethodBase> TargetMethods()
		{
			return new MethodInfo[5]
			{
				AccessTools.Method(typeof(MixingStationInterface), "UpdatePreview", (Type[])null, (Type[])null),
				AccessTools.Method(typeof(MixingStationInterface), "UpdateInstruction", (Type[])null, (Type[])null),
				AccessTools.Method(typeof(MixingStationInterface), "UpdateBeginButton", (Type[])null, (Type[])null),
				AccessTools.Method(typeof(MixingStationInterface), "UpdateDisplayMode", (Type[])null, (Type[])null),
				AccessTools.Method(typeof(MixingStationInterface), "StationContentsChanged", (Type[])null, (Type[])null)
			};
		}

		private static void Postfix(MixingStationInterface __instance)
		{
			if (MkIIIInterface.ActiveFor(__instance.Station))
			{
				MkIIIInterface.Refresh();
			}
		}
	}
	[HarmonyPatch(typeof(MixingStationInterface), "Update")]
	internal static class InterfaceUpdatePatch
	{
		private static float _next;

		private static void Postfix(MixingStationInterface __instance)
		{
			if (MkIIIInterface.ActiveFor(__instance.Station) && !(Time.unscaledTime < _next))
			{
				_next = Time.unscaledTime + 0.25f;
				MkIIIInterface.Refresh();
			}
		}
	}
	[HarmonyPatch(typeof(MixingStationInterface), "CheckForUnknownMix")]
	internal static class InterfaceUnknownMixPatch
	{
		private static bool Prefix(MixingStationInterface __instance)
		{
			if (!MkIIIInterface.ActiveFor(__instance.Station))
			{
				return true;
			}
			MixingStation station = __instance.Station;
			MixOperation currentMixOperation = station.CurrentMixOperation;
			ProductDefinition val = default(ProductDefinition);
			if (currentMixOperation != null && station.IsMixingDone)
			{
				return !currentMixOperation.IsOutputKnown(ref val);
			}
			return false;
		}
	}
	[HarmonyPatch(typeof(MixingStationInterface), "BeginButtonPressed")]
	internal static class InterfaceBeginPatch
	{
		private static bool Prefix(MixingStationInterface __instance)
		{
			if (!MkIIIInterface.ActiveFor(__instance.Station))
			{
				return true;
			}
			MkIIIInterface.BeginPressed();
			return false;
		}
	}
	[HarmonyPatch(typeof(MixingStation), "MixingStart")]
	internal static class StageStartSoundPatch
	{
		private static bool Prefix(MixingStation __instance)
		{
			Machine machine = Machines.Get(__instance);
			if (machine == null || !machine.Running || machine.IsFirstStage)
			{
				return true;
			}
			UnityEvent onMixStart = __instance.onMixStart;
			if (onMixStart != null)
			{
				onMixStart.Invoke();
			}
			return false;
		}
	}
	[HarmonyPatch(typeof(MixingStation), "MixingDone")]
	internal static class StageDoneSoundPatch
	{
		private static bool Prefix(MixingStation __instance)
		{
			Machine machine = Machines.Get(__instance);
			if (machine == null || !machine.Running || machine.IsFinishingStage)
			{
				return true;
			}
			UnityEvent onMixDone = __instance.onMixDone;
			if (onMixDone != null)
			{
				onMixDone.Invoke();
			}
			return false;
		}
	}
	[HarmonyPatch(typeof(MixingStation), "OnTimePass")]
	internal static class ClockRefreshPatch
	{
		private static void Postfix(MixingStation __instance)
		{
			Machines.Get(__instance)?.Visual?.UpdateClock();
		}
	}
	[HarmonyPatch(typeof(MixingStationMk2), "UpdateScreen")]
	internal static class TabletScreenPatch
	{
		private static void Postfix(MixingStationMk2 __instance)
		{
			Machine machine = Machines.Get((MixingStation)(object)__instance);
			if (machine != null && !((Object)(object)__instance.ProgressLabel == (Object)null))
			{
				if (machine.Running && !machine.AwaitingName && ((MixingStation)__instance).CurrentMixOperation != null && !((MixingStation)__instance).IsMixingDone)
				{
					((TMP_Text)__instance.ProgressLabel).text = $"{Math.Max(0, machine.MinutesLeft())} mins remaining";
				}
				else if (machine.AwaitingName || (!machine.Running && ((MixingStation)__instance).CurrentMixOperation != null && ((MixingStation)__instance).IsMixingDone))
				{
					__instance.DisableScreen();
				}
			}
		}
	}
	[HarmonyPatch(typeof(GrowLight), "InitializeProceduralGridItem")]
	internal static class GrowLightInitPatch
	{
		private static void Postfix(GrowLight __instance, ItemInstance instance)
		{
			LightSpec lightSpec = CustomLights.Get(instance);
			if (lightSpec != null)
			{
				CustomLights.Attach(__instance, lightSpec);
			}
		}
	}
	[HarmonyPatch(typeof(BuildStart_ProceduralGrid), "CreateGhostModel")]
	internal static class GrowLightGhostPatch
	{
		private static void Postfix(BuildableItemDefinition itemDefinition, ProceduralGridItem __result)
		{
			LightSpec lightSpec = CustomLights.Get((itemDefinition != null) ? ((BaseItemDefinition)itemDefinition).ID : null);
			if (lightSpec == null || (Object)(object)__result == (Object)null)
			{
				return;
			}
			try
			{
				CustomLights.ApplyToGhost(((Component)__result).gameObject, lightSpec);
			}
			catch (Exception value)
			{
				Mod.Log.Error($"High-Intensity ghost failed: {value}");
			}
		}
	}
	internal sealed class RecipeChain
	{
		public string TargetId;

		public string BaseId;

		public List<string> Mixers = new List<string>();

		public int Stages => Mixers.Count;

		public string Signature => BaseId + ">" + string.Join(">", Mixers) + "=" + TargetId;

		public Dictionary<string, int> MixerCounts()
		{
			return (from m in Mixers
				group m by m).ToDictionary((IGrouping<string, string> g) => g.Key, (IGrouping<string, string> g) => g.Count());
		}
	}
	internal sealed class CatalogEntry
	{
		public RecipeChain Chain;

		public bool TooManyStages;

		public string Problem;

		public bool Available
		{
			get
			{
				if (!TooManyStages)
				{
					return Problem == null;
				}
				return false;
			}
		}
	}
	internal static class RecipeCatalog
	{
		private const int SearchDepth = 12;

		public static List<CatalogEntry> Build()
		{
			List<CatalogEntry> list = new List<CatalogEntry>();
			ProductManager instance = NetworkSingleton<ProductManager>.Instance;
			if ((Object)(object)instance == (Object)null)
			{
				return list;
			}
			Dictionary<string, List<(string, string)>> dictionary = new Dictionary<string, List<(string, string)>>();
			List<StationRecipe> mixRecipes = instance.mixRecipes;
			for (int i = 0; i < mixRecipes.Count; i++)
			{
				StationRecipe val = mixRecipes[i];
				object obj;
				if (val == null)
				{
					obj = null;
				}
				else
				{
					ItemQuantity product = val.Product;
					obj = ((product != null) ? product.Item : null);
				}
				if ((Object)obj == (Object)null || val.Ingredients == null)
				{
					continue;
				}
				string text = null;
				string text2 = null;
				for (int j = 0; j < val.Ingredients.Count; j++)
				{
					IngredientQuantity obj2 = val.Ingredients[j];
					ItemDefinition val2 = ((obj2 != null) ? obj2.Item : null);
					if (!((Object)(object)val2 == (Object)null))
					{
						if ((Object)(object)((Il2CppObjectBase)val2).TryCast<ProductDefinition>() != (Object)null && text == null)
						{
							text = ((BaseItemDefinition)val2).ID;
						}
						else
						{
							text2 = ((BaseItemDefinition)val2).ID;
						}
					}
				}
				if (text != null && text2 != null)
				{
					string iD = ((BaseItemDefinition)val.Product.Item).ID;
					if (!dictionary.TryGetValue(iD, out var value))
					{
						value = (dictionary[iD] = new List<(string, string)>());
					}
					if (!value.Contains((text, text2)))
					{
						value.Add((text, text2));
					}
				}
			}
			HashSet<string> hashSet = new HashSet<string>();
			List<ProductDefinition> discoveredProducts = ProductManager.DiscoveredProducts;
			for (int k = 0; k < discoveredProducts.Count; k++)
			{
				ProductDefinition val3 = discoveredProducts[k];
				if ((Object)(object)val3 == (Object)null || !dictionary.ContainsKey(((BaseItemDefinition)val3).ID) || !hashSet.Add(((BaseItemDefinition)val3).ID))
				{
					continue;
				}
				RecipeChain recipeChain = FindChain(((BaseItemDefinition)val3).ID, dictionary);
				if (recipeChain != null)
				{
					CatalogEntry catalogEntry = new CatalogEntry
					{
						Chain = recipeChain,
						TooManyStages = (recipeChain.Stages > ModConfig.MaxMixSteps)
					};
					if (!catalogEntry.TooManyStages)
					{
						catalogEntry.Problem = Validate(recipeChain);
					}
					list.Add(catalogEntry);
				}
			}
			return (from e in list
				orderby e.TooManyStages, Name(e.Chain.TargetId)
				select e).ToList();
		}

		private static RecipeChain FindChain(string target, Dictionary<string, List<(string product, string mixer)>> producers)
		{
			Queue<(string, List<string>)> queue = new Queue<(string, List<string>)>();
			HashSet<string> hashSet = new HashSet<string> { target };
			queue.Enqueue((target, new List<string>()));
			while (queue.Count > 0)
			{
				var (text, list) = queue.Dequeue();
				if (!producers.TryGetValue(text, out List<(string, string)> value))
				{
					if (list.Count == 0)
					{
						return null;
					}
					List<string> list2 = new List<string>(list);
					list2.Reverse();
					return new RecipeChain
					{
						TargetId = target,
						BaseId = text,
						Mixers = list2
					};
				}
				if (list.Count >= 12)
				{
					continue;
				}
				foreach (var (text2, item) in value)
				{
					if (hashSet.Add(text2))
					{
						List<string> item2 = new List<string>(list) { item };
						queue.Enqueue((text2, item2));
					}
				}
			}
			return null;
		}

		public static string Validate(RecipeChain chain)
		{
			string text = chain.BaseId;
			foreach (string mixer in chain.Mixers)
			{
				ProductDefinition val = MixOnce(text, mixer);
				if ((Object)(object)val == (Object)null)
				{
					return Name(text) + " + " + Name(mixer) + " gives an undiscovered product";
				}
				text = ((BaseItemDefinition)val).ID;
			}
			if (!(text == chain.TargetId))
			{
				return "Chain now produces " + Name(text) + " instead";
			}
			return null;
		}

		public static ProductDefinition MixOnce(string productId, string mixerId)
		{
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				ProductDefinition val = default(ProductDefinition);
				return new MixOperation(productId, (EQuality)2, mixerId, 1).IsOutputKnown(ref val) ? val : null;
			}
			catch (Exception ex)
			{
				Mod.Log.Warning($"Mix lookup failed for {productId} + {mixerId}: {ex.Message}");
				return null;
			}
		}

		public static string Name(string id)
		{
			if (string.IsNullOrEmpty(id))
			{
				return "?";
			}
			ItemDefinition val = (Registry.ItemExists(id) ? Registry.GetItem(id) : null);
			if (!((Object)(object)val != (Object)null))
			{
				return id;
			}
			return ((BaseItemDefinition)val).Name;
		}
	}
	internal static class SelfTest
	{
		private enum Step
		{
			Idle,
			Loading,
			Opening,
			Selecting,
			Capturing,
			ModelShow,
			ModelCapture,
			Done
		}

		private static Step _step = Step.Idle;

		private static float _next;

		private static float _checkFile;

		private static ShopInterface _shop;

		private static ListingUI _ui;

		private static string _itemId = "mixingstationmk3";

		private static GameObject _preview;

		private static string Trigger => Path.Combine(MelonEnvironment.UserDataDirectory, "MkIII_selftest.txt");

		private static string Shot => Path.Combine(MelonEnvironment.UserDataDirectory, "MkIII_shop.png");

		private static string ModelShot => Path.Combine(MelonEnvironment.UserDataDirectory, "MkIII_model.png");

		private static void Start(float now)
		{
			string mode = "";
			try
			{
				mode = File.ReadAllText(Trigger).Trim().ToLowerInvariant();
			}
			catch
			{
			}
			_itemId = CustomLights.Specs.FirstOrDefault((LightSpec s) => mode.Contains(s.Id) || (mode.Contains("hilight") && s.Id.StartsWith("highintensity")))?.Id ?? "mixingstationmk3";
			_step = Step.Loading;
			_shop = null;
			_ui = null;
			_next = now + 3f;
			Mod.Log.Msg("SELFTEST: started (" + _itemId + ").");
		}

		public static void Update()
		{
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_056b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0575: Expected O, but got Unknown
			//IL_058a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0596: Unknown result type (might be due to invalid IL or missing references)
			//IL_05a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_05a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_05b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_05bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_05c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_05d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_05d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_05dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_05e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_05f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_05fb: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				float unscaledTime = Time.unscaledTime;
				if (_step == Step.Idle || _step == Step.Done)
				{
					if (unscaledTime < _checkFile)
					{
						return;
					}
					_checkFile = unscaledTime + 2f;
					if (!File.Exists(Trigger))
					{
						return;
					}
					Start(unscaledTime);
				}
				if (unscaledTime < _next)
				{
					return;
				}
				LoadManager instance = Singleton<LoadManager>.Instance;
				switch (_step)
				{
				case Step.Loading:
				{
					Scene activeScene = SceneManager.GetActiveScene();
					if (((Scene)(ref activeScene)).name == "Menu" && (Object)(object)instance != (Object)null && !instance.IsLoading)
					{
						Mod.Log.Msg("SELFTEST: loading last save.");
						instance.LoadLastSave();
						_next = unscaledTime + 10f;
						break;
					}
					if ((Object)(object)instance != (Object)null && instance.IsGameLoaded && instance.TimeSinceGameLoaded > 10f)
					{
						_step = Step.Opening;
					}
					_next = unscaledTime + 1f;
					break;
				}
				case Step.Opening:
				{
					Enumerator<ShopInterface> enumerator = ShopInterface.AllShops.GetEnumerator();
					while (enumerator.MoveNext())
					{
						ShopInterface current = enumerator.Current;
						if (((current != null) ? current.Listings : null) == null)
						{
							continue;
						}
						bool flag = false;
						for (int i = 0; i < current.Listings.Count; i++)
						{
							bool num = flag;
							ShopListing obj = current.Listings[i];
							object obj2;
							if (obj == null)
							{
								obj2 = null;
							}
							else
							{
								StorableItemDefinition item = obj.Item;
								obj2 = ((item != null) ? ((BaseItemDefinition)item).ID : null);
							}
							flag = num | ((string?)obj2 == _itemId);
						}
						if (flag)
						{
							Mod.Log.Msg($"SELFTEST: shop '{current.ShopName}' lists {_itemId} ({current.Listings.Count} listings, {current.listingUI?.Count ?? (-1)} listing buttons).");
							if (_shop == null)
							{
								_shop = current;
							}
						}
					}
					if ((Object)(object)_shop == (Object)null)
					{
						Finish("no shop lists " + _itemId);
						break;
					}
					_shop.SetIsOpen(true);
					_step = Step.Selecting;
					_next = unscaledTime + 1.5f;
					break;
				}
				case Step.Selecting:
				{
					int num2 = 0;
					int num3 = 0;
					for (int j = 0; j < _shop.listingUI.Count; j++)
					{
						ListingUI val2 = _shop.listingUI[j];
						if (!((Object)(object)val2 == (Object)null) && ((Component)val2).gameObject.activeSelf)
						{
							ShopListing listing = val2.Listing;
							object obj4;
							if (listing == null)
							{
								obj4 = null;
							}
							else
							{
								StorableItemDefinition item2 = listing.Item;
								obj4 = ((item2 != null) ? ((BaseItemDefinition)item2).ID : null);
							}
							if ((string?)obj4 == _itemId)
							{
								_ui = val2;
								num2 = num3;
							}
							num3++;
						}
					}
					if ((Object)(object)_ui == (Object)null)
					{
						Finish("the shop has no visible button for " + _itemId);
						break;
					}
					Instance log2 = Mod.Log;
					DefaultInterpolatedStringHandler defaultInterpolatedStringHandler = new DefaultInterpolatedStringHandler(55, 6);
					defaultInterpolatedStringHandler.AppendLiteral("SELFTEST: button ");
					defaultInterpolatedStringHandler.AppendFormatted(num2 + 1);
					defaultInterpolatedStringHandler.AppendLiteral("/");
					defaultInterpolatedStringHandler.AppendFormatted(num3);
					defaultInterpolatedStringHandler.AppendLiteral(", name '");
					TextMeshProUGUI nameLabel = _ui.NameLabel;
					defaultInterpolatedStringHandler.AppendFormatted((nameLabel != null) ? ((TMP_Text)nameLabel).text : null);
					defaultInterpolatedStringHandler.AppendLiteral("', price '");
					TextMeshProUGUI priceLabel = _ui.PriceLabel;
					defaultInterpolatedStringHandler.AppendFormatted((priceLabel != null) ? ((TMP_Text)priceLabel).text : null);
					defaultInterpolatedStringHandler.AppendLiteral("', ");
					defaultInterpolatedStringHandler.AppendLiteral("locked=");
					defaultInterpolatedStringHandler.AppendFormatted((Object)(object)_ui.LockedContainer != (Object)null && _ui.LockedContainer.activeSelf);
					defaultInterpolatedStringHandler.AppendLiteral(", shown=");
					defaultInterpolatedStringHandler.AppendFormatted(_ui.Listing.ShouldShow());
					defaultInterpolatedStringHandler.AppendLiteral(".");
					log2.Msg(defaultInterpolatedStringHandler.ToStringAndClear());
					if ((Object)(object)_shop.ListingScrollRect != (Object)null && num3 > 1)
					{
						_shop.ListingScrollRect.verticalNormalizedPosition = 1f - (float)num2 / (float)(num3 - 1);
					}
					_ui.HoverStart();
					_step = Step.Capturing;
					_next = unscaledTime + 1.5f;
					break;
				}
				case Step.Capturing:
				{
					ShopInterfaceDetailPanel detailPanel = _shop.DetailPanel;
					if (detailPanel != null)
					{
						detailPanel.Open(_ui);
					}
					if (File.Exists(Shot))
					{
						File.Delete(Shot);
					}
					ScreenCapture.CaptureScreenshot(Shot);
					Instance log = Mod.Log;
					ShopInterfaceDetailPanel detailPanel2 = _shop.DetailPanel;
					object obj3;
					if (detailPanel2 == null)
					{
						obj3 = null;
					}
					else
					{
						TextMeshProUGUI descriptionLabel = detailPanel2.DescriptionLabel;
						obj3 = ((descriptionLabel != null) ? ((TMP_Text)descriptionLabel).text : null);
					}
					log.Msg("SELFTEST: detail '" + (string?)obj3 + "'.");
					if (CustomLights.Get(_itemId) == null)
					{
						Finish(null);
						break;
					}
					_step = Step.ModelShow;
					_next = unscaledTime + 1f;
					break;
				}
				case Step.ModelShow:
				{
					_shop.SetIsOpen(false);
					Camera main = Camera.main;
					if ((Object)(object)main == (Object)null)
					{
						Finish("no camera for the model shot");
						break;
					}
					_preview = new GameObject("HI Selftest Preview");
					Transform transform = _preview.transform;
					transform.position = ((Component)main).transform.position + ((Component)main).transform.forward * 0.95f + ((Component)main).transform.up * 0.12f;
					transform.rotation = Quaternion.LookRotation(-((Component)main).transform.forward, Vector3.up) * Quaternion.Euler(-25f, 35f, 0f);
					MeshRenderer val = CustomLights.Get(_itemId).Model.Build(transform);
					if ((Object)(object)val == (Object)null)
					{
						Finish("model failed to build");
						break;
					}
					((Component)val).gameObject.layer = 0;
					_step = Step.ModelCapture;
					_next = unscaledTime + 1.5f;
					break;
				}
				case Step.ModelCapture:
					if (File.Exists(ModelShot))
					{
						File.Delete(ModelShot);
					}
					ScreenCapture.CaptureScreenshot(ModelShot);
					_step = Step.Done;
					_next = unscaledTime + 1f;
					Finish(null);
					break;
				}
			}
			catch (Exception ex)
			{
				Finish("error: " + ex.Message);
			}
		}

		private static void Finish(string problem)
		{
			_step = Step.Done;
			try
			{
				File.Delete(Trigger);
			}
			catch
			{
			}
			if ((Object)(object)_preview != (Object)null && problem != null)
			{
				Object.Destroy((Object)(object)_preview);
			}
			Mod.Log.Msg((problem == null) ? ("SELFTEST: DONE, screenshot " + Shot) : ("SELFTEST: FAILED, " + problem));
		}
	}
}
namespace MixingStationMkIII.UI
{
	internal static class MkIIIInterface
	{
		private sealed class Preview
		{
			public List<CatalogEntry> Candidates = new List<CatalogEntry>();

			public CatalogEntry Selected;

			public RecipeChain Chain;

			public bool IsNewMix;

			public string KnownResultId;

			public bool Filtered;

			public int LoadedMixers;
		}

		private const float SlotGap = 60f;

		private const float ArrowGap = 99f;

		private const float PreviewGap = 102f;

		private static MixingStationInterface _iface;

		private static Machine _machine;

		private static bool _built;

		private static RectTransform _itemSlots;

		private static RectTransform _plus;

		private static RectTransform _arrow;

		private static RectTransform _shade;

		private static readonly List<ItemSlotUI> ExtraSlotUIs = new List<ItemSlotUI>();

		private static readonly List<RectTransform> ExtraPlus = new List<RectTransform>();

		private static readonly Dictionary<IntPtr, Image> Ghosts = new Dictionary<IntPtr, Image>();

		private static Button _prev;

		private static Button _next;

		private static TextMeshProUGUI _title;

		private static string _vanillaTitle;

		private static Vector2 _vProduct;

		private static Vector2 _vPlus;

		private static Vector2 _vIngredient;

		private static Vector2 _vArrow;

		private static Vector2 _vPreview;

		private static Vector2 _vShade;

		private static List<CatalogEntry> _catalog = new List<CatalogEntry>();

		private static float _catalogAt;

		private static string _selected;

		private static IntPtr _namingAskedFor;

		public static bool ActiveFor(MixingStation station)
		{
			if ((Object)(object)_iface != (Object)null && _machine != null)
			{
				return (Object)(object)_machine.Station == (Object)(object)station;
			}
			return false;
		}

		public static void OnOpen(MixingStationInterface iface, MixingStation station)
		{
			try
			{
				_iface = iface;
				_machine = Machines.Get(station);
				Build(iface);
				if (_machine == null)
				{
					RestoreVanilla();
					return;
				}
				_machine.EnsureExtraSlots();
				_machine.UiOpen = true;
				_machine.ClearRecipeFilters();
				_catalog = RecipeCatalog.Build();
				_catalogAt = Time.unscaledTime;
				_selected = _machine.Recipe?.Signature;
				for (int i = 0; i < ExtraSlotUIs.Count; i++)
				{
					ExtraSlotUIs[i].AssignSlot((i < _machine.ExtraSlots.Count) ? _machine.ExtraSlots[i] : null);
				}
				AddQuickMoveSlots();
				if ((Object)(object)_title != (Object)null)
				{
					((TMP_Text)_title).text = "Mixing Station MkIII";
				}
				((Component)_prev).gameObject.SetActive(true);
				((Component)_next).gameObject.SetActive(true);
				Refresh();
			}
			catch (Exception value)
			{
				Mod.Log.Error($"MkIII screen setup failed: {value}");
			}
		}

		public static void OnClose()
		{
			foreach (ItemSlotUI extraSlotUI in ExtraSlotUIs)
			{
				try
				{
					extraSlotUI.ClearSlot();
				}
				catch
				{
				}
			}
			if (_machine != null)
			{
				_machine.UiOpen = false;
				_machine.ApplyRecipeFilters();
			}
			_machine = null;
		}

		private static void AddQuickMoveSlots()
		{
			ItemUIManager instance = Singleton<ItemUIManager>.Instance;
			List<ItemSlot> val = ((instance != null) ? instance.SecondarySlots : null);
			if (val == null)
			{
				return;
			}
			foreach (ItemSlot extraSlot in _machine.ExtraSlots)
			{
				if (!val.Contains(extraSlot))
				{
					val.Add(extraSlot);
				}
			}
		}

		private static void Build(MixingStationInterface iface)
		{
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0107: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_010c: Unknown result type (might be due to invalid IL or missing references)
			//IL_012a: Unknown result type (might be due to invalid IL or missing references)
			//IL_011e: Unknown result type (might be due to invalid IL or missing references)
			//IL_012f: Unknown result type (might be due to invalid IL or missing references)
			//IL_014d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0141: Unknown result type (might be due to invalid IL or missing references)
			//IL_0152: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_031e: Unknown result type (might be due to invalid IL or missing references)
			//IL_032b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0335: Unknown result type (might be due to invalid IL or missing references)
			//IL_036b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0378: Unknown result type (might be due to invalid IL or missing references)
			//IL_0382: Unknown result type (might be due to invalid IL or missing references)
			if (_built && (Object)(object)_itemSlots != (Object)null && !((Il2CppObjectBase)_itemSlots).WasCollected)
			{
				return;
			}
			ExtraSlotUIs.Clear();
			ExtraPlus.Clear();
			Ghosts.Clear();
			RectTransform rect = iface.ProductSlotUI.Rect;
			_itemSlots = ((Il2CppObjectBase)((Transform)rect).parent).Cast<RectTransform>();
			Transform obj = ((Transform)_itemSlots).Find("Plus");
			_plus = ((obj != null) ? ((Il2CppObjectBase)obj).Cast<RectTransform>() : null);
			Transform obj2 = ((Transform)_itemSlots).Find("Arrow");
			_arrow = ((obj2 != null) ? ((Il2CppObjectBase)obj2).Cast<RectTransform>() : null);
			Transform obj3 = ((Transform)_itemSlots).Find("Shade");
			_shade = ((obj3 != null) ? ((Il2CppObjectBase)obj3).Cast<RectTransform>() : null);
			_vProduct = rect.anchoredPosition;
			_vIngredient = iface.IngredientSlotUI.Rect.anchoredPosition;
			_vPreview = iface.PreviewSlotUI.Rect.anchoredPosition;
			_vPlus = (((Object)(object)_plus != (Object)null) ? _plus.anchoredPosition : Vector2.zero);
			_vArrow = (((Object)(object)_arrow != (Object)null) ? _arrow.anchoredPosition : Vector2.zero);
			_vShade = (((Object)(object)_shade != (Object)null) ? _shade.sizeDelta : Vector2.zero);
			int num = Math.Max(0, ModConfig.MaxMixSteps - 1);
			for (int i = 0; i < num; i++)
			{
				GameObject obj4 = Object.Instantiate<GameObject>(((Component)iface.IngredientSlotUI).gameObject, (Transform)(object)_itemSlots);
				((Object)obj4).name = $"MkIII IngredientSlot {i + 2}";
				ItemSlotUI component = obj4.GetComponent<ItemSlotUI>();
				component.ClearSlot();
				Transform obj5 = obj4.transform.Find("Hint");
				if (obj5 != null)
				{
					((Component)obj5).gameObject.SetActive(false);
				}
				SetText(obj4.transform.Find("Problem"), "");
				ExtraSlotUIs.Add(component);
				if ((Object)(object)_plus != (Object)null)
				{
					RectTransform component2 = Object.Instantiate<GameObject>(((Component)_plus).gameObject, (Transform)(object)_itemSlots).GetComponent<RectTransform>();
					((Object)component2).name = $"MkIII Plus {i + 2}";
					ExtraPlus.Add(component2);
				}
			}
			foreach (ItemSlotUI item in AllInputSlotUIs(iface))
			{
				Ghost(item);
			}
			Transform parent = ((TMP_Text)iface.InstructionLabel).transform.parent;
			Transform obj6 = parent.Find("Title");
			_title = ((obj6 != null) ? ((Component)obj6).GetComponent<TextMeshProUGUI>() : null);
			if (_vanillaTitle == null)
			{
				TextMeshProUGUI title = _title;
				_vanillaTitle = ((title != null) ? ((TMP_Text)title).text : null);
			}
			RectTransform rectTransform = ((TMP_Text)iface.InstructionLabel).rectTransform;
			float num2 = rectTransform.sizeDelta.x / 2f + 30f;
			_prev = ArrowButton(iface, parent, "<", new Vector2(rectTransform.anchoredPosition.x - num2, rectTransform.anchoredPosition.y), delegate
			{
				Cycle(-1);
			});
			_next = ArrowButton(iface, parent, ">", new Vector2(rectTransform.anchoredPosition.x + num2, rectTransform.anchoredPosition.y), delegate
			{
				Cycle(1);
			});
			_built = true;
		}

		private static IEnumerable<ItemSlotUI> AllInputSlotUIs(MixingStationInterface iface)
		{
			yield return iface.ProductSlotUI;
			yield return iface.IngredientSlotUI;
			foreach (ItemSlotUI extraSlotUI in ExtraSlotUIs)
			{
				yield return extraSlotUI;
			}
		}

		private static void Ghost(ItemSlotUI ui)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: 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)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject("MkIII Ghost");
			RectTransform val2 = val.AddComponent<RectTransform>();
			((Transform)val2).SetParent((Transform)(object)ui.Rect, false);
			Vector2 val3 = default(Vector2);
			((Vector2)(ref val3))..ctor(0.5f, 0.5f);
			val2.anchorMax = val3;
			val2.anchorMin = val3;
			val2.sizeDelta = new Vector2(56f, 56f);
			val2.anchoredPosition = Vector2.zero;
			Transform val4 = ((Transform)ui.Rect).Find("Background");
			if ((Object)(object)val4 != (Object)null)
			{
				((Transform)val2).SetSiblingIndex(val4.GetSiblingIndex() + 1);
			}
			Image val5 = val.AddComponent<Image>();
			((Graphic)val5).raycastTarget = false;
			val5.preserveAspect = true;
			((Graphic)val5).color = new Color(1f, 1f, 1f, 0.28f);
			val.SetActive(false);
			Ghosts[((Il2CppObjectBase)ui).Pointer] = val5;
		}

		private static Button ArrowButton(MixingStationInterface iface, Transform parent, string label, Vector2 pos, Action onClick)
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cc: Expected O, but got Unknown
			GameObject val = Object.Instantiate<GameObject>(((Component)iface.BeginButton).gameObject, parent);
			((Object)val).name = "MkIII Recipe " + label;
			RectTransform component = val.GetComponent<RectTransform>();
			component.anchoredPosition = pos;
			component.sizeDelta = new Vector2(40f, 40f);
			foreach (Transform componentsInChild in val.GetComponentsInChildren<Transform>(true))
			{
				if (((Object)componentsInChild).name.StartsWith("EmbeddedInputPrompt"))
				{
					((Component)componentsInChild).gameObject.SetActive(false);
				}
			}
			foreach (TextMeshProUGUI componentsInChild2 in val.GetComponentsInChildren<TextMeshProUGUI>(true))
			{
				((TMP_Text)componentsInChild2).text = label;
			}
			Button component2 = val.GetComponent<Button>();
			component2.onClick = new ButtonClickedEvent();
			((UnityEvent)component2.onClick).AddListener(UnityAction.op_Implicit(onClick));
			((Selectable)component2).interactable = true;
			val.SetActive(false);
			return component2;
		}

		private static void Layout(int ingredientSlots)
		{
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0183: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_0145: Unknown result type (might be due to invalid IL or missing references)
			//IL_0125: Unknown result type (might be due to invalid IL or missing references)
			float num = 0f;
			List<float> list = new List<float> { num };
			for (int i = 0; i < ingredientSlots; i++)
			{
				num += 60f;
				list.Add(num);
				num += 60f;
				list.Add(num);
			}
			num += 99f;
			list.Add(num);
			num += 102f;
			list.Add(num);
			float num2 = (_vProduct.x + _vPreview.x) / 2f - num / 2f;
			float y = _vProduct.y;
			int num3 = 0;
			_iface.ProductSlotUI.Rect.anchoredPosition = new Vector2(list[num3++] + num2, y);
			for (int j = 0; j < ingredientSlots; j++)
			{
				RectTransform val = ((j == 0) ? _plus : ExtraPlus[j - 1]);
				RectTransform obj = ((j == 0) ? _iface.IngredientSlotUI.Rect : ExtraSlotUIs[j - 1].Rect);
				if ((Object)(object)val != (Object)null)
				{
					val.anchoredPosition = new Vector2(list[num3] + num2, _vPlus.y);
				}
				num3++;
				obj.anchoredPosition = new Vector2(list[num3++] + num2, y);
			}
			if ((Object)(object)_arrow != (Object)null)
			{
				_arrow.anchoredPosition = new Vector2(list[num3] + num2, _vArrow.y);
			}
			num3++;
			_iface.PreviewSlotUI.Rect.anchoredPosition = new Vector2(list[num3] + num2, _vPreview.y);
			if ((Object)(object)_shade != (Object)null)
			{
				_shade.sizeDelta = new Vector2(_vShade.x + (num - (_vPreview.x - _vProduct.x)), _vShade.y);
			}
			for (int k = 0; k < ExtraSlotUIs.Count; k++)
			{
				bool active = k + 1 < ingredientSlots;
				((Component)ExtraSlotUIs[k]).gameObject.SetActive(active);
				if (k < ExtraPlus.Count)
				{
					((Component)ExtraPlus[k]).gameObject.SetActive(active);
				}
			}
		}

		private static void RestoreVanilla()
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			if (!_built)
			{
				return;
			}
			_iface.ProductSlotUI.Rect.anchoredPosition = _vProduct;
			_iface.IngredientSlotUI.Rect.anchoredPosition = _vIngredient;
			_iface.PreviewSlotUI.Rect.anchoredPosition = _vPreview;
			if ((Object)(object)_plus != (Object)null)
			{
				_plus.anchoredPosition = _vPlus;
			}
			if ((Object)(object)_arrow != (Object)null)
			{
				_arrow.anchoredPosition = _vArrow;
			}
			if ((Object)(object)_shade != (Object)null)
			{
				_shade.sizeDelta = _vShade;
			}
			foreach (ItemSlotUI extraSlotUI in ExtraSlotUIs)
			{
				extraSlotUI.ClearSlot();
				((Component)extraSlotUI).gameObject.SetActive(false);
			}
			foreach (RectTransform extraPlu in ExtraPlus)
			{
				((Component)extraPlu).gameObject.SetActive(false);
			}
			foreach (Image value in Ghosts.Values)
			{
				((Component)value).gameObject.SetActive(false);
			}
			Button prev = _prev;
			if (prev != null)
			{
				((Component)prev).gameObject.SetActive(false);
			}
			Button next = _next;
			if (next != null)
			{
				((Component)next).gameObject.SetActive(false);
			}
			if ((Object)(object)_title != (Object)null && _vanillaTitle != null)
			{
				((TMP_Text)_title).text = _vanillaTitle;
			}
			SetText(((Transform)_iface.IngredientSlotUI.Rect).Find("Label"), "Mixer");
			SetText(((Transform)_iface.ProductSlotUI.Rect).Find("Label"), "Product");
		}

		private static bool PrefixMatches(List<string> loaded, List<string> recipe)
		{
			if (loaded.Count > recipe.Count)
			{
				return false;
			}
			for (int i = 0; i < loaded.Count; i++)
			{
				if (loaded[i] != recipe[i])
				{
					return false;
				}
			}
			return true;
		}

		private static Preview ComputePreview()
		{
			Machine m = _machine;
			Preview preview = new Preview();
			if (Time.unscaledTime - _catalogAt > 2f)
			{
				_catalog = RecipeCatalog.Build();
				_catalogAt = Time.unscaledTime;
			}
			(string, List<string>) tuple = m.LoadedSequence();
			string baseId = tuple.Item1;
			List<string> mixers = tuple.Item2;
			preview.LoadedMixers = mixers.Count;
			preview.Filtered = baseId != null || mixers.Count > 0;
			preview.Candidates = _catalog.Where((CatalogEntry e) => (baseId == null || e.Chain.BaseId == baseId) && PrefixMatches(mixers, e.Chain.Mixers)).ToList();
			if (preview.Candidates.Count > 0)
			{
				preview.Selected = preview.Candidates.FirstOrDefault((CatalogEntry e) => e.Chain.Signature == _selected) ?? preview.Candidates.FirstOrDefault((CatalogEntry e) => e.Chain.Signature == m.Recipe?.Signature) ?? preview.Candidates.FirstOrDefault((CatalogEntry e) => e.Available) ?? preview.Candidates[0];
				_selected = preview.Selected.Chain.Signature;
				preview.Chain = preview.Selected.Chain;
				return preview;
			}
			if (!preview.Filtered)
			{
				return preview;
			}
			preview.Chain = new RecipeChain
			{
				BaseId = baseId,
				Mixers = mixers
			};
			if (baseId == null || mixers.Count == 0)
			{
				preview.IsNewMix = true;
				return preview;
			}
			string text = baseId;
			foreach (string item in mixers)
			{
				ProductDefinition val = RecipeCatalog.MixOnce(text, item);
				if ((Object)(object)val == (Object)null)
				{
					preview.IsNewMix = true;
					return preview;
				}
				text = ((BaseItemDefinition)val).ID;
			}
			preview.KnownResultId = text;
			preview.Chain.TargetId = text;
			return preview;
		}

		private static void Cycle(int dir)
		{
			if (_machine != null && !_machine.Running)
			{
				Preview preview = ComputePreview();
				if (preview.Candidates.Count >= 2)
				{
					int num = preview.Candidates.IndexOf(preview.Selected);
					num = ((num + dir) % preview.Candidates.Count + preview.Candidates.Count) % preview.Candidates.Count;
					_selected = preview.Candidates[num].Chain.Signature;
					Refresh();
				}
			}
		}

		public static void Refresh()
		{
			if (_machine == null || (Object)(object)_iface == (Object)null)
			{
				return;
			}
			try
			{
				Machine machine = _machine;
				MixOperation currentMixOperation = machine.Station.CurrentMixOperation;
				if (machine.AwaitingName && currentMixOperation != null && ((Il2CppObjectBase)currentMixOperation).Pointer != _namingAskedFor)
				{
					NewMixScreen instance = Singleton<NewMixScreen>.Instance;
					if ((Object)(object)instance != (Object)null && !instance.IsOpen)
					{
						_namingAskedFor = ((Il2CppObjectBase)currentMixOperation).Pointer;
						_iface.CheckForUnknownMix();
					}
				}
				Preview preview = ComputePreview();
				RecipeChain recipeChain = (machine.Running ? machine.Recipe : preview.Chain);
				bool flag = (machine.Running ? (machine.Recipe?.TargetId == null) : preview.IsNewMix);
				int maxMixSteps = ModConfig.MaxMixSteps;
				int num = -1;
				for (int i = 0; i < machine.IngredientSlotCount; i++)
				{
					if (Machine.Has(machine