Decompiled source of RecipeManager v0.12.2

plugins/RecipeManager.dll

Decompiled 6 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Serialization;
using System.Runtime.Versioning;
using System.Security.Permissions;
using System.Text;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Jotunn;
using Jotunn.Configs;
using Jotunn.Entities;
using Jotunn.Managers;
using Jotunn.Utils;
using RecipeManager.Common;
using RecipeManager.Importer;
using RecipeManager.Patches;
using UnityEngine;
using YamlDotNet.Core;
using YamlDotNet.RepresentationModel;
using YamlDotNet.Serialization;
using YamlDotNet.Serialization.NamingConventions;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("RecipeManager")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("RecipeManager")]
[assembly: AssemblyCopyright("Copyright ©  2021")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("e3243d22-4307-4008-ba36-9f326008cde5")]
[assembly: AssemblyFileVersion("0.12.2")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.12.2.0")]
namespace RecipeManager
{
	internal class ConversionPrintCommand : ConsoleCommand
	{
		internal const string DumpFileName = "AllConversionsDebug.yaml";

		public override string Name => "RM_Conversion_PrintAll";

		public override string Help => "Prints all conversions.";

		public override bool IsCheat => true;

		public override void Run(string[] args)
		{
			if (ValConfig.EnableDebugMode.Value)
			{
				Logger.LogInfo("Starting to dump conversion list");
			}
			string text = Path.Combine(Paths.ConfigPath, "RecipeManager", "AllConversionsDebug.yaml");
			DataObjects.ConversionModificationCollection conversionModificationCollection = new DataObjects.ConversionModificationCollection();
			List<Smelter> list = (from pc in Resources.FindObjectsOfTypeAll<Smelter>()
				where IsPrefab((Component)(object)pc)
				select pc).ToList();
			List<Fermenter> list2 = (from pc in Resources.FindObjectsOfTypeAll<Fermenter>()
				where IsPrefab((Component)(object)pc)
				select pc).ToList();
			List<CookingStation> list3 = (from pc in Resources.FindObjectsOfTypeAll<CookingStation>()
				where IsPrefab((Component)(object)pc)
				select pc).ToList();
			List<Incinerator> list4 = (from pc in Resources.FindObjectsOfTypeAll<Incinerator>()
				where IsPrefab((Component)(object)pc)
				select pc).ToList();
			foreach (Smelter item in list)
			{
				string text2 = PrefabName((Component)(object)item);
				if (ValConfig.EnableDebugMode.Value)
				{
					Logger.LogInfo("Building Smelter definition " + text2);
				}
				DataObjects.ConversionModification conversionModification = new DataObjects.ConversionModification
				{
					prefab = text2,
					action = DataObjects.ConversionAction.Modify,
					fuelPerProduct = item.m_fuelPerProduct,
					maxFuel = item.m_maxFuel,
					maxOres = item.m_maxOre,
					fuelItem = ItemName(item.m_fuelItem),
					conversionTime = item.m_secPerProduct,
					spawnStack = item.m_spawnStack,
					requiresRoof = item.m_requiresRoof,
					conversions = new List<DataObjects.ConversionDef>()
				};
				foreach (ItemConversion item2 in item.m_conversion ?? new List<ItemConversion>())
				{
					if (item2 != null)
					{
						int num = ConversionUpdater.SmelterAmount(item, item2);
						conversionModification.conversions.Add(new DataObjects.ConversionDef
						{
							fromPrefab = ItemName(item2.m_from),
							toPrefab = ItemName(item2.m_to),
							amount = ((num > 1) ? new int?(num) : ((int?)null))
						});
					}
				}
				AddDefinition(conversionModificationCollection, conversionModification);
			}
			foreach (Fermenter item3 in list2)
			{
				if (ValConfig.EnableDebugMode.Value)
				{
					Logger.LogInfo("Building Fermenter definition " + item3.m_name);
				}
				DataObjects.ConversionModification conversionModification2 = new DataObjects.ConversionModification
				{
					prefab = PrefabName((Component)(object)item3),
					action = DataObjects.ConversionAction.Modify,
					conversionTime = item3.m_fermentationDuration,
					conversions = new List<DataObjects.ConversionDef>()
				};
				foreach (ItemConversion item4 in item3.m_conversion ?? new List<ItemConversion>())
				{
					if (item4 != null)
					{
						conversionModification2.conversions.Add(new DataObjects.ConversionDef
						{
							fromPrefab = ItemName(item4.m_from),
							toPrefab = ItemName(item4.m_to),
							amount = item4.m_producedItems
						});
					}
				}
				AddDefinition(conversionModificationCollection, conversionModification2);
			}
			foreach (CookingStation item5 in list3)
			{
				if (ValConfig.EnableDebugMode.Value)
				{
					Logger.LogInfo("Building Cooking Station definition " + item5.m_name);
				}
				DataObjects.ConversionModification conversionModification3 = new DataObjects.ConversionModification
				{
					prefab = PrefabName((Component)(object)item5),
					action = DataObjects.ConversionAction.Modify,
					fuelItem = ItemName(item5.m_fuelItem),
					overCookedItem = ItemName(item5.m_overCookedItem),
					secPerFuel = item5.m_secPerFuel,
					maxFuel = item5.m_maxFuel,
					requiresFire = item5.m_requireFire,
					conversions = new List<DataObjects.ConversionDef>()
				};
				foreach (ItemConversion item6 in item5.m_conversion ?? new List<ItemConversion>())
				{
					if (item6 != null)
					{
						conversionModification3.conversions.Add(new DataObjects.ConversionDef
						{
							fromPrefab = ItemName(item6.m_from),
							toPrefab = ItemName(item6.m_to),
							cookTime = item6.m_cookTime
						});
					}
				}
				AddDefinition(conversionModificationCollection, conversionModification3);
			}
			foreach (Incinerator item7 in list4)
			{
				DataObjects.ConversionModification conversionModification4 = new DataObjects.ConversionModification
				{
					prefab = PrefabName((Component)(object)item7),
					action = DataObjects.ConversionAction.Modify,
					defaultResult = ItemName(item7.m_defaultResult),
					defaultCost = item7.m_defaultCost,
					incineratorConversions = new List<DataObjects.IncineratorConversionDef>()
				};
				foreach (IncineratorConversion item8 in item7.m_conversions ?? new List<IncineratorConversion>())
				{
					if (item8 != null)
					{
						conversionModification4.incineratorConversions.Add(new DataObjects.IncineratorConversionDef
						{
							result = ItemName(item8.m_result),
							resultAmount = item8.m_resultAmount,
							priority = item8.m_priority,
							anyOneIngredient = item8.m_requireOnlyOneIngredient,
							ingredients = (from requirement in item8.m_requirements ?? new List<Requirement>()
								where requirement != null && (Object)(object)requirement.m_resItem != (Object)null
								select new DataObjects.IncineratorIngredientDef
								{
									prefab = ItemName(requirement.m_resItem),
									amount = requirement.m_amount
								}).ToList()
						});
					}
				}
				AddDefinition(conversionModificationCollection, conversionModification4);
			}
			if (ValConfig.EnableDebugMode.Value)
			{
				Logger.LogInfo("Serializing and printing conversions.");
			}
			string value = ValConfig.yamlserializer.Serialize((object)conversionModificationCollection);
			using (StreamWriter streamWriter = new StreamWriter(text))
			{
				streamWriter.WriteLine("# Reference dump of every conversion station, as it currently is.");
				streamWriter.WriteLine("# This file is NOT loaded by the mod, copy the entries you want to change into your own Conversions.yaml.");
				streamWriter.WriteLine();
				streamWriter.WriteLine(value);
			}
			ItemCommandUtils.Respond($"Dumped {conversionModificationCollection.ConversionModifications.Count} station(s) to {text}");
		}

		private static void AddDefinition(DataObjects.ConversionModificationCollection collection, DataObjects.ConversionModification convMod)
		{
			if (collection.ConversionModifications.ContainsKey(convMod.prefab))
			{
				Logger.LogDebug(convMod.prefab + " was already dumped, skipping its second station.");
			}
			else
			{
				collection.ConversionModifications.Add(convMod.prefab, convMod);
			}
		}

		private static string ItemName(ItemDrop item)
		{
			if (!((Object)(object)item != (Object)null))
			{
				return null;
			}
			return ((Object)((Component)item).gameObject).name;
		}

		private static string PrefabName(Component station)
		{
			return ((Object)ConversionUpdater.StationRoot(station)).name;
		}

		private static bool IsPrefab(Component station)
		{
			string text = PrefabName(station);
			if (!text.EndsWith("(Clone)"))
			{
				return !Regex.IsMatch(text.Trim(), "\\(\\d+\\)");
			}
			return false;
		}
	}
	internal class ConversionReloadCommand : ConsoleCommand
	{
		public override string Name => "RM_Conversion_Reload";

		public override string Help => "Resynchronizes conversion modifications.";

		public override bool IsCheat => true;

		public override void Run(string[] args)
		{
			ValConfig.Conversions.ReloadFromDisk();
		}
	}
	internal class ConversionUpdater
	{
		private class ConversionSnapshot
		{
			private GameObject gameObject;

			private bool hasSmelter;

			private List<ItemConversion> smelterConversions;

			private ItemDrop smelterFuelItem;

			private int smelterFuelPerProduct;

			private float smelterSecPerProduct;

			private int smelterMaxOre;

			private int smelterMaxFuel;

			private bool smelterSpawnStack;

			private bool smelterRequiresRoof;

			private bool hasFermenter;

			private List<ItemConversion> fermenterConversions;

			private float fermentationDuration;

			private bool hasCookingStation;

			private List<ItemConversion> cookingConversions;

			private ItemDrop cookingFuelItem;

			private ItemDrop cookingOverCookedItem;

			private int cookingMaxFuel;

			private bool cookingUseFuel;

			private bool cookingRequireFire;

			private int cookingSecPerFuel;

			private bool hasIncinerator;

			private List<IncineratorConversion> incineratorConversions;

			private ItemDrop incineratorDefaultResult;

			private int incineratorDefaultCost;

			public bool IsEmpty
			{
				get
				{
					if (!hasSmelter && !hasFermenter && !hasCookingStation)
					{
						return !hasIncinerator;
					}
					return false;
				}
			}

			public static ConversionSnapshot From(GameObject go)
			{
				ConversionSnapshot conversionSnapshot = new ConversionSnapshot
				{
					gameObject = go
				};
				Smelter val = ConversionUpdater.FindStation<Smelter>(go);
				if ((Object)(object)val != (Object)null)
				{
					conversionSnapshot.hasSmelter = true;
					conversionSnapshot.smelterConversions = Copy(val.m_conversion);
					conversionSnapshot.smelterFuelItem = val.m_fuelItem;
					conversionSnapshot.smelterFuelPerProduct = val.m_fuelPerProduct;
					conversionSnapshot.smelterSecPerProduct = val.m_secPerProduct;
					conversionSnapshot.smelterMaxOre = val.m_maxOre;
					conversionSnapshot.smelterMaxFuel = val.m_maxFuel;
					conversionSnapshot.smelterSpawnStack = val.m_spawnStack;
					conversionSnapshot.smelterRequiresRoof = val.m_requiresRoof;
				}
				Fermenter val2 = ConversionUpdater.FindStation<Fermenter>(go);
				if ((Object)(object)val2 != (Object)null)
				{
					conversionSnapshot.hasFermenter = true;
					conversionSnapshot.fermenterConversions = Copy(val2.m_conversion);
					conversionSnapshot.fermentationDuration = val2.m_fermentationDuration;
				}
				CookingStation val3 = ConversionUpdater.FindStation<CookingStation>(go);
				if ((Object)(object)val3 != (Object)null)
				{
					conversionSnapshot.hasCookingStation = true;
					conversionSnapshot.cookingConversions = Copy(val3.m_conversion);
					conversionSnapshot.cookingFuelItem = val3.m_fuelItem;
					conversionSnapshot.cookingOverCookedItem = val3.m_overCookedItem;
					conversionSnapshot.cookingMaxFuel = val3.m_maxFuel;
					conversionSnapshot.cookingUseFuel = val3.m_useFuel;
					conversionSnapshot.cookingRequireFire = val3.m_requireFire;
					conversionSnapshot.cookingSecPerFuel = val3.m_secPerFuel;
				}
				Incinerator val4 = ConversionUpdater.FindStation<Incinerator>(go);
				if ((Object)(object)val4 != (Object)null)
				{
					conversionSnapshot.hasIncinerator = true;
					conversionSnapshot.incineratorConversions = Copy(val4.m_conversions);
					conversionSnapshot.incineratorDefaultResult = val4.m_defaultResult;
					conversionSnapshot.incineratorDefaultCost = val4.m_defaultCost;
				}
				return conversionSnapshot;
			}

			public ConversionSnapshot For(GameObject go)
			{
				ConversionSnapshot obj = (ConversionSnapshot)MemberwiseClone();
				obj.gameObject = go;
				obj.smelterConversions = Copy(smelterConversions);
				obj.fermenterConversions = Copy(fermenterConversions);
				obj.cookingConversions = Copy(cookingConversions);
				obj.incineratorConversions = Copy(incineratorConversions);
				return obj;
			}

			public bool WriteBack()
			{
				if ((Object)(object)gameObject == (Object)null)
				{
					return false;
				}
				if (hasSmelter)
				{
					Smelter val = ConversionUpdater.FindStation<Smelter>(gameObject);
					if ((Object)(object)val != (Object)null)
					{
						val.m_conversion = Copy(smelterConversions);
						val.m_fuelItem = smelterFuelItem;
						val.m_fuelPerProduct = smelterFuelPerProduct;
						val.m_secPerProduct = smelterSecPerProduct;
						val.m_maxOre = smelterMaxOre;
						val.m_maxFuel = smelterMaxFuel;
						val.m_spawnStack = smelterSpawnStack;
						val.m_requiresRoof = smelterRequiresRoof;
					}
				}
				if (hasFermenter)
				{
					Fermenter val2 = ConversionUpdater.FindStation<Fermenter>(gameObject);
					if ((Object)(object)val2 != (Object)null)
					{
						val2.m_conversion = Copy(fermenterConversions);
						val2.m_fermentationDuration = fermentationDuration;
					}
				}
				if (hasCookingStation)
				{
					CookingStation val3 = ConversionUpdater.FindStation<CookingStation>(gameObject);
					if ((Object)(object)val3 != (Object)null)
					{
						val3.m_conversion = Copy(cookingConversions);
						val3.m_fuelItem = cookingFuelItem;
						val3.m_overCookedItem = cookingOverCookedItem;
						val3.m_maxFuel = cookingMaxFuel;
						val3.m_useFuel = cookingUseFuel;
						val3.m_requireFire = cookingRequireFire;
						val3.m_secPerFuel = cookingSecPerFuel;
					}
				}
				if (hasIncinerator)
				{
					Incinerator val4 = ConversionUpdater.FindStation<Incinerator>(gameObject);
					if ((Object)(object)val4 != (Object)null)
					{
						val4.m_conversions = ByPriority(gameObject, Copy(incineratorConversions));
						val4.m_defaultResult = incineratorDefaultResult;
						val4.m_defaultCost = incineratorDefaultCost;
					}
				}
				return true;
			}

			private static List<T> Copy<T>(List<T> source)
			{
				if (source != null)
				{
					return new List<T>(source);
				}
				return null;
			}
		}

		internal const int DefaultFermenterAmount = 4;

		internal const float DefaultCookTime = 10f;

		public static Dictionary<string, DataObjects.ConversionModification> ConversionsToModify = new Dictionary<string, DataObjects.ConversionModification>();

		public static List<DataObjects.TrackedConversion> TrackedConversions = new List<DataObjects.TrackedConversion>();

		private static readonly Dictionary<int, ConversionSnapshot> OriginalConversions = new Dictionary<int, ConversionSnapshot>();

		private static readonly Dictionary<string, ConversionSnapshot> OriginalByPrefab = new Dictionary<string, ConversionSnapshot>();

		private static readonly Dictionary<string, Dictionary<string, int>> SmelterAmounts = new Dictionary<string, Dictionary<string, int>>();

		private static bool warnedOverCookedTime;

		public static void InitialSychronization()
		{
			RevertConversionModifications();
			BuildConversionTracker();
			ConversionUpdateRunner();
		}

		public static void Reapply()
		{
			RevertConversionModifications();
			BuildConversionTracker();
			ConversionUpdateRunner();
		}

		public static void ConversionUpdateRunner()
		{
			Logger.LogInfo($"Applying {TrackedConversions.Count} conversion modifications");
			if (TrackedConversions.Count == 0)
			{
				return;
			}
			Dictionary<string, List<GameObject>> stations = IndexStations();
			foreach (DataObjects.TrackedConversion trackedConversion in TrackedConversions)
			{
				ApplyConversionModifications(trackedConversion, stations);
			}
		}

		private static void ApplyConversionModifications(DataObjects.TrackedConversion conv, Dictionary<string, List<GameObject>> stations)
		{
			if (conv == null || (Object)(object)conv.prefab == (Object)null)
			{
				Logger.LogWarning("A tracked conversion has no target prefab, this modification will be skipped.");
				return;
			}
			Logger.LogDebug($"Applying conversion ({((Object)conv.prefab).name}) modification action: {conv.action}");
			if (!stations.TryGetValue(((Object)conv.prefab).name, out var value))
			{
				Logger.LogWarning("No stations found for conversion " + ((Object)conv.prefab).name + ", skipping modification.");
				return;
			}
			foreach (GameObject item in value)
			{
				if (!((Object)(object)item == (Object)null))
				{
					switch (conv.action)
					{
					case DataObjects.ConversionAction.Modify:
						ModifyConversion(conv, item);
						break;
					case DataObjects.ConversionAction.Add:
						AddConversion(conv, item);
						break;
					case DataObjects.ConversionAction.Remove:
						RemoveConversion(conv, item);
						break;
					}
				}
			}
			if (conv.convertType == DataObjects.ConversionType.Smelter)
			{
				UpdateSmelterAmounts(conv);
			}
		}

		private static void UpdateSmelterAmounts(DataObjects.TrackedConversion conv)
		{
			if (conv.conversions == null || (Object)(object)conv.smelterComponent == (Object)null)
			{
				return;
			}
			string name = ((Object)conv.prefab).name;
			Dictionary<string, int> value;
			Dictionary<string, int> dictionary = (SmelterAmounts.TryGetValue(name, out value) ? value : new Dictionary<string, int>());
			Dictionary<string, int> dictionary2 = new Dictionary<string, int>(dictionary);
			if (conv.action == DataObjects.ConversionAction.Modify)
			{
				dictionary2.Clear();
				foreach (DataObjects.ResolvedConversion conversion in conv.conversions)
				{
					string key = InputName(conversion.from);
					if (!dictionary2.ContainsKey(key))
					{
						dictionary2[key] = conversion.amount ?? ((!dictionary.TryGetValue(key, out var value2)) ? 1 : value2);
					}
				}
			}
			else if (conv.action == DataObjects.ConversionAction.Add)
			{
				foreach (DataObjects.ResolvedConversion conversion2 in conv.conversions)
				{
					if (conversion2.amount.HasValue)
					{
						dictionary2[InputName(conversion2.from)] = conversion2.amount.Value;
					}
				}
			}
			List<ItemConversion> list = conv.smelterComponent.m_conversion ?? new List<ItemConversion>();
			foreach (string input in dictionary2.Keys.ToList())
			{
				if (dictionary2[input] <= 1 || !list.Exists((ItemConversion e) => e != null && InputName(e.m_from) == input))
				{
					dictionary2.Remove(input);
				}
			}
			if (dictionary2.Count > 0)
			{
				SmelterAmounts[name] = dictionary2;
			}
			else
			{
				SmelterAmounts.Remove(name);
			}
		}

		internal static int SmelterAmount(Smelter station, ItemConversion conversion)
		{
			if (SmelterAmounts.Count == 0 || conversion == null)
			{
				return 1;
			}
			if (!SmelterAmounts.TryGetValue(StationRef.PrefabNameOf(StationRoot((Component)(object)station)), out var value))
			{
				return 1;
			}
			if (!value.TryGetValue(InputName(conversion.m_from), out var value2))
			{
				return 1;
			}
			return value2;
		}

		private static string InputName(ItemDrop input)
		{
			if (!((Object)(object)input != (Object)null))
			{
				return "";
			}
			return ((Object)((Component)input).gameObject).name;
		}

		internal static Dictionary<string, List<GameObject>> IndexStations()
		{
			Dictionary<string, List<GameObject>> dictionary = new Dictionary<string, List<GameObject>>();
			HashSet<int> seen = new HashSet<int>();
			AddStations<Smelter>(dictionary, seen);
			AddStations<Fermenter>(dictionary, seen);
			AddStations<CookingStation>(dictionary, seen);
			AddStations<Incinerator>(dictionary, seen);
			return dictionary;
		}

		private static void AddStations<T>(Dictionary<string, List<GameObject>> index, HashSet<int> seen) where T : Component
		{
			T[] array = Resources.FindObjectsOfTypeAll<T>();
			foreach (T val in array)
			{
				if ((Object)(object)val == (Object)null)
				{
					continue;
				}
				GameObject val2 = StationRoot((Component)(object)val);
				if (seen.Add(((Object)val2).GetInstanceID()))
				{
					string key = StationRef.PrefabNameOf(val2);
					if (!index.TryGetValue(key, out var value))
					{
						value = new List<GameObject>();
						index.Add(key, value);
					}
					if (IsPlaced(val2))
					{
						value.Add(val2);
					}
					else
					{
						value.Insert(0, val2);
					}
				}
			}
		}

		private static bool IsPlaced(GameObject go)
		{
			return ((Object)go).name.EndsWith("(Clone)", StringComparison.Ordinal);
		}

		internal static GameObject StationRoot(Component station)
		{
			ZNetView componentInParent = station.GetComponentInParent<ZNetView>(true);
			if (!((Object)(object)componentInParent != (Object)null))
			{
				return station.gameObject;
			}
			return ((Component)componentInParent).gameObject;
		}

		public static void RevertConversionModifications()
		{
			SmelterAmounts.Clear();
			if (OriginalConversions.Count == 0)
			{
				return;
			}
			Logger.LogInfo($"Reverting {OriginalConversions.Count} conversion modifications");
			List<int> list = new List<int>();
			int num = 0;
			foreach (KeyValuePair<int, ConversionSnapshot> originalConversion in OriginalConversions)
			{
				if (originalConversion.Value.WriteBack())
				{
					num++;
				}
				else
				{
					list.Add(originalConversion.Key);
				}
			}
			foreach (int item in list)
			{
				OriginalConversions.Remove(item);
			}
			Logger.LogDebug($"Restored {num} conversion station(s), {list.Count} no longer exist.");
		}

		internal static T FindStation<T>(GameObject go) where T : Component
		{
			return go.GetComponentInChildren<T>(true);
		}

		private static void CaptureOriginal(GameObject go)
		{
			if ((Object)(object)go == (Object)null)
			{
				return;
			}
			int instanceID = ((Object)go).GetInstanceID();
			if (OriginalConversions.ContainsKey(instanceID))
			{
				return;
			}
			string key = StationRef.PrefabNameOf(go);
			bool flag = IsPlaced(go);
			ConversionSnapshot value;
			ConversionSnapshot conversionSnapshot = ((flag && OriginalByPrefab.TryGetValue(key, out value)) ? value.For(go) : ConversionSnapshot.From(go));
			if (!conversionSnapshot.IsEmpty)
			{
				OriginalConversions.Add(instanceID, conversionSnapshot);
				if (!flag && !OriginalByPrefab.ContainsKey(key))
				{
					OriginalByPrefab.Add(key, conversionSnapshot);
				}
			}
		}

		private static void ModifyConversion(DataObjects.TrackedConversion conversion, GameObject go)
		{
			CaptureOriginal(go);
			switch (conversion.convertType)
			{
			case DataObjects.ConversionType.Smelter:
			{
				Smelter val4 = ConversionUpdater.FindStation<Smelter>(go);
				if ((Object)(object)val4 == (Object)null)
				{
					Logger.LogInfo("Smelter component not found on " + ((Object)go).name + ", cannot apply conversion modifications.");
					break;
				}
				if (conversion.conversions != null)
				{
					val4.m_conversion = ((IEnumerable<DataObjects.ResolvedConversion>)conversion.conversions).Select((Func<DataObjects.ResolvedConversion, ItemConversion>)((DataObjects.ResolvedConversion c) => new ItemConversion
					{
						m_from = c.from,
						m_to = c.to
					})).ToList();
				}
				if ((Object)(object)conversion.fuelItem != (Object)null)
				{
					val4.m_fuelItem = conversion.fuelItem;
				}
				if (conversion.fuelPerProduct.HasValue)
				{
					val4.m_fuelPerProduct = conversion.fuelPerProduct.Value;
				}
				if (conversion.conversionTime.HasValue)
				{
					val4.m_secPerProduct = conversion.conversionTime.Value;
				}
				if (conversion.maxOres.HasValue)
				{
					val4.m_maxOre = conversion.maxOres.Value;
				}
				if (conversion.maxFuel.HasValue)
				{
					val4.m_maxFuel = conversion.maxFuel.Value;
				}
				if (conversion.spawnStack.HasValue)
				{
					val4.m_spawnStack = conversion.spawnStack.Value;
				}
				if (conversion.requiresRoof.HasValue)
				{
					val4.m_requiresRoof = conversion.requiresRoof.Value;
				}
				break;
			}
			case DataObjects.ConversionType.Fermenter:
			{
				Fermenter val2 = ConversionUpdater.FindStation<Fermenter>(go);
				if ((Object)(object)val2 == (Object)null)
				{
					Logger.LogInfo("Fermenter component not found on " + ((Object)go).name + ", cannot apply conversion modifications.");
					break;
				}
				if (conversion.conversions != null)
				{
					List<ItemConversion> current = val2.m_conversion ?? new List<ItemConversion>();
					val2.m_conversion = ((IEnumerable<DataObjects.ResolvedConversion>)conversion.conversions).Select((Func<DataObjects.ResolvedConversion, ItemConversion>)((DataObjects.ResolvedConversion c) => new ItemConversion
					{
						m_from = c.from,
						m_to = c.to,
						m_producedItems = (c.amount ?? current.Find((ItemConversion e) => e != null && SameItem(e.m_from, c.from))?.m_producedItems ?? 4)
					})).ToList();
				}
				if (conversion.conversionTime.HasValue)
				{
					val2.m_fermentationDuration = conversion.conversionTime.Value;
				}
				break;
			}
			case DataObjects.ConversionType.CookingStation:
			{
				CookingStation val3 = ConversionUpdater.FindStation<CookingStation>(go);
				if ((Object)(object)val3 == (Object)null)
				{
					Logger.LogInfo("CookingStation component not found on " + ((Object)go).name + ", cannot apply conversion modifications.");
					break;
				}
				if (conversion.conversions != null)
				{
					List<ItemConversion> current2 = val3.m_conversion ?? new List<ItemConversion>();
					val3.m_conversion = ((IEnumerable<DataObjects.ResolvedConversion>)conversion.conversions).Select((Func<DataObjects.ResolvedConversion, ItemConversion>)((DataObjects.ResolvedConversion c) => new ItemConversion
					{
						m_from = c.from,
						m_to = c.to,
						m_cookTime = (c.cookTime ?? current2.Find((ItemConversion e) => e != null && SameItem(e.m_from, c.from))?.m_cookTime ?? 10f)
					})).ToList();
				}
				if (conversion.maxFuel.HasValue)
				{
					val3.m_maxFuel = conversion.maxFuel.Value;
				}
				if ((Object)(object)conversion.fuelItem != (Object)null)
				{
					val3.m_fuelItem = conversion.fuelItem;
				}
				if ((Object)(object)conversion.overCookedItem != (Object)null)
				{
					val3.m_overCookedItem = conversion.overCookedItem;
				}
				if (conversion.requiresFire.HasValue)
				{
					val3.m_requireFire = conversion.requiresFire.Value;
				}
				if (conversion.secPerFuel.HasValue)
				{
					val3.m_secPerFuel = (int)conversion.secPerFuel.Value;
				}
				break;
			}
			case DataObjects.ConversionType.Incinerator:
			{
				Incinerator val = ConversionUpdater.FindStation<Incinerator>(go);
				if ((Object)(object)val == (Object)null)
				{
					Logger.LogInfo("Incinerator component not found on " + ((Object)go).name + ", cannot apply conversion modifications.");
					break;
				}
				if (conversion.incineratorConversions != null)
				{
					val.m_conversions = ByPriority(go, conversion.incineratorConversions.Select(((IncineratorConversion built, DataObjects.IncineratorConversionDef def) c) => NewIncineratorConversion(c.built, null)).ToList());
				}
				if ((Object)(object)conversion.defaultResult != (Object)null)
				{
					val.m_defaultResult = conversion.defaultResult;
				}
				if (conversion.defaultCost.HasValue)
				{
					val.m_defaultCost = conversion.defaultCost.Value;
				}
				break;
			}
			}
		}

		private static List<IncineratorConversion> ByPriority(GameObject go, List<IncineratorConversion> conversions)
		{
			if (conversions != null && (Object)(object)go != (Object)null && IsPlaced(go))
			{
				conversions.Sort((IncineratorConversion a, IncineratorConversion b) => (b?.m_priority ?? 0).CompareTo(a?.m_priority ?? 0));
			}
			return conversions;
		}

		private static IncineratorConversion NewIncineratorConversion(IncineratorConversion built, IncineratorConversion existing)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Expected O, but got Unknown
			return new IncineratorConversion
			{
				m_result = built.m_result,
				m_resultAmount = built.m_resultAmount,
				m_priority = built.m_priority,
				m_requireOnlyOneIngredient = built.m_requireOnlyOneIngredient,
				m_requirements = (built.m_requirements ?? ((existing?.m_requirements != null) ? new List<Requirement>(existing.m_requirements) : new List<Requirement>()))
			};
		}

		private static bool SameItem(ItemDrop a, ItemDrop b)
		{
			if ((Object)(object)a == (Object)null || (Object)(object)b == (Object)null)
			{
				if ((Object)(object)a == (Object)null)
				{
					return (Object)(object)b == (Object)null;
				}
				return false;
			}
			return ((Object)((Component)a).gameObject).name == ((Object)((Component)b).gameObject).name;
		}

		public static void AddConversion(DataObjects.TrackedConversion conversion, GameObject go)
		{
			if (conversion.convertType == DataObjects.ConversionType.Incinerator)
			{
				if (conversion.incineratorConversions != null)
				{
					CaptureOriginal(go);
					AddIncineratorConversions(conversion, go);
				}
			}
			else
			{
				if (conversion.conversions == null)
				{
					return;
				}
				CaptureOriginal(go);
				switch (conversion.convertType)
				{
				case DataObjects.ConversionType.Smelter:
				{
					Smelter val2 = ConversionUpdater.FindStation<Smelter>(go);
					if ((Object)(object)val2 == (Object)null)
					{
						Logger.LogWarning("Smelter component not found on " + ((Object)go).name + ", no conversions can be added to it.");
						break;
					}
					val2.m_conversion = Upsert<ItemConversion>(val2.m_conversion, conversion.conversions, emptyOutputAllowed: true, (ItemConversion e) => e.m_from, (ItemConversion e, DataObjects.ResolvedConversion c) => new ItemConversion
					{
						m_from = e.m_from,
						m_to = (c.toGiven ? c.to : e.m_to)
					}, (DataObjects.ResolvedConversion c) => new ItemConversion
					{
						m_from = c.from,
						m_to = c.to
					});
					break;
				}
				case DataObjects.ConversionType.Fermenter:
				{
					Fermenter val3 = ConversionUpdater.FindStation<Fermenter>(go);
					if ((Object)(object)val3 == (Object)null)
					{
						Logger.LogWarning("Fermenter component not found on " + ((Object)go).name + ", no conversions can be added to it.");
						break;
					}
					val3.m_conversion = Upsert<ItemConversion>(val3.m_conversion, conversion.conversions, emptyOutputAllowed: false, (ItemConversion e) => e.m_from, (ItemConversion e, DataObjects.ResolvedConversion c) => new ItemConversion
					{
						m_from = e.m_from,
						m_to = (c.toGiven ? c.to : e.m_to),
						m_producedItems = (c.amount ?? e.m_producedItems)
					}, (DataObjects.ResolvedConversion c) => new ItemConversion
					{
						m_from = c.from,
						m_to = c.to,
						m_producedItems = (c.amount ?? 4)
					});
					break;
				}
				case DataObjects.ConversionType.CookingStation:
				{
					CookingStation val = ConversionUpdater.FindStation<CookingStation>(go);
					if ((Object)(object)val == (Object)null)
					{
						Logger.LogWarning("CookingStation component not found on " + ((Object)go).name + ", no conversions can be added to it.");
						break;
					}
					val.m_conversion = Upsert<ItemConversion>(val.m_conversion, conversion.conversions, emptyOutputAllowed: false, (ItemConversion e) => e.m_from, (ItemConversion e, DataObjects.ResolvedConversion c) => new ItemConversion
					{
						m_from = e.m_from,
						m_to = (c.toGiven ? c.to : e.m_to),
						m_cookTime = (c.cookTime ?? e.m_cookTime)
					}, (DataObjects.ResolvedConversion c) => new ItemConversion
					{
						m_from = c.from,
						m_to = c.to,
						m_cookTime = (c.cookTime ?? 10f)
					});
					break;
				}
				}
			}
		}

		private static void AddIncineratorConversions(DataObjects.TrackedConversion conversion, GameObject go)
		{
			Incinerator val = ConversionUpdater.FindStation<Incinerator>(go);
			if ((Object)(object)val == (Object)null)
			{
				Logger.LogWarning("Incinerator component not found on " + ((Object)go).name + ", no conversions can be added to it.");
				return;
			}
			List<IncineratorConversion> list = ((val.m_conversions == null) ? new List<IncineratorConversion>() : new List<IncineratorConversion>(val.m_conversions));
			foreach (var incineratorConversion in conversion.incineratorConversions)
			{
				IncineratorConversion built = incineratorConversion.built;
				DataObjects.IncineratorConversionDef item = incineratorConversion.def;
				int num = list.FindIndex((IncineratorConversion e) => e != null && SameItem(e.m_result, built.m_result));
				if (num < 0)
				{
					list.Add(NewIncineratorConversion(built, null));
					continue;
				}
				IncineratorConversion val2 = list[num];
				IncineratorConversion val3 = NewIncineratorConversion(built, val2);
				if (!item.resultAmount.HasValue)
				{
					val3.m_resultAmount = val2.m_resultAmount;
				}
				if (!item.priority.HasValue)
				{
					val3.m_priority = val2.m_priority;
				}
				if (!item.anyOneIngredient.HasValue)
				{
					val3.m_requireOnlyOneIngredient = val2.m_requireOnlyOneIngredient;
				}
				list[num] = val3;
			}
			val.m_conversions = ByPriority(go, list);
		}

		private static List<T> Upsert<T>(List<T> existing, List<DataObjects.ResolvedConversion> entries, bool emptyOutputAllowed, Func<T, ItemDrop> fromOf, Func<T, DataObjects.ResolvedConversion, T> update, Func<DataObjects.ResolvedConversion, T> create) where T : class
		{
			List<T> list = ((existing == null) ? new List<T>() : new List<T>(existing));
			foreach (DataObjects.ResolvedConversion entry in entries)
			{
				int num = list.FindIndex((T e) => e != null && SameItem(fromOf(e), entry.from));
				if (num > -1)
				{
					list[num] = update(list[num], entry);
				}
				else if ((Object)(object)entry.to != (Object)null || emptyOutputAllowed)
				{
					list.Add(create(entry));
				}
			}
			return list;
		}

		public static void RemoveConversion(DataObjects.TrackedConversion conversion, GameObject go)
		{
			if (conversion.convertType == DataObjects.ConversionType.Incinerator)
			{
				if (conversion.incineratorConversions == null)
				{
					return;
				}
				CaptureOriginal(go);
				Incinerator val = ConversionUpdater.FindStation<Incinerator>(go);
				if ((Object)(object)val == (Object)null || val.m_conversions == null)
				{
					Logger.LogWarning("Incinerator component not found on " + ((Object)go).name + ", no conversions can be removed from it.");
					return;
				}
				val.m_conversions = ByPriority(go, val.m_conversions.Where((IncineratorConversion e) => e == null || !conversion.incineratorConversions.Exists(((IncineratorConversion built, DataObjects.IncineratorConversionDef def) c) => SameItem(e.m_result, c.built.m_result))).ToList());
			}
			else
			{
				if (conversion.conversions == null)
				{
					return;
				}
				CaptureOriginal(go);
				switch (conversion.convertType)
				{
				case DataObjects.ConversionType.Smelter:
				{
					Smelter val4 = ConversionUpdater.FindStation<Smelter>(go);
					if ((Object)(object)val4 == (Object)null || val4.m_conversion == null)
					{
						Logger.LogWarning("Smelter component not found on " + ((Object)go).name + ", no conversions can be removed from it.");
						break;
					}
					val4.m_conversion = Without(val4.m_conversion, conversion.conversions, (ItemConversion e) => e.m_from, (ItemConversion e) => e.m_to);
					break;
				}
				case DataObjects.ConversionType.Fermenter:
				{
					Fermenter val3 = ConversionUpdater.FindStation<Fermenter>(go);
					if ((Object)(object)val3 == (Object)null || val3.m_conversion == null)
					{
						Logger.LogWarning("Fermenter component not found on " + ((Object)go).name + ", no conversions can be removed from it.");
						break;
					}
					val3.m_conversion = Without(val3.m_conversion, conversion.conversions, (ItemConversion e) => e.m_from, (ItemConversion e) => e.m_to);
					break;
				}
				case DataObjects.ConversionType.CookingStation:
				{
					CookingStation val2 = ConversionUpdater.FindStation<CookingStation>(go);
					if ((Object)(object)val2 == (Object)null || val2.m_conversion == null)
					{
						Logger.LogWarning("CookingStation component not found on " + ((Object)go).name + ", no conversions can be removed from it.");
						break;
					}
					val2.m_conversion = Without(val2.m_conversion, conversion.conversions, (ItemConversion e) => e.m_from, (ItemConversion e) => e.m_to);
					break;
				}
				}
			}
		}

		private static List<T> Without<T>(List<T> existing, List<DataObjects.ResolvedConversion> entries, Func<T, ItemDrop> fromOf, Func<T, ItemDrop> toOf)
		{
			List<T> list = new List<T>(existing);
			foreach (DataObjects.ResolvedConversion entry in entries)
			{
				list.RemoveAll((T e) => e != null && SameItem(fromOf(e), entry.from) && (!entry.toGiven || SameItem(toOf(e), entry.to)));
			}
			return list;
		}

		public static void BuildConversionTracker()
		{
			TrackedConversions.Clear();
			foreach (KeyValuePair<string, DataObjects.ConversionModification> item3 in ConversionsToModify)
			{
				if (item3.Key == null || item3.Value == null)
				{
					continue;
				}
				Logger.LogDebug("Constructing Conversion modifications for " + item3.Key);
				if (string.IsNullOrEmpty(item3.Value.prefab))
				{
					Logger.LogWarning("Conversion modification '" + item3.Key + "' has no prefab set, it will be skipped. Define a prefab to modify to fix this.");
					continue;
				}
				DataObjects.TrackedConversion trackedConversion = new DataObjects.TrackedConversion
				{
					key = item3.Key,
					action = item3.Value.action
				};
				GameObject prefab = PrefabManager.Instance.GetPrefab(item3.Value.prefab);
				if ((Object)(object)prefab == (Object)null)
				{
					Logger.LogWarning("Could not find piece with name: " + item3.Value.prefab + ", this modification will be skipped.");
					continue;
				}
				trackedConversion.prefab = prefab;
				Smelter val = ConversionUpdater.FindStation<Smelter>(prefab);
				if ((Object)(object)val != (Object)null)
				{
					trackedConversion.smelterComponent = val;
					trackedConversion.convertType = DataObjects.ConversionType.Smelter;
				}
				Fermenter val2 = ConversionUpdater.FindStation<Fermenter>(prefab);
				if ((Object)(object)val2 != (Object)null)
				{
					trackedConversion.fermenterComponent = val2;
					trackedConversion.convertType = DataObjects.ConversionType.Fermenter;
				}
				CookingStation val3 = ConversionUpdater.FindStation<CookingStation>(prefab);
				if ((Object)(object)val3 != (Object)null)
				{
					trackedConversion.cookingStationComponent = val3;
					trackedConversion.convertType = DataObjects.ConversionType.CookingStation;
				}
				Incinerator val4 = ConversionUpdater.FindStation<Incinerator>(prefab);
				if ((Object)(object)val4 != (Object)null)
				{
					trackedConversion.incineratorComponent = val4;
					trackedConversion.convertType = DataObjects.ConversionType.Incinerator;
				}
				if ((Object)(object)trackedConversion.cookingStationComponent == (Object)null && (Object)(object)trackedConversion.fermenterComponent == (Object)null && (Object)(object)trackedConversion.smelterComponent == (Object)null && (Object)(object)trackedConversion.incineratorComponent == (Object)null)
				{
					Logger.LogWarning(item3.Value.prefab + " did not have an attached Fermenter, Smelter, CookingStation or Incinerator. This modification will be skipped.");
					continue;
				}
				bool flag = trackedConversion.convertType == DataObjects.ConversionType.Incinerator;
				if (flag && item3.Value.conversions != null)
				{
					Logger.LogWarning("Conversion modification '" + item3.Key + "' lists conversions for " + item3.Value.prefab + ", an incinerator. Use incineratorConversions instead, they are skipped.");
				}
				if (!flag && (item3.Value.incineratorConversions != null || item3.Value.defaultResult != null || item3.Value.defaultCost.HasValue))
				{
					Logger.LogWarning("Conversion modification '" + item3.Key + "' sets incinerator settings on " + item3.Value.prefab + ", which is not an incinerator. They are skipped.");
				}
				if (item3.Value.overCookedTime > 0f && !warnedOverCookedTime)
				{
					warnedOverCookedTime = true;
					Logger.LogWarning("Conversion modification '" + item3.Key + "' sets overCookedTime, which the game has no setting for. It is ignored, remove it from your conversion files.");
				}
				if (item3.Value.conversions != null && trackedConversion.convertType != DataObjects.ConversionType.Incinerator)
				{
					trackedConversion.conversions = new List<DataObjects.ResolvedConversion>();
					foreach (DataObjects.ConversionDef conversion in item3.Value.conversions)
					{
						if (ResolveConversion(item3.Key, conversion, trackedConversion, out var resolved))
						{
							trackedConversion.conversions.Add(resolved);
						}
					}
				}
				switch (trackedConversion.convertType)
				{
				case DataObjects.ConversionType.Fermenter:
					Logger.LogDebug("Setting Fermenter");
					if (item3.Value.conversionTime.HasValue)
					{
						if (item3.Value.conversionTime.Value > 0f)
						{
							trackedConversion.conversionTime = item3.Value.conversionTime;
						}
						else
						{
							Logger.LogWarning($"{item3.Key} has a conversionTime of {item3.Value.conversionTime.Value}, it must be above 0. The fermentation time will not be changed.");
						}
					}
					break;
				case DataObjects.ConversionType.CookingStation:
					Logger.LogDebug("Setting CookingStation");
					trackedConversion.maxFuel = NonNegative(item3.Key, "maxFuel", item3.Value.maxFuel);
					if (item3.Value.secPerFuel.HasValue)
					{
						if (item3.Value.secPerFuel.Value > 0f)
						{
							trackedConversion.secPerFuel = item3.Value.secPerFuel;
						}
						else
						{
							Logger.LogWarning($"{item3.Key} has a secPerFuel of {item3.Value.secPerFuel.Value}, it must be above 0 and will be ignored.");
						}
					}
					trackedConversion.fuelItem = ResolveNamedItem(item3.Key, "fuel item", item3.Value.fuelItem);
					trackedConversion.overCookedItem = ResolveNamedItem(item3.Key, "over cooked item", item3.Value.overCookedItem);
					trackedConversion.requiresFire = item3.Value.requiresFire;
					break;
				case DataObjects.ConversionType.Incinerator:
					trackedConversion.defaultResult = ResolveNamedItem(item3.Key, "default result", item3.Value.defaultResult);
					if (item3.Value.defaultCost.HasValue)
					{
						if (item3.Value.defaultCost.Value > 0)
						{
							trackedConversion.defaultCost = item3.Value.defaultCost;
						}
						else
						{
							Logger.LogWarning($"{item3.Key} has a defaultCost of {item3.Value.defaultCost.Value}, it must be above 0 and will be ignored.");
						}
					}
					if (item3.Value.incineratorConversions == null)
					{
						break;
					}
					trackedConversion.incineratorConversions = new List<(IncineratorConversion, DataObjects.IncineratorConversionDef)>();
					foreach (DataObjects.IncineratorConversionDef incineratorConversion in item3.Value.incineratorConversions)
					{
						IncineratorConversion val5 = ResolveIncineratorConversion(item3.Key, incineratorConversion, trackedConversion.action, val4);
						if (val5 != null)
						{
							trackedConversion.incineratorConversions.Add((val5, incineratorConversion));
						}
					}
					break;
				case DataObjects.ConversionType.Smelter:
					Logger.LogDebug("Setting Smelter");
					trackedConversion.conversionTime = SmelterConversionTime(item3.Key, item3.Value.conversionTime);
					trackedConversion.fuelPerProduct = NonNegative(item3.Key, "fuelPerProduct", item3.Value.fuelPerProduct);
					trackedConversion.maxFuel = NonNegative(item3.Key, "maxFuel", item3.Value.maxFuel);
					trackedConversion.maxOres = NonNegative(item3.Key, "maxOres", item3.Value.maxOres);
					trackedConversion.fuelItem = ResolveNamedItem(item3.Key, "fuel item", item3.Value.fuelItem);
					trackedConversion.spawnStack = item3.Value.spawnStack;
					trackedConversion.requiresRoof = item3.Value.requiresRoof;
					if (trackedConversion.requiresRoof == true && (Object)(object)val.m_roofCheckPoint == (Object)null)
					{
						Logger.LogWarning(item3.Key + " makes " + item3.Value.prefab + " need a roof, but it has no point to check for one from. That setting is ignored.");
						trackedConversion.requiresRoof = null;
					}
					if (trackedConversion.action != DataObjects.ConversionAction.Remove)
					{
						CheckSmelterSetup(item3.Key, trackedConversion);
					}
					break;
				}
				TrackedConversions.Add(trackedConversion);
			}
			PatchManager.Refresh();
			static void CheckSmelterSetup(string entry, DataObjects.TrackedConversion tconv)
			{
				Smelter smelterComponent = tconv.smelterComponent;
				string name = ((Object)tconv.prefab).name;
				bool flag2 = tconv.action == DataObjects.ConversionAction.Modify;
				int num = (flag2 ? tconv.maxOres : ((int?)null)) ?? smelterComponent.m_maxOre;
				int num2 = (flag2 ? tconv.maxFuel : ((int?)null)) ?? smelterComponent.m_maxFuel;
				if (num > 0 && (Object)(object)smelterComponent.m_addOreSwitch == (Object)null)
				{
					Logger.LogWarning($"{entry} leaves {name} with maxOres {num}, but it has nowhere to load items. It will never produce anything unless maxOres is 0.");
				}
				if (num2 > 0 && (Object)(object)smelterComponent.m_addWoodSwitch == (Object)null)
				{
					Logger.LogWarning($"{entry} leaves {name} with maxFuel {num2}, but it has nowhere to load fuel. It will never produce anything unless maxFuel is 0.");
				}
				List<DataObjects.ResolvedConversion> conversions = tconv.conversions;
				if (num > 0)
				{
					int num3 = conversions?.RemoveAll((DataObjects.ResolvedConversion resolvedConversion) => (Object)(object)resolvedConversion.from == (Object)null) ?? 0;
					if (num3 > 0)
					{
						Logger.LogWarning($"{entry} gives {name} {num3} conversion(s) with no fromPrefab, but it takes items (maxOres {num}). Those only work with maxOres 0 and have been skipped.");
					}
				}
				else
				{
					List<ItemDrop> list;
					if (flag2 && conversions != null)
					{
						list = conversions.Select((DataObjects.ResolvedConversion resolvedConversion) => resolvedConversion.from).ToList();
					}
					else
					{
						list = (from val6 in smelterComponent.m_conversion ?? new List<ItemConversion>()
							where val6 != null
							select val6.m_from).ToList();
						foreach (DataObjects.ResolvedConversion c in conversions ?? new List<DataObjects.ResolvedConversion>())
						{
							if (!list.Exists((ItemDrop i) => SameItem(i, c.from)))
							{
								list.Add(c.from);
							}
						}
					}
					if (list.Any((ItemDrop i) => (Object)(object)i != (Object)null))
					{
						Logger.LogWarning(entry + " leaves " + name + " with maxOres 0, so it takes no items and its conversions with a fromPrefab will never run.");
					}
					if (list.Count((ItemDrop i) => (Object)(object)i == (Object)null) > 1)
					{
						Logger.LogWarning(entry + " leaves " + name + " with more than one conversion without a fromPrefab, only the first of them is ever produced.");
					}
				}
			}
			static int? NonNegative(string entry, string field, int? value)
			{
				if (value.HasValue && value.Value < 0)
				{
					Logger.LogWarning($"{entry} has a {field} of {value.Value}, it can not be negative and will be ignored.");
					return null;
				}
				return value;
			}
			static bool ResolveConversion(string entry, DataObjects.ConversionDef conv, DataObjects.TrackedConversion tconv, out DataObjects.ResolvedConversion reference)
			{
				reference = null;
				if (conv == null)
				{
					return false;
				}
				Logger.LogDebug("Creating Conversion " + conv.fromPrefab + " to " + conv.toPrefab);
				bool flag2 = tconv.convertType == DataObjects.ConversionType.Smelter;
				bool flag3 = string.IsNullOrEmpty(conv.fromPrefab);
				bool flag4 = string.IsNullOrEmpty(conv.toPrefab);
				if (flag3 && flag4)
				{
					Logger.LogWarning(entry + " has a conversion with neither a fromPrefab nor a toPrefab, it will be skipped.");
					return false;
				}
				if (!flag2 && flag3)
				{
					Logger.LogWarning(entry + " has a conversion with no fromPrefab, only smelter type stations can leave it out. This conversion will be skipped.");
					return false;
				}
				if (!ResolveItem(entry, conv.fromPrefab, out var item) || !ResolveItem(entry, conv.toPrefab, out var item2))
				{
					return false;
				}
				if (!flag2 && flag4 && tconv.action != DataObjects.ConversionAction.Remove && (tconv.action != DataObjects.ConversionAction.Add || !StationHasInput(tconv, item)))
				{
					Logger.LogWarning(entry + " has a conversion from " + conv.fromPrefab + " with no toPrefab, only smelter type stations can leave it out. This conversion will be skipped.");
					return false;
				}
				int? amount = conv.amount;
				if (amount.HasValue && tconv.action != DataObjects.ConversionAction.Remove)
				{
					if (tconv.convertType == DataObjects.ConversionType.CookingStation)
					{
						Logger.LogWarning(entry + " sets an amount on its conversion from " + conv.fromPrefab + ", but cooking stations always make one item. It is ignored.");
					}
					else if (flag2 && amount.Value < 1)
					{
						Logger.LogWarning($"{entry} has an amount of {amount.Value} on its conversion from {conv.fromPrefab} to {conv.toPrefab}, it must be at least 1 and will be ignored.");
						amount = null;
					}
				}
				reference = new DataObjects.ResolvedConversion
				{
					from = item,
					to = item2,
					toGiven = (!flag4 || (flag2 && tconv.action == DataObjects.ConversionAction.Modify)),
					amount = amount,
					cookTime = conv.cookTime
				};
				return true;
			}
			static IncineratorConversion ResolveIncineratorConversion(string entry, DataObjects.IncineratorConversionDef def, DataObjects.ConversionAction action, Incinerator station)
			{
				//IL_0179: Unknown result type (might be due to invalid IL or missing references)
				//IL_017e: Unknown result type (might be due to invalid IL or missing references)
				//IL_018a: Unknown result type (might be due to invalid IL or missing references)
				//IL_01a6: Unknown result type (might be due to invalid IL or missing references)
				//IL_01bb: Unknown result type (might be due to invalid IL or missing references)
				//IL_01d0: Unknown result type (might be due to invalid IL or missing references)
				//IL_01d8: Expected O, but got Unknown
				//IL_0099: Unknown result type (might be due to invalid IL or missing references)
				//IL_009e: Unknown result type (might be due to invalid IL or missing references)
				//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
				//IL_00c7: Expected O, but got Unknown
				if (def == null)
				{
					return null;
				}
				ItemDrop result = ResolveNamedItem(entry, "incinerator result", def.result);
				if (!((Object)(object)result == (Object)null))
				{
					List<Requirement> list = null;
					if (def.ingredients != null)
					{
						list = new List<Requirement>();
						foreach (DataObjects.IncineratorIngredientDef ingredient in def.ingredients)
						{
							if (ingredient != null)
							{
								ItemDrop val6 = ResolveNamedItem(entry, "incinerator ingredient", ingredient.prefab);
								if (!((Object)(object)val6 == (Object)null))
								{
									list.Add(new Requirement
									{
										m_resItem = val6,
										m_amount = Math.Max(1, ingredient.amount ?? 1)
									});
								}
							}
						}
						if (list.Count == 0 && action != DataObjects.ConversionAction.Remove)
						{
							Logger.LogWarning(entry + " has an incinerator conversion making " + def.result + " with no ingredients that exist. It needs at least one, it will be skipped.");
							return null;
						}
					}
					else if (action == DataObjects.ConversionAction.Modify || (action == DataObjects.ConversionAction.Add && station?.m_conversions?.Exists((IncineratorConversion e) => e != null && SameItem(e.m_result, result)) != true))
					{
						Logger.LogWarning(entry + " has an incinerator conversion making " + def.result + " with no ingredients. It needs at least one, it will be skipped.");
						return null;
					}
					return new IncineratorConversion
					{
						m_result = result,
						m_resultAmount = Math.Max(1, def.resultAmount ?? 1),
						m_priority = def.priority.GetValueOrDefault(),
						m_requireOnlyOneIngredient = (def.anyOneIngredient == true),
						m_requirements = list
					};
				}
				if (string.IsNullOrEmpty(def.result))
				{
					Logger.LogWarning(entry + " has an incinerator conversion with no result, it will be skipped.");
				}
				return null;
			}
			static bool ResolveItem(string entry, string prefabName, out ItemDrop item)
			{
				item = null;
				if (string.IsNullOrEmpty(prefabName))
				{
					return true;
				}
				GameObject prefab2 = PrefabManager.Instance.GetPrefab(prefabName);
				item = (((Object)(object)prefab2 != (Object)null) ? prefab2.GetComponent<ItemDrop>() : null);
				if ((Object)(object)item == (Object)null)
				{
					Logger.LogWarning(entry + " has a conversion using " + prefabName + ", which " + (((Object)(object)prefab2 == (Object)null) ? "could not be found" : "is not an item") + ". This conversion will be skipped.");
					return false;
				}
				return true;
			}
			static ItemDrop ResolveNamedItem(string entry, string field, string itemName)
			{
				if (string.IsNullOrEmpty(itemName))
				{
					return null;
				}
				GameObject prefab2 = PrefabManager.Instance.GetPrefab(itemName);
				ItemDrop val6 = (((Object)(object)prefab2 != (Object)null) ? prefab2.GetComponent<ItemDrop>() : null);
				if ((Object)(object)val6 == (Object)null)
				{
					Logger.LogWarning(entry + " could not find an item named " + itemName + ", the " + field + " will not be changed.");
				}
				return val6;
			}
			static float? SmelterConversionTime(string entry, float? conversionTime)
			{
				if (!conversionTime.HasValue)
				{
					return null;
				}
				if (conversionTime.Value <= 0f)
				{
					Logger.LogWarning($"{entry} has a conversionTime of {conversionTime.Value}, a smelter with no conversion time never produces anything. The conversion time will not be changed.");
					return null;
				}
				if (conversionTime.Value < 1f)
				{
					Logger.LogWarning($"{entry} has a conversionTime of {conversionTime.Value}, smelters can not produce more than one item per second and a shorter time only burns more fuel. Using 1 instead.");
					return 1f;
				}
				return conversionTime;
			}
			static bool StationHasInput(DataObjects.TrackedConversion tconv, ItemDrop from)
			{
				if ((Object)(object)tconv.fermenterComponent != (Object)null && tconv.fermenterComponent.m_conversion != null)
				{
					return tconv.fermenterComponent.m_conversion.Exists((ItemConversion e) => e != null && SameItem(e.m_from, from));
				}
				if ((Object)(object)tconv.cookingStationComponent != (Object)null && tconv.cookingStationComponent.m_conversion != null)
				{
					return tconv.cookingStationComponent.m_conversion.Exists((ItemConversion e) => e != null && SameItem(e.m_from, from));
				}
				return false;
			}
		}
	}
	internal class WackyImportCommand : ConsoleCommand
	{
		public override string Name => "RM_Import_WackyDB";

		public override string Help => "Converts WackysDatabase files into RecipeManager files, written to BepInEx/config/RecipeManager/WackyImport with a report. Usage: RM_Import_WackyDB [folder], the folder defaults to BepInEx/config/wackysDatabase";

		public override bool IsCheat => true;

		public override void Run(string[] args)
		{
			if ((Object)(object)ObjectDB.instance == (Object)null || (Object)(object)ZNetScene.instance == (Object)null)
			{
				ItemCommandUtils.Respond("Load into a world first, the import checks stations and items against the game.");
				return;
			}
			string text = ((args != null && args.Length != 0) ? string.Join(" ", args).Trim(new char[1] { '"' }) : Path.Combine(Paths.ConfigPath, "wackysDatabase"));
			if (!Path.IsPathRooted(text))
			{
				text = Path.Combine(Paths.ConfigPath, text);
			}
			if (!Directory.Exists(text))
			{
				ItemCommandUtils.Respond("There is no folder " + text + ". Give the folder WackysDatabase's files are in, for example RM_Import_WackyDB wackysDatabase");
				return;
			}
			try
			{
				WackyImporter wackyImporter = new WackyImporter(text);
				wackyImporter.Run();
				int count = wackyImporter.Report.Files.Count;
				int num = wackyImporter.Report.Files.Count((ImportReport.FileResult file) => file.Targets.Count > 0);
				ItemCommandUtils.Respond($"Converted {num} of {count} WackysDatabase file(s): {wackyImporter.Recipes.Count} recipe(s), {wackyImporter.Items.Count} item(s), {wackyImporter.Pieces.Count} piece(s), {wackyImporter.Conversions.Count} conversion(s) and {wackyImporter.StatusEffects.Count} status effect(s).");
				ItemCommandUtils.Respond("Written to " + wackyImporter.Output + ", nothing there is loaded until you move it. Read ImportReport.txt there first.");
				if (wackyImporter.WackyDBLoaded)
				{
					ItemCommandUtils.Respond("WackysDatabase is running, so the report could not tell which of its values the game already had. Run the import with it disabled for a more accurate report.");
				}
			}
			catch (Exception ex)
			{
				ItemCommandUtils.Respond("The import failed: " + ex.Message);
				Logger.LogError($"WackysDatabase import from {text} failed. Error: {ex}");
			}
		}
	}
	internal class ItemReloadCommand : ConsoleCommand
	{
		public override string Name => "RM_Items_Reload";

		public override string Help => "Reloads item additions and modifications. Stats, names, icons and textures are re-applied; new or removed items need a restart.";

		public override bool IsCheat => true;

		public override void Run(string[] args)
		{
			ValConfig.Items.ReloadFromDisk();
			ItemCommandUtils.Respond($"Reloaded {ItemUpdater.ItemDefinitions.Count} item entry(s), {ItemUpdater.ModifiedItems.Count} of which modify an existing item.");
		}
	}
	internal class ItemPrintCommand : ConsoleCommand
	{
		internal const string DumpFileName = "ObjectDBItems.yaml";

		public override string Name => "RM_Items_PrintAll";

		public override string Help => "Prints items in the Object DB to a reference file, with their real stats. Usage: RM_Items_PrintAll [nameFilter]";

		public override bool IsCheat => true;

		public override void Run(string[] args)
		{
			if ((Object)(object)ObjectDB.instance == (Object)null)
			{
				ItemCommandUtils.Respond("The object DB is not available yet, load into a world first.");
				return;
			}
			string text = ((args != null && args.Length != 0) ? args[0] : null);
			if (ValConfig.EnableDebugMode.Value)
			{
				Logger.LogInfo("Starting to dump items");
			}
			string text2 = Path.Combine(Paths.ConfigPath, "RecipeManager", "ObjectDBItems.yaml");
			DataObjects.ItemAdditionCollection itemAdditionCollection = new DataObjects.ItemAdditionCollection();
			foreach (GameObject item in ObjectDB.instance.m_items.ToList())
			{
				if ((Object)(object)item == (Object)null || (text != null && ((Object)item).name.IndexOf(text, StringComparison.OrdinalIgnoreCase) < 0))
				{
					continue;
				}
				SharedData val = item.GetComponent<ItemDrop>()?.m_itemData?.m_shared;
				if (val == null)
				{
					continue;
				}
				if (itemAdditionCollection.ItemAdditions.ContainsKey(((Object)item).name))
				{
					Logger.LogWarning(((Object)item).name + " was already added to the list of items and will be skipped.");
					continue;
				}
				if (ValConfig.EnableDebugMode.Value)
				{
					Logger.LogInfo("Building item definition " + ((Object)item).name);
				}
				itemAdditionCollection.ItemAdditions.Add(((Object)item).name, DescribeItem(((Object)item).name, val));
			}
			if (ValConfig.EnableDebugMode.Value)
			{
				Logger.LogInfo("Serializing and printing items.");
			}
			using (StreamWriter streamWriter = new StreamWriter(text2))
			{
				streamWriter.WriteLine("# Reference dump of the items in the object DB, with the stats they currently have.");
				streamWriter.WriteLine("# This file is NOT loaded by the mod, it is here so you can see what the numbers are.");
				streamWriter.WriteLine("#");
				streamWriter.WriteLine("# To change one of these items, copy its entry into your own Items.yaml and delete");
				streamWriter.WriteLine("# every line you do not want to change - anything left out keeps its current value.");
				streamWriter.WriteLine("# To create a new item from one instead, set 'action: Add', point 'clonedFrom' at the");
				streamWriter.WriteLine("# prefab below and give 'prefab' a new unique name.");
				streamWriter.WriteLine();
				streamWriter.WriteLine(ValConfig.yamlserializer.Serialize((object)itemAdditionCollection));
			}
			ItemCommandUtils.Respond($"Dumped {itemAdditionCollection.ItemAdditions.Count} item(s) to {text2}");
		}

		private static DataObjects.ItemAddition DescribeItem(string prefabName, SharedData shared)
		{
			//IL_02c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_021b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0221: Invalid comparison between Unknown and I4
			//IL_02d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d9: Invalid comparison between Unknown and I4
			//IL_02f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_02fc: Invalid comparison between Unknown and I4
			//IL_02ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_0353: Unknown result type (might be due to invalid IL or missing references)
			//IL_0364: Unknown result type (might be due to invalid IL or missing references)
			//IL_030c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0313: Invalid comparison between Unknown and I4
			//IL_07f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_07f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0806: Unknown result type (might be due to invalid IL or missing references)
			//IL_0808: Unknown result type (might be due to invalid IL or missing references)
			//IL_0813: Unknown result type (might be due to invalid IL or missing references)
			//IL_0815: Unknown result type (might be due to invalid IL or missing references)
			DataObjects.ItemAddition itemAddition = new DataObjects.ItemAddition
			{
				action = DataObjects.ItemAction.Modify,
				prefab = prefabName,
				name = shared.m_name,
				description = shared.m_description,
				weight = shared.m_weight,
				maxStackSize = shared.m_maxStackSize,
				value = shared.m_value,
				teleportable = shared.m_teleportable,
				equipDuration = shared.m_equipDuration,
				questItem = (shared.m_questItem ? new bool?(true) : ((bool?)null)),
				appendToolTip = (((Object)(object)shared.m_appendToolTip != (Object)null) ? ((Object)((Component)shared.m_appendToolTip).gameObject).name : null),
				quality = new DataObjects.ItemQuality
				{
					maxLevel = shared.m_maxQuality,
					weightPerLevel = ((shared.m_scaleWeightByQuality != 0f) ? new float?(shared.m_scaleWeightByQuality) : ((float?)null))
				}
			};
			if (shared.m_maxQuality > 1)
			{
				itemAddition.quality.upgradeChance = shared.m_upgradeChance;
				itemAddition.quality.breakChance = shared.m_breakChance;
				itemAddition.quality.upgradeSteps = shared.m_successUpgradeSteps;
				itemAddition.quality.breakRefund = shared.m_breakReturnIngreientsAmount;
			}
			if (shared.m_useDurability)
			{
				itemAddition.durability = new DataObjects.ItemDurability
				{
					enabled = true,
					max = shared.m_maxDurability,
					perLevel = shared.m_durabilityPerLevel,
					drainPerUse = shared.m_useDurabilityDrain,
					drainPerSecond = shared.m_durabilityDrain,
					repairable = shared.m_canBeReparied,
					destroyWhenBroken = shared.m_destroyBroken
				};
			}
			if (shared.m_food > 0f || shared.m_foodStamina > 0f || shared.m_foodEitr > 0f || (int)shared.m_itemType == 2)
			{
				DataObjects.FoodStats obj = new DataObjects.FoodStats
				{
					health = shared.m_food,
					stamina = shared.m_foodStamina,
					eitr = shared.m_foodEitr,
					duration = shared.m_foodBurnTime,
					regen = shared.m_foodRegen,
					eatAnimTime = shared.m_foodEatAnimTime,
					isDrink = shared.m_isDrink
				};
				StatusEffect consumeStatusEffect = shared.m_consumeStatusEffect;
				obj.consumeStatusEffect = ((consumeStatusEffect != null) ? ((Object)consumeStatusEffect).name : null);
				itemAddition.food = obj;
			}
			bool flag = UsesAttacks(shared.m_itemType) || ((int)shared.m_itemType == 5 && shared.m_buildBlockCharges);
			bool flag2 = shared.m_blockPower > 0f && ((int)shared.m_itemType == 5 || (UsesAttacks(shared.m_itemType) && (int)shared.m_itemType != 9));
			if (flag || ((DamageTypes)(ref shared.m_damages)).HaveDamage() || ((DamageTypes)(ref shared.m_damagesPerLevel)).HaveDamage() || shared.m_toolTier > 0)
			{
				itemAddition.weapon = new DataObjects.WeaponStats
				{
					damage = DescribeDamage(shared.m_damages),
					damagePerLevel = DescribeDamage(shared.m_damagesPerLevel),
					attackForce = shared.m_attackForce,
					backstabBonus = shared.m_backstabBonus,
					toolTier = shared.m_toolTier,
					skill = ((object)Unsafe.As<SkillType, SkillType>(ref shared.m_skillType)/*cast due to .constrained prefix*/).ToString(),
					ammoType = (string.IsNullOrEmpty(shared.m_ammoType) ? null : shared.m_ammoType),
					dodgeable = shared.m_dodgeable,
					blockable = shared.m_blockable,
					hitVariant = ((shared.m_hitVariant >= 0) ? new int?(shared.m_hitVariant) : ((int?)null)),
					spawnOnHit = (((Object)(object)shared.m_spawnOnHit != (Object)null) ? ((Object)shared.m_spawnOnHit).name : null),
					spawnOnHitTerrain = (((Object)(object)shared.m_spawnOnHitTerrain != (Object)null) ? ((Object)shared.m_spawnOnHitTerrain).name : null)
				};
				if (flag)
				{
					itemAddition.weapon.primaryAttack = DescribeAttack(shared.m_attack);
					if (shared.m_secondaryAttack != null && !string.IsNullOrEmpty(shared.m_secondaryAttack.m_attackAnimation))
					{
						itemAddition.weapon.secondaryAttack = DescribeAttack(shared.m_secondaryAttack);
					}
				}
			}
			if (shared.m_armor > 0f || shared.m_blockPower > 0f || shared.m_movementModifier != 0f || shared.m_eitrRegenModifier != 0f || shared.m_heatResistanceModifier != 0f)
			{
				itemAddition.armor = new DataObjects.ArmorStats
				{
					armor = shared.m_armor,
					armorPerLevel = shared.m_armorPerLevel,
					blockPower = shared.m_blockPower,
					blockPowerPerLevel = shared.m_blockPowerPerLevel,
					deflectionForce = shared.m_deflectionForce,
					deflectionForcePerLevel = shared.m_deflectionForcePerLevel,
					movementModifier = shared.m_movementModifier,
					eitrRegenModifier = shared.m_eitrRegenModifier,
					heatResistanceModifier = ((shared.m_heatResistanceModifier != 0f) ? new float?(shared.m_heatResistanceModifier) : ((float?)null))
				};
				if (flag2)
				{
					itemAddition.armor.timedBlockBonus = shared.m_timedBlockBonus;
					itemAddition.armor.perfectBlockStaminaRegen = shared.m_perfectBlockStaminaRegen;
					itemAddition.armor.buildBlockCharges = shared.m_buildBlockCharges;
					if (shared.m_buildBlockCharges)
					{
						itemAddition.armor.maxBlockCharges = shared.m_maxBlockCharges;
						itemAddition.armor.blockChargeDecayTime = shared.m_blockChargeDecayTime;
					}
				}
			}
			if (shared.m_homeItemsStaminaModifier != 0f || shared.m_jumpStaminaModifier != 0f || shared.m_attackStaminaModifier != 0f || shared.m_blockStaminaModifier != 0f || shared.m_dodgeStaminaModifier != 0f || shared.m_swimStaminaModifier != 0f || shared.m_sneakStaminaModifier != 0f || shared.m_runStaminaModifier != 0f)
			{
				itemAddition.staminaModifiers = new DataObjects.StaminaModifiers
				{
					homeItems = shared.m_homeItemsStaminaModifier,
					jump = shared.m_jumpStaminaModifier,
					attack = shared.m_attackStaminaModifier,
					block = shared.m_blockStaminaModifier,
					dodge = shared.m_dodgeStaminaModifier,
					swim = shared.m_swimStaminaModifier,
					sneak = shared.m_sneakStaminaModifier,
					run = shared.m_runStaminaModifier
				};
			}
			if (shared.m_maxAdrenaline > 0f || flag2)
			{
				itemAddition.adrenaline = new DataObjects.AdrenalineStats
				{
					max = ((shared.m_maxAdrenaline > 0f) ? new float?(shared.m_maxAdrenaline) : ((float?)null)),
					block = (flag2 ? new float?(shared.m_blockAdrenaline) : ((float?)null)),
					perfectBlock = (flag2 ? new float?(shared.m_perfectBlockAdrenaline) : ((float?)null))
				};
			}
			if (shared.m_damageModifiers != null && shared.m_damageModifiers.Count > 0)
			{
				itemAddition.resistances = new List<DataObjects.DamageResistance>();
				foreach (DamageModPair damageModifier in shared.m_damageModifiers)
				{
					itemAddition.resistances.Add(new DataObjects.DamageResistance
					{
						type = damageModifier.m_type,
						modifier = damageModifier.m_modifier
					});
				}
			}
			if ((Object)(object)shared.m_equipStatusEffect != (Object)null || (Object)(object)shared.m_attackStatusEffect != (Object)null || (Object)(object)shared.m_perfectBlockStatusEffect != (Object)null || (Object)(object)shared.m_fullAdrenalineSE != (Object)null)
			{
				DataObjects.ItemStatusEffects itemStatusEffects = new DataObjects.ItemStatusEffects();
				StatusEffect equipStatusEffect = shared.m_equipStatusEffect;
				itemStatusEffects.equipped = ((equipStatusEffect != null) ? ((Object)equipStatusEffect).name : null);
				StatusEffect attackStatusEffect = shared.m_attackStatusEffect;
				itemStatusEffects.onHit = ((attackStatusEffect != null) ? ((Object)attackStatusEffect).name : null);
				itemStatusEffects.onHitChance = (((Object)(object)shared.m_attackStatusEffect != (Object)null) ? new float?(shared.m_attackStatusEffectChance) : ((float?)null));
				StatusEffect perfectBlockStatusEffect = shared.m_perfectBlockStatusEffect;
				itemStatusEffects.perfectBlock = ((perfectBlockStatusEffect != null) ? ((Object)perfectBlockStatusEffect).name : null);
				StatusEffect fullAdrenalineSE = shared.m_fullAdrenalineSE;
				itemStatusEffects.fullAdrenaline = ((fullAdrenalineSE != null) ? ((Object)fullAdrenalineSE).name : null);
				itemAddition.statusEffects = itemStatusEffects;
			}
			if (!string.IsNullOrEmpty(shared.m_setName))
			{
				DataObjects.SetBonus obj2 = new DataObjects.SetBonus
				{
					name = shared.m_setName,
					size = shared.m_setSize
				};
				StatusEffect setStatusEffect = shared.m_setStatusEffect;
				obj2.statusEffect = ((setStatusEffect != null) ? ((Object)setStatusEffect).name : null);
				itemAddition.setBonus = obj2;
			}
			return itemAddition;
		}

		private static DataObjects.DamageValues DescribeDamage(DamageTypes damages)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d9: Unknown result type (might be due to invalid IL or missing references)
			return new DataObjects.DamageValues
			{
				damage = damages.m_damage,
				blunt = damages.m_blunt,
				slash = damages.m_slash,
				pierce = damages.m_pierce,
				chop = damages.m_chop,
				pickaxe = damages.m_pickaxe,
				fire = damages.m_fire,
				frost = damages.m_frost,
				lightning = damages.m_lightning,
				poison = damages.m_poison,
				spirit = damages.m_spirit,
				nonPlayer = ((damages.m_nonPlayer != 0f) ? new float?(damages.m_nonPlayer) : ((float?)null))
			};
		}

		private static bool UsesAttacks(ItemType type)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0003: Invalid comparison between Unknown and I4
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Invalid comparison between Unknown and I4
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Invalid comparison between Unknown and I4
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Invalid comparison between Unknown and I4
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Invalid comparison between Unknown and I4
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Invalid comparison between Unknown and I4
			if ((int)type <= 9)
			{
				if (type - 3 <= 1 || (int)type == 9)
				{
					goto IL_0023;
				}
			}
			else if (type - 14 <= 1 || (int)type == 19 || (int)type == 22)
			{
				goto IL_0023;
			}
			return false;
			IL_0023:
			return true;
		}

		private static DataObjects.AttackStats DescribeAttack(Attack attack)
		{
			//IL_02e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e8: Invalid comparison between Unknown and I4
			if (attack == null)
			{
				return null;
			}
			DataObjects.AttackStats attackStats = new DataObjects.AttackStats
			{
				stamina = attack.m_attackStamina,
				eitr = attack.m_attackEitr,
				health = attack.m_attackHealth,
				healthPercentage = attack.m_attackHealthPercentage,
				adrenaline = attack.m_attackAdrenaline,
				useAdrenaline = attack.m_attackUseAdrenaline,
				speedFactor = attack.m_speedFactor,
				rotationSpeedFactor = attack.m_speedFactorRotation,
				damageMultiplier = attack.m_damageMultiplier,
				forceMultiplier = attack.m_forceMultiplier,
				staggerMultiplier = attack.m_staggerMultiplier,
				lastChainDamageMultiplier = ((attack.m_attackChainLevels > 1) ? new float?(attack.m_lastChainDamageMultiplier) : ((float?)null)),
				damageMultiplierPerMissingHealth = ((attack.m_damageMultiplierPerMissingHP != 0f) ? new float?(attack.m_damageMultiplierPerMissingHP) : ((float?)null)),
				damageMultiplierByHealthMissing = ((attack.m_damageMultiplierByTotalHealthMissing != 0f) ? new float?(attack.m_damageMultiplierByTotalHealthMissing) : ((float?)null)),
				staminaReturnPerMissingHealth = ((attack.m_staminaReturnPerMissingHP != 0f) ? new float?(attack.m_staminaReturnPerMissingHP) : ((float?)null)),
				range = attack.m_attackRange,
				height = attack.m_attackHeight,
				angle = attack.m_attackAngle,
				rayWidth = attack.m_attackRayWidth,
				multiHit = attack.m_multiHit,
				lowerDamagePerHit = attack.m_lowerDamagePerHit,
				hitTerrain = attack.m_hitTerrain,
				hitFriendly = attack.m_hitFriendly,
				hitThroughWalls = attack.m_hitThroughWalls,
				recoilPushback = attack.m_recoilPushback,
				selfDamage = attack.m_selfDamage,
				consumeItem = attack.m_consumeItem,
				raiseSkillAmount = attack.m_raiseSkillAmount,
				startNoise = attack.m_attackStartNoise,
				hitNoise = attack.m_attackHitNoise,
				spawnOnTrigger = (((Object)(object)attack.m_spawnOnTrigger != (Object)null) ? ((Object)attack.m_spawnOnTrigger).name : null),
				spawnOnHit = (((Object)(object)attack.m_spawnOnHit != (Object)null) ? ((Object)attack.m_spawnOnHit).name : null),
				spawnOnHitChance = (((Object)(object)attack.m_spawnOnHit != (Object)null) ? new float?(attack.m_spawnOnHitChance) : ((float?)null))
			};
			if ((Object)(object)attack.m_attackProjectile != (Object)null || (int)attack.m_attackType == 2)
			{
				attackStats.projectile = new DataObjects.ProjectileStats
				{
					prefab = (((Object)(object)attack.m_attackProjectile != (Object)null) ? ((Object)attack.m_attackProjectile).name : null),
					velocity = attack.m_projectileVel,
					velocityMin = attack.m_projectileVelMin,
					accuracy = attack.m_projectileAccuracy,
					accuracyMin = attack.m_projectileAccuracyMin,
					skillAccuracy = attack.m_skillAccuracy,
					count = attack.m_projectiles,
					bursts = attack.m_projectileBursts,
					burstInterval = attack.m_burstInterval,
					launchAngle = attack.m_launchAngle
				};
			}
			if (attack.m_requiresReload)
			{
				attackStats.reload = new DataObjects.ReloadStats
				{
					time = attack.m_reloadTime,
					staminaDrain = attack.m_reloadStaminaDrain,
					eitrDrain = attack.m_reloadEitrDrain
				};
			}
			if (attack.m_bowDraw)
			{
				attackStats.draw = new DataObjects.DrawStats
				{
					durationMin = attack.m_drawDurationMin,
					staminaDrain = attack.m_drawStaminaDrain,
					eitrDrain = attack.m_drawEitrDrain
				};
			}
			return attackStats;
		}

		public override List<string> CommandOptionList()
		{
			List<string> list = new List<string>();
			if (ObjectDB.instance?.m_items == null)
			{
				return list;
			}
			foreach (GameObject item in ObjectDB.instance.m_items)
			{
				if ((Object)(object)item != (Object)null)
				{
					list.Add(((Object)item).name);
				}
			}
			return list;
		}
	}
	internal class TextureDumpCommand : ConsoleCommand
	{
		public override string Name => "RM_Textures_Dump";

		public override string Help => "Dumps a prefab's icons and material textures to disk as PNGs. Usage: RM_Textures_Dump <prefabName>";

		public override bool IsCheat => true;

		public override void Run(string[] args)
		{
			if (args == null || args.Length < 1 || string.IsNullOrEmpty(args[0]))
			{
				ItemCommandUtils.Respond("Usage: " + ((ConsoleCommand)this).Name + " <prefabName>");
				return;
			}
			string text = args[0];
			GameObject prefab = PrefabManager.Instance.GetPrefab(text);
			if ((Object)(object)prefab == (Object)null)
			{
				ItemCommandUtils.Respond("No prefab named '" + text + "' could be found. Prefab names are case sensitive.");
				return;
			}
			try
			{
				string outputDirectory;
				int num = TextureTools.DumpPrefab(prefab, out outputDirectory);
				if (num == 0)
				{
					ItemCommandUtils.Respond("Found no textures to dump for '" + text + "'. See " + outputDirectory + "\\_dump_info.txt");
				}
				else
				{
					ItemCommandUtils.Respond($"Dumped {num} texture(s) for '{text}' to {outputDirectory}");
				}
			}
			catch (Exception ex)
			{
				ItemCommandUtils.Respond("Dumping textures for '" + text + "' failed: " + ex.Message);
				Logger.LogError($"Texture dump for '{text}' failed. Error: {ex}");
			}
		}

		public override List<string> CommandOptionList()
		{
			List<string> list = new List<string>();
			if (ObjectDB.instance?.m_items == null)
			{
				return list;
			}
			foreach (GameObject item in ObjectDB.instance.m_items)
			{
				if ((Object)(object)item != (Object)null)
				{
					list.Add(((Object)item).name);
				}
			}
			return list;
		}
	}
	internal static class ItemCommandUtils
	{
		internal static void Respond(string message)
		{
			Logger.LogInfo(message);
			if ((Object)(object)Console.instance != (Object)null)
			{
				Console.instance.Print(message);
			}
		}
	}
	internal class ItemUpdater
	{
		public static Dictionary<string, DataObjects.ItemAddition> ItemDefinitions = new Dictionary<string, DataObjects.ItemAddition>();

		public static List<DataObjects.TrackedItem> TrackedItems = new List<DataObjects.TrackedItem>();

		public static List<DataObjects.TrackedItem> ModifiedItems = new List<DataObjects.TrackedItem>();

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

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

		public static void CreateCustomItems()
		{
			TrackedItems.Clear();
			if (ItemDefinitions.Count == 0)
			{
				return;
			}
			foreach (KeyValuePair<string, DataObjects.ItemAddition> itemDefinition in ItemDefinitions)
			{
				if (itemDefinition.Value == null)
				{
					Logger.LogError("Item entry '" + itemDefinition.Key + "' is empty, it needs at least an action and a prefab. Skipping.");
				}
				else if (itemDefinition.Value.action != DataObjects.ItemAction.Modify)
				{
					try
					{
						CreateCustomItem(itemDefinition.Key, itemDefinition.Value);
					}
					catch (Exception arg)
					{
						Logger.LogError($"Item addition '{itemDefinition.Key}' failed and will be skipped. Error: {arg}");
					}
				}
			}
			if (TrackedItems.Count > 0)
			{
				Logger.LogInfo($"Registered {TrackedItems.Count} custom item(s).");
			}
		}

		private static void CreateCustomItem(string modificationName, DataObjects.ItemAddition def)
		{
			//IL_01ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f2: Expected O, but got Unknown
			if (!ValidateDefinition(modificationName, def))
			{
				return;
			}
			if (def.action == DataObjects.ItemAction.Remove)
			{
				if (CreatedPrefabs.Contains(def.prefab))
				{
					Logger.LogWarning("Item addition '" + modificationName + "' is set to Remove, but " + def.prefab + " was already registered this session. Restart the game to fully remove it.");
				}
				return;
			}
			DataObjects.TrackedItem trackedItem = new DataObjects.TrackedItem
			{
				action = def.action,
				prefab = def.prefab,
				clonedFrom = def.clonedFrom,
				definition = def
			};
			if (CreatedPrefabs.Contains(def.prefab))
			{
				GameObject prefab = PrefabManager.Instance.GetPrefab(def.prefab);
				if ((Object)(object)prefab == (Object)null)
				{
					Logger.LogWarning(def.prefab + " was registered earlier but can no longer be resolved, skipping.");
					return;
				}
				ApplyStatsOnly(prefab, def);
				trackedItem.itemPrefab = prefab;
				TrackedItems.Add(trackedItem);
				return;
			}
			if ((Object)(object)PrefabManager.Instance.GetPrefab(def.prefab) != (Object)null)
			{
				Logger.LogError("Item addition '" + modificationName + "' wants to create the prefab '" + def.prefab + "', but a prefab with that name already exists. Prefab names must be globally unique, and changing an item that already exists is done with 'action: Modify'. This addition will be skipped.");
				return;
			}
			GameObject val = PrefabManager.Instance.CreateClonedPrefab(def.prefab, def.clonedFrom);
			if ((Object)(object)val == (Object)null)
			{
				Logger.LogError("Item addition '" + modificationName + "' could not clone '" + def.clonedFrom + "'. Check that the prefab name is spelled correctly. This addition will be skipped.");
				return;
			}
			if ((Object)(object)val.GetComponent<ItemDrop>() == (Object)null)
			{
				Logger.LogError("'" + def.clonedFrom + "' has no ItemDrop component, so it cannot be used as the source for an item. Item addition '" + modificationName + "' will be skipped.");
				return;
			}
			ItemAttributes.RestoreOriginal(def.clonedFrom, GetSharedData(val));
			ApplyDefinition(val, def);
			CustomItem val2 = new CustomItem(val, false);
			if (!val2.IsValid())
			{
				Logger.LogError("The item created by '" + modificationName + "' is not valid and will be skipped.");
				return;
			}
			if (!ItemManager.Instance.AddItem(val2))
			{
				Logger.LogError("Jotunn rejected the item created by '" + modificationName + "'. This addition will be skipped.");
				return;
			}
			CreatedPrefabs.Add(def.prefab);
			trackedItem.itemPrefab = val;
			trackedItem.customItem = val2;
			if (def.recipe != null)
			{
				CustomRecipe val3 = BuildRecipe(modificationName, def);
				if (val3 != null && !ItemManager.Instance.AddRecipe(val3))
				{
					Logger.LogWarning("Could not register the recipe for '" + modificationName + "'. The item exists but will not be craftable.");
				}
			}
			TrackedItems.Add(trackedItem);
			Logger.LogInfo("Created item " + def.prefab + " from " + def.clonedFrom + ".");
		}

		private static bool ValidateDefinition(string modificationName, DataObjects.ItemAddition def)
		{
			if (string.IsNullOrEmpty(def.prefab))
			{
				Logger.LogError("Item addition '" + modificationName + "' has no 'prefab' set. This is the name of the new item and is required. Skipping.");
				return false;
			}
			if (def.action != DataObjects.ItemAction.Remove && string.IsNullOrEmpty(def.clonedFrom))
			{
				Logger.LogError("Item addition '" + modificationName + "' has no 'clonedFrom' set. This is the existing prefab to copy and is required. If you meant to change an item that already exists, use 'action: Modify' instead. Skipping.");
				return false;
			}
			if (string.Equals(def.prefab, def.clonedFrom, StringComparison.Ordinal))
			{
				Logger.LogError("Item addition '" + modificationName + "' has 'prefab' and 'clonedFrom' set to the same value (" + def.prefab + "). The new item needs its own unique name, and changing the original in place is done with 'action: Modify'. Skipping.");
				return false;
			}
			return true;
		}

		public static void ApplyDefinition(GameObject prefab, DataObjects.ItemAddition def)
		{
			if (ApplyStatsOnly(prefab, def))
			{
				ApplyArt(prefab, def.prefab, warnWhenIconless: true);
			}
		}

		private static bool ApplyStatsOnly(GameObject prefab, DataObjects.ItemAddition def)
		{
			SharedData sharedData = GetSharedData(prefab);
			if (sharedData == null)
			{
				Logger.LogWarning(def.prefab + " has no usable ItemDrop data, nothing to apply.");
				return false;
			}
			ItemAttributes.CaptureOriginal(def.prefab, sharedData);
			ItemAttributes.RestoreOriginal(def.prefab, sharedData);
			ApplyStats(sharedData, def, referencesAvailable: false);
			ItemAttributes.ApplyDefaultHitVariant(sharedData, new DataObjects.ItemAddition[1] { def });
			return true;
		}

		private static void ApplyStats(SharedData shared, DataObjects.ItemAddition def, bool referencesAvailable)
		{
			if (def.action == DataObjects.ItemAction.Modify)
			{
				if (!string.IsNullOrEmpty(def.name))
				{
					shared.m_name = Tokens.Resolve(def.name, "rm_item_", def.prefab, string.Empty);
				}
			}
			else
			{
				shared.m_name = ResolveNameToken(def);
			}
			string text = Tokens.Resolve(def.description, "rm_item_", def.prefab, "_desc");
			if (text != null)
			{
				shared.m_description = text;
			}
			ItemAttributes.Apply(shared, def, referencesAvailable);
		}

		private static void ApplyArt(GameObject prefab, string prefabName, bool warnWhenIconless)
		{
			SharedData sharedData = GetSharedData(prefab);
			if (sharedData != null)
			{
				Sprite[] array = TextureTools.LoadIcons(prefabName, sharedData.m_icons);
				if (array != null && array.Length != 0)
				{
					sharedData.m_icons = array;
				}
				else if (warnWhenIconless)
				{
					Logger.LogWarning(prefabName + " ended up with no icons and would not be pickupable. Check that the prefab it was cloned from has an icon.");
				}
				int num = TextureTools.ApplyTextureOverrides(prefab, prefabName);
				if (num > 0)
				{
					Logger.LogInfo($"Applied {num} texture override(s) to {prefabName}.");
				}
			}
		}

		public static void ApplyItemModifications()
		{
			ApplyItemModifications(withArt: true);
		}

		private static void ApplyItemModifications(bool withArt)
		{
			Dictionary<string, List<DataObjects.ItemAddition>> dictionary = GroupModificationsByPrefab();
			RevertPrefabs(Except(ModifiedPrefabs, dictionary.Keys));
			ModifiedItems.Clear();
			int num = 0;
			foreach (KeyValuePair<string, List<DataObjects.ItemAddition>> item in dictionary)
			{
				try
				{
					if (ApplyModification(item.Key, item.Value, withArt))
					{
						num++;
					}
				}
				catch (Exception arg)
				{
					Logger.LogError($"Could not modify {item.Key}. Error: {arg}");
				}
			}
			if (num > 0)
			{
				Logger.LogInfo($"Applied modifications to {num} existing item(s).");
			}
		}

		private static bool ApplyModification(string prefabName, List<DataObjects.ItemAddition> definitions, bool withArt)
		{
			GameObject val = ResolveItemPrefab(prefabName);
			if ((Object)(object)val == (Object)null)
			{
				Logger.LogWarning("No item named '" + prefabName + "' could be found, the modification targeting it will be skipped. Prefab names are case sensitive, run RM_Items_PrintAll to see the ones that exist.");
				return false;
			}
			SharedData sharedData = GetSharedData(val);
			if (sharedData == null)
			{
				Logger.LogWarning("'" + prefabName + "' has no usable ItemDrop data, the modification targeting it will be skipped.");
				return false;
			}
			ItemAttributes.CaptureOriginal(prefabName, sharedData);
			ItemAttributes.RestoreOriginal(prefabName, sharedData);
			foreach (DataObjects.ItemAddition definition in definitions)
			{
				ApplyStats(sharedData, definition, referencesAvailable: true);
				ModifiedItems.Add(new DataObjects.TrackedItem
				{
					action = DataObjects.ItemAction.Modify,
					prefab = prefabName,
					itemPrefab = val,
					definition = definition
				});
			}
			ItemAttributes.ApplyDefaultHitVariant(sharedData, definitions);
			if (withArt)
			{
				ApplyArt(val, prefabName, warnWhenIconless: false);
			}
			ModifiedPrefabs.Add(prefabName);
			if (ValConfig.EnableDebugMode.Value)
			{
				Logger.LogInfo("Modified " + prefabName + ".");
			}
			return true;
		}

		private static Dictionary<string, List<DataObjects.ItemAddition>> GroupModificationsByPrefab()
		{
			Dictionary<string, List<DataObjects.ItemAddition>> dictionary = new Dictionary<string, List<DataObjects.ItemAddition>>();
			foreach (KeyValuePair<string, DataObjects.ItemAddition> itemDefinition in ItemDefinitions)
			{
				DataObjects.ItemAddition value = itemDefinition.Value;
				if (value == null || value.action != DataObjects.ItemAction.Modify)
				{
					continue;
				}
				if (string.IsNullOrEmpty(value.prefab))
				{
					Logger.LogError("Item modification '" + itemDefinition.Key + "' has no 'prefab' set. This is the item to change and is required. Skipping.");
				}
				else if (CreatedPrefabs.Contains(value.prefab))
				{
					Logger.LogError("Item modification '" + itemDefinition.Key + "' targets " + value.prefab + ", which is an item this mod adds. Put the stats on that 'action: Add' entry instead of modifying it separately. Skipping.");
				}
				else
				{
					if (!dictionary.TryGetValue(value.prefab, out var value2))
					{
						value2 = new List<DataObjects.ItemAddition>();
						dictionary.Add(value.prefab, value2);
					}
					value2.Add(value);
				}
			}
			return dictionary;
		}

		public static void RevertItemModifications()
		{
			RevertPrefabs(new List<string>(ModifiedPrefabs));
			ModifiedItems.Clear();
		}

		private static void RevertPrefabs(List<string> prefabNames)
		{
			foreach (string prefabName in prefabNames)
			{
				SharedData sharedData = GetSharedData(ResolveItemPrefab(prefabName));
				if (sharedData == null)
				{
					Logger.LogWarning(prefabName + " can no longer be resolved, its original stats could not be restored.");
				}
				else if (ItemAttributes.RestoreOriginal(prefabName, sharedData) && ValConfig.EnableDebugMode.Value)
				{
					Logger.LogInfo("Restored the original stats of " + prefabName + ".");
				}
				ModifiedPrefabs.Remove(prefabName);
			}
		}

		private static List<string> Except(IEnumerable<string> source, ICollection<string> exclude)
		{
			List<string> list = new List<string>();
			foreach (string item in source)
			{
				if (!exclude.Contains(item))
				{
					list.Add(item);
				}
			}
			return list;
		}

		private static GameObject ResolveItemPrefab(string prefabName)
		{
			GameObject val = null;
			if ((Object)(object)ObjectDB.instance != (Object)null)
			{
				val = ObjectDB.instance.GetItemPrefab(prefabName);
			}
			if ((Object)(object)val == (Object)null)
			{
				val = PrefabManager.Instance.GetPrefab(prefabName);
			}
			return val;
		}

		private static SharedData GetSharedData(GameObject prefab)
		{
			if ((Object)(object)prefab == (Object)null)
			{
				return null;
			}
			return prefab.GetComponent<ItemDrop>()?.m_itemData?.m_shared;
		}

		public static void ResolveDeferredReferences()
		{
			ResolveDeferredReferences(TrackedItems);
			ResolveDeferredReferences(ModifiedItems);
		}

		private static void ResolveDeferredReferences(List<DataObjects.TrackedItem> tracked)
		{
			foreach (DataObjects.TrackedItem item in tracked)
			{
				if (item.definition != null && !((Object)(object)item.itemPrefab == (Object)null))
				{
					SharedData sharedData = GetSharedData(item.itemPrefab);
					if (sharedData != null)
					{
						ItemAttributes.ApplyReferences(sharedData, item.definition, warnOnMissing: true);
					}
				}
			}
		}

		private static string ResolveNameToken(DataObjects.ItemAddition def)
		{
			if (string.IsNullOrEmpty(def.name))
			{
				Logger.LogWarning("Item addition for " + def.prefab + " has no 'name' set. Falling back to the prefab name - set 'name' to give it a proper display name.");
				return Tokens.Resolve(def.prefab, "rm_item_", def.prefab, string.Empty);
			}
			return Tokens.Resolve(def.name, "rm_item_", def.prefab, string.Empty);
		}

		private static CustomRecipe BuildRecipe(string modificationName, DataObjects.ItemAddition def)
		{
			//IL_0117: Unknown result type (might be due to invalid IL or missing references)
			//IL_011c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0132: Unknown result type (might be due to invalid IL or missing references)
			//IL_013e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0154: Unknown result type (might be due to invalid IL or missing references)
			//IL_015b: 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_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Expected O, but got Unknown
			//IL_017d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0193: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01af: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c0: Expected O, but got Unknown
			//IL_01bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c2: Expected O, but got Unknown
			DataObjects.ItemRecipe recipe = def.recipe;
			List<RequirementConfig> list = new List<RequirementConfig>();
			List<DataObjects.Ingrediant> list2 = new List<DataObjects.Ingrediant>();
			foreach (DataObjects.Ingrediant item in recipe.ingredients ?? new List<DataObjects.Ingrediant>())
			{
				if (item == null || string.IsNullOrEmpty(item.prefab))
				{
					Logger.LogWarning("Recipe for '" + modificationName + "' has an ingredient with no prefab set, it will be ignored.");
					continue;
				}
				RequirementConfig val = new RequirementConfig
				{
					Item = item.prefab,
					Amount = item.craftCost.GetValueOrDefault(),
					AmountPerLevel = item.upgradeCost.GetValueOrDefault()
				};
				if (val.Amount == 0 && val.AmountPerLevel == 0 && item.upgradeCosts != null && item.upgradeCosts.Count > 0)
				{
					val.AmountPerLevel = 1;
				}
				if (val.IsValid())
				{
					list.Add(val);
					list2.Add(item);
				}
			}
			string text = StationPrefabName(modificationName, recipe.craftedAt);
			CustomRecipe val2 = new CustomRecipe(new RecipeConfig
			{
				Name = "Recipe_" + def.prefab,
				Item = def.prefab,
				Amount = (recipe.craftAmount ?? 1),
				CraftingStation = text,
				RepairStation = (string.IsNullOrEmpty(recipe.repairAt) ? text : StationPrefabName(modificationName, recipe.repairAt)),
				MinStationLevel = (recipe.minStationLevel ?? 1),
				RequireOnlyOneIngredient = (recipe.anyOneResource == true),
				Enabled = true,
				Requirements = list.ToArray()
			});
			if ((Object)(object)val2.Recipe != (Object)null)
			{
				val2.Recipe.m_resources = RecipeFields.ApplyRequirements(val2.Recipe.m_resources, list2, null, null);
				for (int i = 0; i < list2.Count && i < val2.Recipe.m_resources.Length; i++)
				{
					if (RecipeUpgradeCosts.Register(RecipeUpgradeCosts.FromItems, val2.Recipe.m_resources[i], list2[i], "The recipe for '" + modificationName + "'"))
					{
						RecipeUpgradeCosts.ItemUsers.Add(modificationName);
					}
				}
				if (RecipeStationLevelCap.Register(RecipeStationLevelCap.FromItems, val2.Recipe, recipe.maxStationLevel, "The recipe for '" + modificationName + "'"))
				{
					RecipeStationLevelCap.ItemUsers.Add(modificationName);
				}
			}
			return val2;
		}

		private static string StationPrefabName(string modificationName, string configured)
		{
			if (string.IsNullOrEmpty(configured) || StationRef.IsNone(configured))
			{
				return null;
			}
			CraftingStation val = StationRef.Resolve(configured);
			if ((Object)(object)val != (Object)null)
			{
				return StationRef.NameOf(val);
			}
			if (configured.StartsWith("$", StringComparison.Ordinal))
			{
				Logger.LogWarning("The recipe for '" + modificationName + "' names the station " + configured + ", which matches no crafting station. Use the station's prefab name, such as piece_workbench.");
			}
			return configured;
		}

		public static void ReapplyExistingItems()
		{
			HashSet<string> hashSet = new HashSet<string>();
			List<DataObjects.TrackedItem> list = new List<DataObjects.TrackedItem>();
			foreach (KeyValuePair<string, DataObjects.ItemAddition> itemDefinition in ItemDefinitions)
			{
				DataObjects.ItemAddition def = itemDefinition.Value;
				if (def == null || string.IsNullOrEmpty(def.prefab) || def.action == DataObjects.ItemAction.Remove || def.action == DataObjects.ItemAction.Modify)
				{
					continue;
				}
				hashSet.Add(def.prefab);
				if (!CreatedPrefabs.Contains(def.prefab))
				{
					Logger.LogWarning("'" + itemDefinition.Key + "' defines a new item (" + def.prefab + ") which cannot be added while the game is running. Restart the game or return to the main menu to create it.");
					continue;
				}
				GameObject prefab = PrefabManager.Instance.GetPrefab(def.prefab);
				if ((Object)(object)prefab == (Object)null)
				{
					Logger.LogWarning(def.prefab + " was registered but can no longer be resolved, skipping.");
					continue;
				}
				try
				{
					ApplyDefinition(prefab, def);
					if (ValConfig.EnableDebugMode.Value)
					{
						Logger.LogInfo("Refreshed " + def.prefab + ".");
					}
				}
				catch (Exception arg)
				{
					Logger.LogError($"Could not refresh {def.prefab}. Error: {arg}");
				}
				list.Add(new DataObjects.TrackedItem
				{
					action = def.action,
					prefab = def.prefab,
					clonedFrom = def.clonedFrom,
					itemPrefab = prefab,
					customItem = TrackedItems.Find((DataObjects.TrackedItem item) => item.prefab == def.prefab)?.customItem,
					definition = def
				});
			}
			TrackedItems.Clear();
			TrackedItems.AddRange(list);
			foreach (string createdPrefab in CreatedPrefabs)
			{
				if (!hashSet.Contains(createdPrefab))
				{
					Logger.LogWarning(createdPrefab + " was removed from the configuration but is already registered. Restart the game to remove it.");
				}
			}
			ApplyItemModifications();
			ResolveDeferredReferences(TrackedItems);
		}

		public static void RefreshStatusEffectReferences()
		{
			foreach (DataObjects.TrackedItem trackedItem in TrackedItems)
			{
				if (trackedItem.definition != null && !((Object)(object)trackedItem.itemPrefab == (Object)null))
				{
					try
					{
						ApplyStatsOnly(trackedItem.itemPrefab, trackedItem.definition);
					}
					catch (Exception arg)
					{
						Logger.LogError($"Could not refresh {trackedItem.prefab}. Error: {arg}");
					}
				}
			}
			ApplyItemModifications(withArt: false);
			ResolveDeferredReferences(TrackedItems);
		}
	}
	internal static class ItemPieces
	{
		private const string WallStand = "itemstand";

		private const string FloorStand = "itemstandh";

		internal static GameObject Build(string key, DataObjects.PieceModification def, out GameObject source)
		{
			//IL_0319: Unknown result type (might be due to invalid IL or missing references)
			//IL_0320: Unknown result type (might be due to invalid IL or missing references)
			//IL_0325: Unknown result type (might be due to invalid IL or missing references)
			//IL_033c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0343: Unknown result type (might be due to invalid IL or missing referenc