Decompiled source of DvergrReclaimer v0.2.0

plugins/DvergrReclaimer.dll

Decompiled a day ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using DvergrReclaimer.Engine;
using DvergrReclaimer.Ledger;
using DvergrReclaimer.Logistics;
using DvergrReclaimer.Machine;
using DvergrReclaimer.Model;
using DvergrReclaimer.Pieces;
using HarmonyLib;
using Jotunn.Configs;
using Jotunn.Entities;
using Jotunn.Managers;
using Jotunn.Utils;
using Microsoft.CodeAnalysis;
using Recycle_N_Reclaim;
using Recycle_N_Reclaim.GamePatches.Recycling;
using TMPro;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: InternalsVisibleTo("DvergrReclaimer.SmokeTest")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: IgnoresAccessChecksTo("assembly_valheim")]
[assembly: AssemblyCompany("DvergrReclaimer")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.2.0.0")]
[assembly: AssemblyInformationalVersion("0.2.0+598d426f5130a7e9a88599bd6b2de118722c3fff")]
[assembly: AssemblyProduct("DvergrReclaimer")]
[assembly: AssemblyTitle("DvergrReclaimer")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.2.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace DvergrReclaimer
{
	[HarmonyPatch(typeof(Game), "Start")]
	internal static class Network
	{
		private static void Postfix()
		{
			LedgerSync.Register();
			ContainerTransfer.Register();
		}
	}
	[BepInPlugin("greatcoltini.mods.dvergrreclaimer", "Dvergr Reclaimer", "0.2.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[NetworkCompatibility(/*Could not decode attribute arguments.*/)]
	public class DvergrReclaimerPlugin : BaseUnityPlugin
	{
		public const string ModGuid = "greatcoltini.mods.dvergrreclaimer";

		public const string ModName = "Dvergr Reclaimer";

		public const string ModVersion = "0.2.0";

		internal static ManualLogSource Log;

		private readonly Harmony _harmony = new Harmony("greatcoltini.mods.dvergrreclaimer");

		private void Awake()
		{
			Log = ((BaseUnityPlugin)this).Logger;
			ReclaimerConfig.Bind(((BaseUnityPlugin)this).Config);
			if (!RnrEngine.Probe())
			{
				Log.LogError((object)("Recycle N' Reclaim is missing or incompatible, so reclaiming is disabled: " + RnrEngine.Problem));
			}
			if (EpicLootSacrifice.Probe())
			{
				Log.LogInfo((object)"Epic Loot found: items without a recipe can be sacrificed for its enchanting materials");
			}
			else if (Chainloader.PluginInfos.ContainsKey("randyknapp.mods.epicloot"))
			{
				Log.LogWarning((object)("Epic Loot is installed but its sacrifice API changed, so the Reclaimer won't sacrifice: " + EpicLootSacrifice.Problem));
			}
			ReclaimerPiece.Register();
			_harmony.PatchAll(typeof(DvergrReclaimerPlugin).Assembly);
		}

		private void OnDestroy()
		{
			_harmony.UnpatchSelf();
		}
	}
	internal enum NoRoomBehavior
	{
		DropInFront,
		Wait
	}
	internal enum RecipeGateMode
	{
		Everyone,
		Off
	}
	internal static class ReclaimerConfig
	{
		public static ConfigEntry<float> SecondsPerItem;

		public static ConfigEntry<int> ItemsPerTick;

		public static ConfigEntry<string> FuelItem;

		public static ConfigEntry<int> FuelPerItem;

		public static ConfigEntry<int> MaxFuel;

		public static ConfigEntry<bool> PullFuelFromChests;

		public static ConfigEntry<float> FuelRadius;

		public static ConfigEntry<float> OutputRadius;

		public static ConfigEntry<NoRoomBehavior> WhenNoRoom;

		public static ConfigEntry<RecipeGateMode> RecipeGate;

		public static ConfigEntry<float> StationRadius;

		public static ConfigEntry<bool> HideReclaimAllButton;

		public static ConfigEntry<bool> SacrificeWithoutRecipe;

		public static ConfigEntry<bool> SacrificeNeedsTable;

		public static void Bind(ConfigFile config)
		{
			SecondsPerItem = Bind(config, "Processing", "Seconds to reclaim an item", 30f, "Time to reclaim or sacrifice one item (or stack). A Smelter takes 30 seconds per bar.");
			ItemsPerTick = Bind(config, "Processing", "Items per catch-up tick", 4, "Most items finished in one second while catching up on time the area spent unloaded.");
			FuelItem = Bind(config, "Fuel", "Fuel item", "Coal", "Prefab name of the fuel. Fuel in the crate is used first.");
			FuelPerItem = Bind(config, "Fuel", "Fuel per item", 1, "Fuel used per item reclaimed. 0 disables fuel.");
			MaxFuel = Bind(config, "Fuel", "Max fuel", 40, "Fuel the Reclaimer can hold.");
			PullFuelFromChests = Bind(config, "Fuel", "Pull fuel from chests", value: true, "Take fuel from nearby chests when an item is waiting and fuel has run out.");
			FuelRadius = Bind(config, "Fuel", "Fuel radius", 8f, "How far away (metres) chests can supply fuel.");
			OutputRadius = Bind(config, "Output", "Output radius", 10f, "How far away (metres) chests can receive reclaimed materials.");
			WhenNoRoom = Bind(config, "Output", "When no chest has room", NoRoomBehavior.DropInFront, "DropInFront: results no nearby chest can take drop out of the front of the Reclaimer, like a Smelter's bars. Wait: they stay inside and it pauses when full, until a chest has room.");
			RecipeGate = Bind(config, "Recipes", "Recipe gate", RecipeGateMode.Everyone, "Everyone: only recipes someone on the world has discovered. Off: any recipe.");
			StationRadius = Bind(config, "Recipes", "Station radius", 20f, "With Recycle N' Reclaim's exact-station rule on, the matching crafting station must be this close (metres).");
			HideReclaimAllButton = Bind(config, "Interface", "Hide Reclaim all on the crate", value: true, "Hide Recycle N' Reclaim's container button on the Reclaimer's crate, which would bypass fuel and the recipe gate.");
			SacrificeWithoutRecipe = Bind(config, "Epic Loot", "Sacrifice items without a recipe", value: true, "With Epic Loot installed, items that have no crafting recipe but that Epic Loot's Enchanting Table accepts as a sacrifice (trophies, boss items) are sacrificed for its products, such as a Runestone for the Elder's trophy.");
			SacrificeNeedsTable = Bind(config, "Epic Loot", "Sacrifice needs an Enchanting Table", value: true, "Sacrificing needs an Enchanting Table with its Sacrifice feature unlocked within the station radius. The table's Sacrifice level gives the same chance of double products as sacrificing at the table.");
		}

		private static ConfigEntry<T> Bind<T>(ConfigFile config, string section, string key, T value, string description)
		{
			//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_000d: Expected O, but got Unknown
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Expected O, but got Unknown
			ConfigurationManagerAttributes val = new ConfigurationManagerAttributes
			{
				IsAdminOnly = true
			};
			return config.Bind<T>(section, key, value, new ConfigDescription(description, (AcceptableValueBase)null, new object[1] { val }));
		}
	}
}
namespace DvergrReclaimer.UI
{
	[HarmonyPatch(typeof(InventoryGui), "Update")]
	internal static class ReclaimerPanel
	{
		internal const int Columns = 8;

		internal const int FuelColumn = 0;

		internal const int ProcessingColumn = 2;

		internal const int FirstYieldColumn = 4;

		private const float Gap = 8f;

		private const float HeaderHeight = 20f;

		private const float StatusMargin = 24f;

		private static readonly Inventory Display = new Inventory("DvergrReclaimerPanel", (Sprite)null, 8, 1);

		private static RectTransform s_panel;

		private static InventoryGrid s_grid;

		private static TMP_Text s_status;

		private static TMP_Text s_processingHeader;

		private static TMP_Text s_yieldHeader;

		private static bool s_broken;

		internal static RectTransform Panel => s_panel;

		internal static Inventory Contents => Display;

		internal static string StatusText
		{
			get
			{
				if (!((Object)(object)s_status != (Object)null))
				{
					return null;
				}
				return s_status.text;
			}
		}

		internal static string SlotText(int column)
		{
			if (!((Object)(object)s_grid != (Object)null))
			{
				return null;
			}
			return s_grid.m_elements[column].m_amount.text;
		}

		private static void Postfix(InventoryGui __instance)
		{
			if (s_broken)
			{
				return;
			}
			try
			{
				object obj;
				if (!InventoryGui.IsVisible() || !__instance.IsContainerOpen())
				{
					obj = null;
				}
				else
				{
					Container currentContainer = __instance.m_currentContainer;
					obj = ((currentContainer != null) ? ((Component)currentContainer).GetComponent<ReclaimerMachine>() : null);
				}
				ReclaimerMachine reclaimerMachine = (ReclaimerMachine)obj;
				if ((Object)(object)reclaimerMachine == (Object)null)
				{
					if ((Object)(object)s_panel != (Object)null)
					{
						((Component)s_panel).gameObject.SetActive(false);
					}
					return;
				}
				if ((Object)(object)s_panel == (Object)null)
				{
					Create(__instance);
				}
				((Component)s_panel).gameObject.SetActive(true);
				ReclaimerMachine.PanelState view = reclaimerMachine.PanelView(Display);
				s_status.text = view.Status;
				s_processingHeader.text = view.ProcessingHeader;
				s_yieldHeader.text = view.YieldHeader;
				s_grid.UpdateInventory(Display, (Player)null, (ItemData)null);
				ShowAmounts(view);
				PlaceBelow(__instance.m_container);
			}
			catch (Exception arg)
			{
				s_broken = true;
				DvergrReclaimerPlugin.Log.LogError((object)$"Reclaimer panel disabled: {arg}");
				if ((Object)(object)s_panel != (Object)null)
				{
					((Component)s_panel).gameObject.SetActive(false);
				}
			}
		}

		private static void ShowAmounts(ReclaimerMachine.PanelState view)
		{
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			foreach (InventoryElement element in s_grid.m_elements)
			{
				ItemData itemAt = Display.GetItemAt(element.Position.x, element.Position.y);
				if (itemAt != null)
				{
					((Behaviour)element.m_amount).enabled = true;
					((Behaviour)element.m_quality).enabled = false;
					if (element.Position.x == 0)
					{
						element.m_amount.text = view.FuelAmount;
					}
					else if (element.Position.x == 2)
					{
						element.m_amount.text = view.Progress;
					}
					else
					{
						element.m_amount.text = $"×{itemAt.m_stack}";
					}
				}
			}
		}

		private static void Create(InventoryGui gui)
		{
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Expected O, but got Unknown
			//IL_01ab: Unknown result type (might be due to invalid IL or missing references)
			RectTransform container = gui.m_container;
			s_panel = (RectTransform)Object.Instantiate<GameObject>(((Component)container).gameObject, ((Transform)container).parent).transform;
			((Object)s_panel).name = "DvergrReclaimerPanel";
			s_grid = ((Component)s_panel).GetComponentsInChildren<InventoryGrid>(true).First();
			s_grid.m_onSelected = delegate
			{
			};
			s_grid.m_onRightClick = delegate
			{
			};
			s_grid.m_onReleased = delegate
			{
			};
			s_grid.m_onEnter = delegate
			{
			};
			s_grid.CanDropDragOntoItem = (ItemData item) => false;
			s_grid.OnMoveToUpperInventoryGrid = delegate
			{
			};
			s_grid.OnMoveToLowerInventoryGrid = delegate
			{
			};
			s_grid.OnSetTouchSelection = delegate
			{
			};
			foreach (Transform item in (Transform)s_grid.m_gridRoot)
			{
				Object.Destroy((Object)(object)((Component)item).gameObject);
			}
			s_status = ((Component)FindCopy((Transform)(object)container, (Transform)(object)s_panel, gui.m_containerName.transform)).GetComponent<TMP_Text>();
			s_status.enableAutoSizing = true;
			s_status.fontSizeMax = s_status.fontSize;
			s_status.fontSizeMin = Mathf.Min(12f, s_status.fontSize);
			s_status.textWrappingMode = (TextWrappingModes)0;
			s_status.overflowMode = (TextOverflowModes)1;
			Object.Destroy((Object)(object)((Component)FindCopy((Transform)(object)container, (Transform)(object)s_panel, gui.m_containerWeight.transform.parent)).gameObject);
			foreach (Button item2 in from b in ((Component)s_panel).GetComponentsInChildren<Button>(true)
				where !((Component)b).transform.IsChildOf(((Component)s_grid).transform)
				select b)
			{
				Object.Destroy((Object)(object)((Component)item2).gameObject);
			}
			foreach (TMP_Text item3 in from t in ((Component)s_panel).GetComponentsInChildren<TMP_Text>(true)
				where (Object)(object)t != (Object)(object)s_status && !t.transform.IsChildOf(((Component)s_grid).transform)
				select t)
			{
				((Component)item3).gameObject.SetActive(false);
			}
			Scrollbar[] componentsInChildren = ((Component)s_panel).GetComponentsInChildren<Scrollbar>(true);
			for (int num = 0; num < componentsInChildren.Length; num++)
			{
				((Component)componentsInChildren[num]).gameObject.SetActive(false);
			}
			s_grid.UpdateInventory(Display, (Player)null, (ItemData)null);
			FitHeightToOneRow();
			StretchAcrossTop(s_status.rectTransform);
			Header(0, "Fuel");
			s_processingHeader = Header(2, "Reclaiming");
			s_yieldHeader = Header(4, "Produces", alignLeft: true);
		}

		private static Transform FindCopy(Transform sourceRoot, Transform copyRoot, Transform descendant)
		{
			string text = PathFrom(sourceRoot, descendant);
			return copyRoot.Find(text) ?? throw new InvalidOperationException("Copied panel has no " + text);
		}

		private static string PathFrom(Transform root, Transform target)
		{
			string text = ((Object)target).name;
			Transform parent = target.parent;
			while ((Object)(object)parent != (Object)null && (Object)(object)parent != (Object)(object)root)
			{
				text = ((Object)parent).name + "/" + text;
				parent = parent.parent;
			}
			return text;
		}

		private static TMP_Text Header(int column, string text, bool alignLeft = false)
		{
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Expected O, but got Unknown
			//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0119: Unknown result type (might be due to invalid IL or missing references)
			//IL_0147: Unknown result type (might be due to invalid IL or missing references)
			//IL_0128: Unknown result type (might be due to invalid IL or missing references)
			//IL_012f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0134: 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)
			TMP_Text component = Object.Instantiate<GameObject>(((Component)s_status).gameObject, (Transform)(object)s_panel).GetComponent<TMP_Text>();
			((Object)component).name = "DvergrReclaimerHeader";
			((Component)component).gameObject.SetActive(true);
			component.text = text;
			component.enableAutoSizing = false;
			component.fontSize = Mathf.Max(12f, s_status.fontSizeMax * 0.6f);
			component.textWrappingMode = (TextWrappingModes)0;
			component.overflowMode = (TextOverflowModes)0;
			component.alignment = (TextAlignmentOptions)(alignLeft ? 257 : 258);
			((Graphic)component).raycastTarget = false;
			RectTransform val = (RectTransform)((Component)s_grid.m_elements[column]).transform;
			Vector3[] array = (Vector3[])(object)new Vector3[4];
			val.GetWorldCorners(array);
			RectTransform rectTransform = component.rectTransform;
			Vector2 val2 = default(Vector2);
			((Vector2)(ref val2))..ctor(0.5f, 0.5f);
			rectTransform.anchorMax = val2;
			rectTransform.anchorMin = val2;
			rectTransform.pivot = new Vector2(alignLeft ? 0f : 0.5f, 1f);
			Rect rect = val.rect;
			rectTransform.sizeDelta = new Vector2(((Rect)(ref rect)).width * (alignLeft ? 4f : 2f), 20f);
			((Transform)rectTransform).position = (alignLeft ? array[0] : ((array[0] + array[3]) / 2f));
			return component;
		}

		private static void FitHeightToOneRow()
		{
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
			Canvas.ForceUpdateCanvases();
			Vector3[] array = (Vector3[])(object)new Vector3[4];
			Vector3[] array2 = (Vector3[])(object)new Vector3[4];
			s_panel.GetWorldCorners(array);
			((RectTransform)((Component)s_grid.m_elements[0]).transform).GetWorldCorners(array2);
			float y = ((Transform)s_panel).lossyScale.y;
			float num = (array[1].y - array2[0].y) / y + 20f + s_grid.m_elementSpace * 0.25f;
			Vector3 val = array[1];
			s_panel.pivot = new Vector2(s_panel.pivot.x, 1f);
			s_panel.SetSizeWithCurrentAnchors((Axis)1, num);
			s_panel.GetWorldCorners(array);
			RectTransform obj = s_panel;
			((Transform)obj).position = ((Transform)obj).position + (val - array[1]);
		}

		private static void StretchAcrossTop(RectTransform rect)
		{
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
			((Transform)rect).SetParent((Transform)(object)s_panel, true);
			Vector3[] array = (Vector3[])(object)new Vector3[4];
			Vector3[] array2 = (Vector3[])(object)new Vector3[4];
			rect.GetWorldCorners(array);
			s_panel.GetWorldCorners(array2);
			float y = ((Transform)s_panel).lossyScale.y;
			float num = (array2[1].y - array[1].y) / y;
			float num2 = (array[1].y - array[0].y) / y;
			rect.anchorMin = new Vector2(0f, 1f);
			rect.anchorMax = new Vector2(1f, 1f);
			rect.pivot = new Vector2(0.5f, 1f);
			rect.sizeDelta = new Vector2(-48f, num2);
			rect.anchoredPosition = new Vector2(0f, 0f - num);
		}

		private static void PlaceBelow(RectTransform container)
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			Vector3[] array = (Vector3[])(object)new Vector3[4];
			Vector3[] array2 = (Vector3[])(object)new Vector3[4];
			container.GetWorldCorners(array);
			s_panel.GetWorldCorners(array2);
			float num = 8f * ((Transform)s_panel).lossyScale.y;
			Vector3 val = default(Vector3);
			((Vector3)(ref val))..ctor(array[0].x, array[0].y - num, array2[1].z);
			RectTransform obj = s_panel;
			((Transform)obj).position = ((Transform)obj).position + (val - array2[1]);
		}
	}
}
namespace DvergrReclaimer.Pieces
{
	internal static class ReclaimerPiece
	{
		public const string PrefabName = "piece_dvergr_reclaimer";

		public const int CrateWidth = 8;

		public const int CrateHeight = 12;

		private const string NameToken = "piece_dvergr_reclaimer";

		private const string DescriptionToken = "piece_dvergr_reclaimer_description";

		internal static void Register()
		{
			CustomLocalization localization = LocalizationManager.Instance.GetLocalization();
			string text = "English";
			localization.AddTranslation(ref text, new Dictionary<string, string>
			{
				["piece_dvergr_reclaimer"] = "Dvergr Reclaimer",
				["piece_dvergr_reclaimer_description"] = "An iron furnace that breaks down the gear in its crate into reclaimed materials."
			});
			PrefabManager.OnVanillaPrefabsAvailable += Create;
		}

		private static void Create()
		{
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Expected O, but got Unknown
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c4: Expected O, but got Unknown
			PrefabManager.OnVanillaPrefabsAvailable -= Create;
			try
			{
				GameObject val = BuildPrefab();
				PieceConfig val2 = new PieceConfig();
				val2.Name = "$piece_dvergr_reclaimer";
				val2.Description = "$piece_dvergr_reclaimer_description";
				val2.PieceTable = PieceTables.Hammer;
				val2.Category = PieceCategories.Crafting;
				val2.Usage = new string[1] { PieceUsages.Crafting };
				val2.CraftingStation = CraftingStations.Forge;
				val2.Icon = LoadSprite("icon_piece.png");
				PieceConfig val3 = val2;
				val3.AddRequirement("Iron", 10, true);
				val3.AddRequirement("Stone", 10, true);
				val3.AddRequirement("SurtlingCore", 2, true);
				PieceManager.Instance.AddPiece(new CustomPiece(val, false, val3));
				DvergrReclaimerPlugin.Log.LogInfo((object)"Registered piece_dvergr_reclaimer");
			}
			catch (Exception arg)
			{
				DvergrReclaimerPlugin.Log.LogError((object)string.Format("Could not create {0}: {1}", "piece_dvergr_reclaimer", arg));
			}
		}

		private static GameObject BuildPrefab()
		{
			//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
			GameObject prefab = PrefabManager.Instance.GetPrefab("piece_chest");
			GameObject prefab2 = PrefabManager.Instance.GetPrefab("forge");
			Material template = RuntimeModel.FindVanillaPieceMaterial((Func<string, GameObject>)PrefabManager.Instance.GetPrefab, new string[1] { "piece_chest" }) ?? throw new InvalidOperationException("No vanilla Custom/Piece material to copy");
			GameObject val = PrefabManager.Instance.CreateEmptyPrefab("piece_dvergr_reclaimer", true);
			Object.DestroyImmediate((Object)(object)val.GetComponent<Collider>());
			Object.DestroyImmediate((Object)(object)val.GetComponent<MeshRenderer>());
			Object.DestroyImmediate((Object)(object)val.GetComponent<MeshFilter>());
			RuntimeModel.Read(EmbeddedAsset("dvergr_reclaimer.dvmodel")).BuildInto(val, template, EmbeddedAsset);
			val.AddComponent<Piece>().m_placeEffect = prefab2.GetComponent<Piece>().m_placeEffect;
			WearNTear component = prefab2.GetComponent<WearNTear>();
			WearNTear obj = val.AddComponent<WearNTear>();
			obj.m_materialType = (MaterialType)2;
			obj.m_health = 1500f;
			obj.m_hitEffect = component.m_hitEffect;
			obj.m_destroyedEffect = component.m_destroyedEffect;
			obj.m_switchEffect = component.m_switchEffect;
			Container component2 = prefab.GetComponent<Container>();
			Container obj2 = val.AddComponent<Container>();
			obj2.m_name = "$piece_dvergr_reclaimer";
			obj2.m_width = 8;
			obj2.m_height = 12;
			obj2.m_bkg = component2.m_bkg;
			obj2.m_openEffects = component2.m_openEffects;
			obj2.m_closeEffects = component2.m_closeEffects;
			obj2.m_checkGuardStone = true;
			AddWorkingEffects(val);
			val.AddComponent<ReclaimerEffects>();
			val.AddComponent<ReclaimerMachine>();
			return val;
		}

		private static void AddWorkingEffects(GameObject prefab)
		{
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d1: 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_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0133: Unknown result type (might be due to invalid IL or missing references)
			//IL_0143: Unknown result type (might be due to invalid IL or missing references)
			//IL_0153: Unknown result type (might be due to invalid IL or missing references)
			//IL_015d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0190: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fa: Expected O, but got Unknown
			//IL_0222: Unknown result type (might be due to invalid IL or missing references)
			//IL_0232: Unknown result type (might be due to invalid IL or missing references)
			//IL_0237: Unknown result type (might be due to invalid IL or missing references)
			//IL_0268: Unknown result type (might be due to invalid IL or missing references)
			//IL_0278: Unknown result type (might be due to invalid IL or missing references)
			//IL_027d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0287: Unknown result type (might be due to invalid IL or missing references)
			//IL_0291: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_02bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_02fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_0300: Unknown result type (might be due to invalid IL or missing references)
			//IL_030a: Unknown result type (might be due to invalid IL or missing references)
			//IL_030f: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c0: Unknown result type (might be due to invalid IL or missing references)
			GameObject prefab2 = PrefabManager.Instance.GetPrefab("smelter");
			Transform source = ((prefab2 != null) ? prefab2.transform.Find("_enabled") : null) ?? throw new InvalidOperationException("The vanilla Smelter has no _enabled effects");
			Bounds bounds = default(Bounds);
			bool flag = true;
			MeshFilter[] componentsInChildren = prefab.GetComponentsInChildren<MeshFilter>(true);
			for (int i = 0; i < componentsInChildren.Length; i++)
			{
				Bounds bounds2 = componentsInChildren[i].sharedMesh.bounds;
				if (flag)
				{
					bounds = bounds2;
					flag = false;
				}
				else
				{
					((Bounds)(ref bounds)).Encapsulate(bounds2);
				}
			}
			Vector3 val = default(Vector3);
			((Vector3)(ref val))..ctor(((Bounds)(ref bounds)).center.x, ((Bounds)(ref bounds)).min.y + ((Bounds)(ref bounds)).size.y * 0.4f, ((Bounds)(ref bounds)).min.z);
			List<Vector3> list = (from v in prefab.GetComponentsInChildren<MeshFilter>(true).SelectMany((MeshFilter f) => f.sharedMesh.vertices)
				where v.y > ((Bounds)(ref bounds)).max.y - ((Bounds)(ref bounds)).size.y * 0.03f
				select v).ToList();
			Vector3 val2 = ((list.Count > 0) ? new Vector3(list.Average((Vector3 v) => v.x), ((Bounds)(ref bounds)).max.y, list.Average((Vector3 v) => v.z)) : new Vector3(((Bounds)(ref bounds)).center.x, ((Bounds)(ref bounds)).max.y, ((Bounds)(ref bounds)).center.z));
			GameObject val3 = new GameObject("feed_point");
			val3.transform.SetParent(prefab.transform, false);
			val3.transform.localPosition = val;
			GameObject val4 = new GameObject("_working");
			val4.transform.SetParent(prefab.transform, false);
			val4.SetActive(false);
			Light component = ((Component)CopyEffect(source, "Point light", val4.transform, val + new Vector3(0f, 0.2f, -0.3f))).GetComponent<Light>();
			component.intensity = 3f;
			component.range = 3f;
			CopyEffect(source, "flames", val4.transform, val + new Vector3(0f, -0.15f, -0.05f)).localScale = Vector3.one * 0.5f;
			CopyEffect(source, "flare (1)", val4.transform, val + new Vector3(0f, 0f, -0.05f));
			CopyEffect(source, "sfx_fire_loop", val4.transform, val);
			CopyEffect(source, "smoke (1)", val4.transform, val2);
			CopyEffect(source, "SmokeSpawner", val4.transform, val2 + Vector3.up * 0.3f);
		}

		private static Transform CopyEffect(Transform source, string name, Transform parent, Vector3 position)
		{
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			Transform transform = Object.Instantiate<GameObject>(((Component)(source.Find(name) ?? throw new InvalidOperationException("The vanilla Smelter has no _enabled/" + name))).gameObject, parent, false).transform;
			((Object)transform).name = name;
			transform.localPosition = position;
			return transform;
		}

		private static Sprite LoadSprite(string name)
		{
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Expected O, but got Unknown
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false);
			RuntimeModel.LoadImage(val, EmbeddedAsset(name), arg3: false);
			return Sprite.Create(val, new Rect(0f, 0f, (float)((Texture)val).width, (float)((Texture)val).height), new Vector2(0.5f, 0.5f));
		}

		internal static byte[] EmbeddedAsset(string name)
		{
			using Stream stream = typeof(ReclaimerPiece).Assembly.GetManifestResourceStream(name) ?? throw new FileNotFoundException("Embedded asset not found", name);
			using MemoryStream memoryStream = new MemoryStream();
			stream.CopyTo(memoryStream);
			return memoryStream.ToArray();
		}
	}
}
namespace DvergrReclaimer.Model
{
	internal sealed class RuntimeModel
	{
		internal sealed class Part
		{
			public string Name;

			public int Lod;

			public Vector3[] Positions;

			public Vector3[] Normals;

			public Vector2[] Uvs;

			public readonly List<(int Material, int[] Indices)> Submeshes = new List<(int, int[])>();
		}

		internal sealed class ModelMaterial
		{
			public string Name;

			public Color Color;

			public Color Emission;

			public float EmissionStrength;

			public float Metallic;

			public float Roughness;

			public readonly Dictionary<string, string> Textures = new Dictionary<string, string>();
		}

		private const int PieceLayer = 10;

		private static readonly float[] LodHeights = new float[4] { 0.4f, 0.15f, 0.03f, 0.01f };

		public readonly List<Part> Parts = new List<Part>();

		public readonly List<ModelMaterial> Materials = new List<ModelMaterial>();

		public readonly List<(string Name, Vector3 Center, Vector3 Size)> Boxes = new List<(string, Vector3, Vector3)>();

		public readonly List<(string Name, Vector3 Position)> Points = new List<(string, Vector3)>();

		internal static readonly Func<Texture2D, byte[], bool, bool> LoadImage = (Func<Texture2D, byte[], bool, bool>)Delegate.CreateDelegate(typeof(Func<Texture2D, byte[], bool, bool>), typeof(ImageConversion).GetMethod("LoadImage", new Type[3]
		{
			typeof(Texture2D),
			typeof(byte[]),
			typeof(bool)
		}));

		public static RuntimeModel Read(byte[] data)
		{
			//IL_01c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_0279: Unknown result type (might be due to invalid IL or missing references)
			//IL_027f: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b4: Unknown result type (might be due to invalid IL or missing references)
			using BinaryReader binaryReader = new BinaryReader(new MemoryStream(data));
			if (Encoding.ASCII.GetString(binaryReader.ReadBytes(4)) != "DVMD")
			{
				throw new InvalidDataException("Not a .dvmodel file");
			}
			int num = binaryReader.ReadInt32();
			if (num != 1)
			{
				throw new InvalidDataException($"Unsupported .dvmodel version {num}");
			}
			RuntimeModel runtimeModel = new RuntimeModel();
			int i = 0;
			for (int num2 = binaryReader.ReadInt32(); i < num2; i++)
			{
				Part part = new Part
				{
					Name = ReadString(binaryReader),
					Lod = binaryReader.ReadInt32()
				};
				int count = binaryReader.ReadInt32();
				part.Positions = ReadArray(binaryReader, count, ReadVector3);
				part.Normals = ReadArray(binaryReader, count, ReadVector3);
				part.Uvs = ReadArray(binaryReader, count, (Func<BinaryReader, Vector2>)((BinaryReader r) => new Vector2(r.ReadSingle(), r.ReadSingle())));
				int num3 = 0;
				for (int num4 = binaryReader.ReadInt32(); num3 < num4; num3++)
				{
					int item = binaryReader.ReadInt32();
					part.Submeshes.Add((item, ReadArray(binaryReader, binaryReader.ReadInt32(), (BinaryReader r) => r.ReadInt32())));
				}
				runtimeModel.Parts.Add(part);
			}
			int num5 = 0;
			for (int num6 = binaryReader.ReadInt32(); num5 < num6; num5++)
			{
				ModelMaterial modelMaterial = new ModelMaterial
				{
					Name = ReadString(binaryReader),
					Color = new Color(binaryReader.ReadSingle(), binaryReader.ReadSingle(), binaryReader.ReadSingle(), binaryReader.ReadSingle()),
					Emission = new Color(binaryReader.ReadSingle(), binaryReader.ReadSingle(), binaryReader.ReadSingle()),
					EmissionStrength = binaryReader.ReadSingle(),
					Metallic = binaryReader.ReadSingle(),
					Roughness = binaryReader.ReadSingle()
				};
				int num7 = 0;
				for (int num8 = binaryReader.ReadInt32(); num7 < num8; num7++)
				{
					modelMaterial.Textures[ReadString(binaryReader)] = ReadString(binaryReader);
				}
				runtimeModel.Materials.Add(modelMaterial);
			}
			int num9 = 0;
			for (int num10 = binaryReader.ReadInt32(); num9 < num10; num9++)
			{
				runtimeModel.Boxes.Add((ReadString(binaryReader), ReadVector3(binaryReader), ReadVector3(binaryReader)));
			}
			int num11 = 0;
			for (int num12 = binaryReader.ReadInt32(); num11 < num12; num11++)
			{
				runtimeModel.Points.Add((ReadString(binaryReader), ReadVector3(binaryReader)));
			}
			return runtimeModel;
		}

		public GameObject Build(string name, Material template, Func<string, byte[]> loadFile)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Expected O, but got Unknown
			GameObject val = new GameObject(name);
			val.SetActive(false);
			BuildInto(val, template, loadFile);
			return val;
		}

		public void BuildInto(GameObject root, Material template, Func<string, byte[]> loadFile)
		{
			//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_0072: Expected O, but got Unknown
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Expected O, but got Unknown
			//IL_0232: Unknown result type (might be due to invalid IL or missing references)
			//IL_0237: Unknown result type (might be due to invalid IL or missing references)
			//IL_023f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0259: Unknown result type (might be due to invalid IL or missing references)
			//IL_0265: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b6: Expected O, but got Unknown
			//IL_02d2: Unknown result type (might be due to invalid IL or missing references)
			root.layer = 10;
			Material[] materials = Materials.Select((ModelMaterial m) => CreateMaterial(m, template, loadFile)).ToArray();
			SortedDictionary<int, List<Renderer>> sortedDictionary = new SortedDictionary<int, List<Renderer>>();
			foreach (Part part in Parts)
			{
				GameObject val = new GameObject(part.Name)
				{
					layer = 10
				};
				val.transform.SetParent(root.transform, false);
				Mesh val2 = new Mesh
				{
					name = part.Name,
					indexFormat = (IndexFormat)(part.Positions.Length > 65535)
				};
				val2.SetVertices(part.Positions);
				val2.SetNormals(part.Normals);
				val2.SetUVs(0, part.Uvs);
				val2.subMeshCount = part.Submeshes.Count;
				for (int num = 0; num < part.Submeshes.Count; num++)
				{
					val2.SetTriangles(part.Submeshes[num].Indices, num);
				}
				val2.RecalculateBounds();
				val2.RecalculateTangents();
				val.AddComponent<MeshFilter>().sharedMesh = val2;
				MeshRenderer val3 = val.AddComponent<MeshRenderer>();
				((Renderer)val3).sharedMaterials = part.Submeshes.Select(((int Material, int[] Indices) s) => materials[s.Material]).ToArray();
				if (!sortedDictionary.TryGetValue(part.Lod, out var value))
				{
					value = (sortedDictionary[part.Lod] = new List<Renderer>());
				}
				value.Add((Renderer)(object)val3);
			}
			if (sortedDictionary.Count > 1)
			{
				LOD[] lODs = ((IEnumerable<List<Renderer>>)sortedDictionary.Values).Select((Func<List<Renderer>, int, LOD>)((List<Renderer> renderers, int i) => new LOD(LodHeights[Math.Min(i, LodHeights.Length - 1)], renderers.ToArray()))).ToArray();
				LODGroup obj = root.AddComponent<LODGroup>();
				obj.SetLODs(lODs);
				obj.RecalculateBounds();
			}
			foreach (var box in Boxes)
			{
				GameObject val4 = new GameObject(box.Name)
				{
					layer = 10
				};
				val4.transform.SetParent(root.transform, false);
				BoxCollider obj2 = val4.AddComponent<BoxCollider>();
				obj2.center = box.Center;
				obj2.size = box.Size;
			}
			foreach (var point in Points)
			{
				GameObject val5 = new GameObject(point.Name)
				{
					layer = 10
				};
				val5.transform.SetParent(root.transform, false);
				val5.transform.localPosition = point.Position;
				if (point.Name.StartsWith("snappoint", StringComparison.Ordinal))
				{
					val5.tag = "snappoint";
				}
			}
		}

		public static Material FindVanillaPieceMaterial(Func<string, GameObject> getPrefab, params string[] prefabNames)
		{
			foreach (string arg in prefabNames)
			{
				GameObject val = getPrefab(arg);
				if (!((Object)(object)val == (Object)null))
				{
					Material val2 = val.GetComponentsInChildren<Renderer>(true).SelectMany((Renderer r) => r.sharedMaterials).FirstOrDefault((Func<Material, bool>)((Material m) => (Object)(object)m != (Object)null && (Object)(object)m.shader != (Object)null && ((Object)m.shader).name == "Custom/Piece"));
					if ((Object)(object)val2 != (Object)null)
					{
						return val2;
					}
				}
			}
			return null;
		}

		internal static Texture2D LoadTexture(byte[] data, bool linear)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Expected O, but got Unknown
			Texture2D val = new Texture2D(2, 2, (TextureFormat)4, true, linear);
			if (!LoadImage(val, data, arg3: false))
			{
				throw new InvalidDataException("Unreadable texture");
			}
			((Texture)val).filterMode = (FilterMode)0;
			if (((Texture)val).width % 4 == 0 && ((Texture)val).height % 4 == 0)
			{
				val.Compress(true);
			}
			val.Apply(false, true);
			return val;
		}

		private static Material CreateMaterial(ModelMaterial source, Material template, Func<string, byte[]> loadFile)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Expected O, but got Unknown
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Invalid comparison between Unknown and I4
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Invalid comparison between Unknown and I4
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_01be: Unknown result type (might be due to invalid IL or missing references)
			//IL_019a: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ac: Unknown result type (might be due to invalid IL or missing references)
			Material val = new Material(template)
			{
				name = source.Name
			};
			Shader shader = val.shader;
			for (int i = 0; i < shader.GetPropertyCount(); i++)
			{
				if ((int)shader.GetPropertyType(i) == 4)
				{
					val.SetTexture(shader.GetPropertyNameId(i), (Texture)null);
				}
			}
			bool linearSpace = (int)QualitySettings.activeColorSpace == 1;
			val.SetColor("_Color", Authored(source.Color));
			if (source.Textures.TryGetValue("baseColor", out var value))
			{
				val.SetTexture("_MainTex", (Texture)(object)LoadTexture(loadFile(value), linear: false));
			}
			if (source.Textures.TryGetValue("normal", out var value2))
			{
				val.SetTexture("_BumpMap", (Texture)(object)LoadTexture(loadFile(value2), linear: true));
				val.EnableKeyword("_NORMALMAP");
			}
			else
			{
				val.DisableKeyword("_NORMALMAP");
			}
			if (source.Textures.TryGetValue("metallicGloss", out var value3))
			{
				val.SetTexture("_MetallicTex", (Texture)(object)LoadTexture(loadFile(value3), linear: true));
				val.EnableKeyword("_METALLICGLOSSMAP");
			}
			else
			{
				val.DisableKeyword("_METALLICGLOSSMAP");
				val.SetFloat("_Metallic", source.Metallic);
				val.SetFloat("_Glossiness", 1f - source.Roughness);
			}
			if (source.EmissionStrength > 0f)
			{
				string value4;
				Texture2D val2 = (source.Textures.TryGetValue("emission", out value4) ? LoadTexture(loadFile(value4), linear: false) : Texture2D.whiteTexture);
				val.SetTexture("_EmissionMap", (Texture)(object)val2);
				val.SetColor("_EmissionColor", Authored(source.Emission * source.EmissionStrength));
			}
			else
			{
				val.SetColor("_EmissionColor", Color.black);
			}
			return val;
			Color Authored(Color linear)
			{
				//IL_000c: Unknown result type (might be due to invalid IL or missing references)
				//IL_0008: Unknown result type (might be due to invalid IL or missing references)
				if (!linearSpace)
				{
					return linear;
				}
				return ((Color)(ref linear)).gamma;
			}
		}

		private static string ReadString(BinaryReader reader)
		{
			return Encoding.UTF8.GetString(reader.ReadBytes(reader.ReadInt32()));
		}

		private static Vector3 ReadVector3(BinaryReader reader)
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			return new Vector3(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle());
		}

		private static T[] ReadArray<T>(BinaryReader reader, int count, Func<BinaryReader, T> read)
		{
			T[] array = new T[count];
			for (int i = 0; i < count; i++)
			{
				array[i] = read(reader);
			}
			return array;
		}
	}
}
namespace DvergrReclaimer.Machine
{
	internal enum ReclaimerEffect
	{
		ItemAdded,
		FuelAdded,
		Produced
	}
	internal sealed class ReclaimerEffects : MonoBehaviour
	{
		internal const string WorkingObjectName = "_working";

		internal const string FeedPointName = "feed_point";

		private static readonly Dictionary<GameObject, Vector3> BakedOffsets = new Dictionary<GameObject, Vector3>();

		private static Smelter s_smelter;

		private ZNetView _nview;

		private GameObject _working;

		private Transform _outputPoint;

		private Transform _feedPoint;

		internal readonly int[] Played = new int[3];

		internal bool WorkingShown
		{
			get
			{
				if ((Object)(object)_working != (Object)null)
				{
					return _working.activeSelf;
				}
				return false;
			}
		}

		private void Awake()
		{
			_nview = ((Component)this).GetComponent<ZNetView>();
			Transform obj = ((Component)this).transform.Find("_working");
			_working = ((obj != null) ? ((Component)obj).gameObject : null);
			_outputPoint = ((Component)this).transform.Find("output_point") ?? ((Component)this).transform;
			_feedPoint = ((Component)this).transform.Find("feed_point") ?? ((Component)this).transform;
			if ((Object)(object)_nview != (Object)null && _nview.GetZDO() != null)
			{
				((MonoBehaviour)this).InvokeRepeating("UpdateWorking", 0.2f, 0.5f);
			}
		}

		private void UpdateWorking()
		{
			if (!((Object)(object)_working == (Object)null) && !((Object)(object)_nview == (Object)null) && _nview.IsValid())
			{
				bool flag = _nview.GetZDO().GetInt("dvergrreclaimer_state", 0) == 1;
				if (_working.activeSelf != flag)
				{
					_working.SetActive(flag);
				}
			}
		}

		internal void Play(ReclaimerEffect effect)
		{
			Played[(int)effect]++;
			if ((Object)(object)s_smelter == (Object)null)
			{
				ZNetScene instance = ZNetScene.instance;
				object obj;
				if (instance == null)
				{
					obj = null;
				}
				else
				{
					GameObject prefab = instance.GetPrefab("smelter");
					obj = ((prefab != null) ? prefab.GetComponent<Smelter>() : null);
				}
				s_smelter = (Smelter)obj;
				if ((Object)(object)s_smelter == (Object)null)
				{
					return;
				}
			}
			switch (effect)
			{
			case ReclaimerEffect.ItemAdded:
				Create(s_smelter.m_oreAddedEffects, _feedPoint);
				break;
			case ReclaimerEffect.FuelAdded:
				Create(s_smelter.m_fuelAddedEffects, _feedPoint);
				break;
			case ReclaimerEffect.Produced:
				Create(s_smelter.m_produceEffects, _outputPoint);
				break;
			}
		}

		private void Create(EffectList effects, Transform at)
		{
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			EffectData[] effectPrefabs = effects.m_effectPrefabs;
			foreach (EffectData val in effectPrefabs)
			{
				if (val.m_enabled && !((Object)(object)val.m_prefab == (Object)null))
				{
					Vector3 val2 = BakedOffset(val.m_prefab);
					Object.Instantiate<GameObject>(val.m_prefab, at.position - ((Component)this).transform.rotation * val2, ((Component)this).transform.rotation);
				}
			}
		}

		private static Vector3 BakedOffset(GameObject prefab)
		{
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			if (!BakedOffsets.TryGetValue(prefab, out var value))
			{
				value = (((Object)(object)prefab.GetComponent<ParticleSystem>() == (Object)null && prefab.transform.childCount > 0) ? prefab.transform.GetChild(0).localPosition : Vector3.zero);
				BakedOffsets[prefab] = value;
			}
			return value;
		}
	}
	internal enum MachineState
	{
		Idle,
		Working,
		Blocked
	}
	internal sealed class ReclaimerMachine : MonoBehaviour
	{
		internal struct PanelState
		{
			public string Status;

			public string YieldHeader;

			public string ProcessingHeader;

			public string FuelAmount;

			public string Progress;
		}

		[HarmonyPatch(typeof(Container), "GetHoverText")]
		private static class ContainerHoverPatch
		{
			[HarmonyPriority(0)]
			[HarmonyAfter(new string[] { "shudnal.MyLittleUI" })]
			private static void Postfix(Container __instance, ref string __result)
			{
				ReclaimerMachine component = ((Component)__instance).GetComponent<ReclaimerMachine>();
				if ((Object)(object)component != (Object)null)
				{
					__result += component.HoverText();
				}
			}
		}

		internal const string FuelKey = "dvergrreclaimer_fuel";

		internal const string BankKey = "dvergrreclaimer_bank";

		internal const string LastTimeKey = "dvergrreclaimer_lastTime";

		internal const string ProcessingKey = "dvergrreclaimer_processing";

		internal const string ProcessingNameKey = "dvergrreclaimer_processingName";

		internal const string OutputKey = "dvergrreclaimer_output";

		internal const string OutputStacksKey = "dvergrreclaimer_outputStacks";

		internal const string StateKey = "dvergrreclaimer_state";

		internal const string StatusKey = "dvergrreclaimer_status";

		private const int OutputWidth = 8;

		private const int OutputHeight = 4;

		private const float MaxBankSeconds = 3600f;

		private const float SkipSeconds = 10f;

		private const float TransferTimeoutSeconds = 10f;

		private static HashSet<string> s_allRecipes;

		private static HashSet<string> s_allMaterials;

		private readonly Dictionary<string, float> _skipUntil = new Dictionary<string, float>();

		private readonly Dictionary<string, string> _skipReasons = new Dictionary<string, string>();

		private ZNetView _nview;

		private Container _crate;

		private Piece _piece;

		private Transform _outputPoint;

		private ReclaimerEffects _effects;

		private float _transferDeadline;

		private string _previewKey;

		private float _previewTime;

		private List<ItemData> _preview = new List<ItemData>();

		private bool TransferPending => Time.time < _transferDeadline;

		private void Awake()
		{
			_nview = ((Component)this).GetComponent<ZNetView>();
			_crate = ((Component)this).GetComponent<Container>();
			_piece = ((Component)this).GetComponent<Piece>();
			_outputPoint = ((Component)this).transform.Find("output_point") ?? ((Component)this).transform;
			_effects = ((Component)this).GetComponent<ReclaimerEffects>();
			if ((Object)(object)_nview != (Object)null && _nview.GetZDO() != null)
			{
				((MonoBehaviour)this).InvokeRepeating("Tick", 1f, 1f);
				WearNTear component = ((Component)this).GetComponent<WearNTear>();
				if ((Object)(object)component != (Object)null)
				{
					component.m_onDestroyed = (Action)Delegate.Combine(component.m_onDestroyed, new Action(OnDestroyed));
				}
			}
		}

		private void OnDestroyed()
		{
			if (!((Object)(object)_nview == (Object)null) && _nview.IsValid() && _nview.IsOwner())
			{
				ZDO zDO = _nview.GetZDO();
				Drop(LoadInventory(zDO, "dvergrreclaimer_processing", 1, 1).GetAllItems());
				Drop(LoadInventory(zDO, "dvergrreclaimer_output", 8, 4).GetAllItems());
				int num = Mathf.FloorToInt(zDO.GetFloat("dvergrreclaimer_fuel", 0f));
				GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(ReclaimerConfig.FuelItem.Value);
				if (num > 0 && (Object)(object)itemPrefab != (Object)null)
				{
					ItemData val = itemPrefab.GetComponent<ItemDrop>().m_itemData.Clone();
					val.m_dropPrefab = itemPrefab;
					val.m_stack = num;
					Drop(SplitStacks(val));
				}
			}
		}

		private static IEnumerable<ItemData> SplitStacks(ItemData item)
		{
			int maxStack = Mathf.Max(1, item.m_shared.m_maxStackSize);
			int remaining = item.m_stack;
			while (remaining > 0)
			{
				ItemData val = item.Clone();
				val.m_stack = Mathf.Min(remaining, maxStack);
				remaining -= val.m_stack;
				yield return val;
			}
		}

		private void Tick()
		{
			if ((Object)(object)_nview == (Object)null || !_nview.IsValid() || !_nview.IsOwner())
			{
				return;
			}
			try
			{
				RunTick();
			}
			catch (Exception arg)
			{
				DvergrReclaimerPlugin.Log.LogError((object)$"Reclaimer tick failed: {arg}");
			}
		}

		private void RunTick()
		{
			ZDO zDO = _nview.GetZDO();
			long ticks = ZNet.instance.GetTime().Ticks;
			float num = (float)Math.Max(0.0, (double)(ticks - zDO.GetLong("dvergrreclaimer_lastTime", ticks)) / 10000000.0);
			zDO.Set("dvergrreclaimer_lastTime", ticks);
			if (!RnrEngine.Available)
			{
				Report(zDO, MachineState.Blocked, "Recycle N' Reclaim is missing or incompatible");
				return;
			}
			float num2 = zDO.GetFloat("dvergrreclaimer_fuel", 0f);
			float num3 = zDO.GetFloat("dvergrreclaimer_bank", 0f);
			int num4 = Mathf.Max(0, ReclaimerConfig.FuelPerItem.Value);
			Inventory val = LoadInventory(zDO, "dvergrreclaimer_output", 8, 4);
			Inventory val2 = LoadInventory(zDO, "dvergrreclaimer_processing", 1, 1);
			ItemData val3 = val2.GetAllItems().FirstOrDefault();
			bool flag = DeliverToOwnedChests(val);
			flag |= DropUndeliverable(val);
			bool flag2 = false;
			bool flag3 = false;
			ReclaimContext context = BuildContext();
			MachineState state = MachineState.Idle;
			string status = null;
			if (val3 == null)
			{
				val3 = TakeNextItem(context, out status);
				flag2 = val3 != null;
			}
			if (val3 == null)
			{
				num3 = 0f;
				state = ((status != null) ? MachineState.Blocked : MachineState.Idle);
			}
			else
			{
				num3 = Mathf.Min(num3 + num, 3600f);
				int num5 = 0;
				while (val3 != null && num5 < ReclaimerConfig.ItemsPerTick.Value)
				{
					if (num2 < (float)num4)
					{
						float num6 = TakeFuelFromCrate(num2);
						num2 += num6;
						if (num6 > 0f)
						{
							_effects?.Play(ReclaimerEffect.FuelAdded);
						}
					}
					if (num2 < (float)num4)
					{
						state = MachineState.Blocked;
						status = "Needs " + FuelDisplayName();
						break;
					}
					ReclaimResult reclaimResult = Processing.Analyze(val3, context);
					if (!reclaimResult.Ok)
					{
						state = MachineState.Blocked;
						status = CantReclaim(val3, reclaimResult);
						ReturnToCrate(val3);
						val3 = null;
						flag2 = true;
						break;
					}
					if (ReclaimerConfig.WhenNoRoom.Value == NoRoomBehavior.Wait && !Fits(val, reclaimResult.Yields))
					{
						state = MachineState.Blocked;
						status = "Output full: no room in nearby chests";
						break;
					}
					if (num3 < ReclaimerConfig.SecondsPerItem.Value)
					{
						state = MachineState.Working;
						break;
					}
					ReclaimResult reclaimResult2 = Processing.Process(val3, context);
					if (!reclaimResult2.Ok)
					{
						state = MachineState.Blocked;
						status = CantReclaim(val3, reclaimResult2);
						ReturnToCrate(val3);
						val3 = null;
						flag2 = true;
						break;
					}
					Drop(ContainerTransfer.AddWhatFits(val, reclaimResult2.Yields));
					num2 -= (float)num4;
					num3 -= ReclaimerConfig.SecondsPerItem.Value;
					val3 = null;
					flag2 = (flag = (flag3 = true));
					num5++;
					DeliverToOwnedChests(val);
					DropUndeliverable(val);
					val3 = TakeNextItem(context, out status);
					state = ((val3 != null) ? MachineState.Working : ((status != null) ? MachineState.Blocked : MachineState.Idle));
				}
			}
			zDO.Set("dvergrreclaimer_fuel", num2);
			zDO.Set("dvergrreclaimer_bank", (val3 == null) ? 0f : num3);
			if (flag2)
			{
				val2.RemoveAll();
				if (val3 != null)
				{
					val2.AddItem(val3);
				}
				SaveInventory(zDO, "dvergrreclaimer_processing", val2);
				zDO.Set("dvergrreclaimer_processingName", val3?.m_shared.m_name ?? "");
			}
			if (flag)
			{
				SaveInventory(zDO, "dvergrreclaimer_output", val);
				zDO.Set("dvergrreclaimer_outputStacks", val.NrOfItems());
			}
			Report(zDO, state, status);
			if (flag3)
			{
				_effects?.Play(ReclaimerEffect.Produced);
			}
			if (val3 != null && num2 < (float)num4)
			{
				RequestFuel(num2);
			}
			if (val.NrOfItems() > 0)
			{
				RequestDeposit(val);
			}
		}

		private ItemData TakeNextItem(ReclaimContext context, out string status)
		{
			status = null;
			string text = FuelName();
			Inventory inventory = _crate.GetInventory();
			foreach (ItemData item in (from i in inventory.GetAllItems()
				orderby i.m_gridPos.y, i.m_gridPos.x
				select i).ToList())
			{
				if (item.m_shared.m_name == text || IsBeingDragged(item))
				{
					continue;
				}
				string key = item.m_shared.m_name + "#" + item.m_quality;
				if (_skipUntil.TryGetValue(key, out var value) && Time.time < value)
				{
					status = status ?? _skipReasons[key];
					continue;
				}
				ReclaimResult reclaimResult = Processing.Analyze(item, context);
				if (!reclaimResult.Ok)
				{
					_skipUntil[key] = Time.time + 10f;
					_skipReasons[key] = CantReclaim(item, reclaimResult);
					status = status ?? _skipReasons[key];
					continue;
				}
				inventory.RemoveItem(item);
				_effects?.Play(ReclaimerEffect.ItemAdded);
				return item;
			}
			return null;
		}

		private static bool IsBeingDragged(ItemData item)
		{
			if ((Object)(object)InventoryGui.instance != (Object)null)
			{
				return InventoryGui.instance.m_dragItem == item;
			}
			return false;
		}

		private float TakeFuelFromCrate(float fuel)
		{
			Inventory inventory = _crate.GetInventory();
			ItemData val = InventoryGui.instance?.m_dragItem;
			if (val != null && val.m_shared.m_name == FuelName() && inventory.ContainsItem(val))
			{
				return 0f;
			}
			int num = Mathf.Min(inventory.CountItems(FuelName(), -1, true), Mathf.FloorToInt((float)ReclaimerConfig.MaxFuel.Value - fuel));
			if (num <= 0)
			{
				return 0f;
			}
			inventory.RemoveItem(FuelName(), num, -1, true);
			return num;
		}

		private bool DeliverToOwnedChests(Inventory output)
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			if (output.NrOfItems() == 0)
			{
				return false;
			}
			List<Container> list = (from c in ChestFinder.Nearby(((Component)this).transform.position, ReclaimerConfig.OutputRadius.Value, _piece.GetCreator())
				where c.m_nview.IsOwner()
				select c).ToList();
			if (list.Count == 0)
			{
				return false;
			}
			bool flag = false;
			foreach (ItemData item in output.GetAllItems().ToList())
			{
				output.RemoveItem(item);
				ItemData val = item;
				foreach (Container item2 in Rank(list, item))
				{
					List<ItemData> list2 = ContainerTransfer.AddWhatFits(item2.GetInventory(), (IEnumerable<ItemData>)(object)new ItemData[1] { val });
					flag |= list2.Count == 0 || list2[0].m_stack < val.m_stack;
					val = list2.FirstOrDefault();
					if (val == null)
					{
						break;
					}
				}
				if (val != null)
				{
					output.AddItem(val);
				}
			}
			return flag;
		}

		private bool DropUndeliverable(Inventory output)
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			if (ReclaimerConfig.WhenNoRoom.Value != NoRoomBehavior.DropInFront || output.NrOfItems() == 0)
			{
				return false;
			}
			List<Container> source = (from c in ChestFinder.Nearby(((Component)this).transform.position, ReclaimerConfig.OutputRadius.Value, _piece.GetCreator())
				where !c.m_nview.IsOwner()
				select c).ToList();
			bool result = false;
			foreach (ItemData stack in output.GetAllItems().ToList())
			{
				if (!source.Any((Container c) => c.GetInventory().CanAddItem(stack, 1)))
				{
					output.RemoveItem(stack);
					Drop((IEnumerable<ItemData>)(object)new ItemData[1] { stack });
					result = true;
				}
			}
			return result;
		}

		private void RequestDeposit(Inventory output)
		{
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			if (TransferPending)
			{
				return;
			}
			ItemData stack = output.GetAllItems().First();
			Container val = Rank(from c in ChestFinder.Nearby(((Component)this).transform.position, ReclaimerConfig.OutputRadius.Value, _piece.GetCreator())
				where !c.m_nview.IsOwner()
				select c, stack).FirstOrDefault((Func<Container, bool>)((Container c) => c.GetInventory().CanAddItem(stack, 1)));
			if (!((Object)(object)val == (Object)null))
			{
				output.RemoveItem(stack);
				ZDO zDO = _nview.GetZDO();
				SaveInventory(zDO, "dvergrreclaimer_output", output);
				zDO.Set("dvergrreclaimer_outputStacks", output.NrOfItems());
				_transferDeadline = Time.time + 10f;
				ContainerTransfer.Deposit(val, new List<ItemData> { stack }, delegate(List<ItemData> leftovers)
				{
					_transferDeadline = 0f;
					ReturnToOutput(leftovers);
				});
			}
		}

		private void RequestFuel(float fuel)
		{
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			int num = Mathf.FloorToInt((float)ReclaimerConfig.MaxFuel.Value - fuel);
			if (!ReclaimerConfig.PullFuelFromChests.Value || TransferPending || num <= 0)
			{
				return;
			}
			string fuelName = FuelName();
			Container val = ChestFinder.Nearby(((Component)this).transform.position, ReclaimerConfig.FuelRadius.Value, _piece.GetCreator()).FirstOrDefault((Func<Container, bool>)((Container c) => c.GetInventory().CountItems(fuelName, -1, true) > 0));
			if (!((Object)(object)val == (Object)null))
			{
				_transferDeadline = Time.time + 10f;
				ContainerTransfer.Withdraw(val, fuelName, num, delegate(List<ItemData> items)
				{
					_transferDeadline = 0f;
					AddFuel(items);
				});
			}
		}

		private void AddFuel(List<ItemData> items)
		{
			if (items.Count == 0)
			{
				return;
			}
			if ((Object)(object)_nview == (Object)null || !_nview.IsValid() || !_nview.IsOwner())
			{
				Drop(items);
				return;
			}
			ZDO zDO = _nview.GetZDO();
			float num = zDO.GetFloat("dvergrreclaimer_fuel", 0f);
			int num2 = Mathf.FloorToInt((float)ReclaimerConfig.MaxFuel.Value - num);
			int num3 = items.Sum((ItemData i) => i.m_stack);
			zDO.Set("dvergrreclaimer_fuel", num + (float)Mathf.Min(num2, num3));
			_effects?.Play(ReclaimerEffect.FuelAdded);
			if (num3 > num2)
			{
				ItemData val = items[0].Clone();
				val.m_stack = num3 - num2;
				ReturnToCrate(val);
			}
		}

		private void ReturnToOutput(List<ItemData> items)
		{
			if (items.Count != 0)
			{
				if ((Object)(object)_nview == (Object)null || !_nview.IsValid() || !_nview.IsOwner())
				{
					Drop(items);
					return;
				}
				ZDO zDO = _nview.GetZDO();
				Inventory val = LoadInventory(zDO, "dvergrreclaimer_output", 8, 4);
				Drop(ContainerTransfer.AddWhatFits(val, items));
				SaveInventory(zDO, "dvergrreclaimer_output", val);
				zDO.Set("dvergrreclaimer_outputStacks", val.NrOfItems());
			}
		}

		private void ReturnToCrate(ItemData item)
		{
			Drop(ContainerTransfer.AddWhatFits(_crate.GetInventory(), (IEnumerable<ItemData>)(object)new ItemData[1] { item }));
		}

		private void Drop(IEnumerable<ItemData> items)
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			foreach (ItemData item in items)
			{
				ItemDrop.DropItem(item, item.m_stack, _outputPoint.position + Vector3.up * 0.3f, Quaternion.identity);
			}
		}

		private static IEnumerable<Container> Rank(IEnumerable<Container> chests, ItemData item)
		{
			return chests.OrderByDescending((Container c) => c.GetInventory().ContainsItemByName(item.m_shared.m_name));
		}

		private static bool Fits(Inventory output, IEnumerable<ItemData> yields)
		{
			//IL_0012: 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_001d: Expected O, but got Unknown
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Expected O, but got Unknown
			Inventory val = new Inventory("DvergrReclaimerFitCheck", (Sprite)null, output.GetWidth(), output.GetHeight());
			ZPackage val2 = new ZPackage();
			output.Save(val2);
			val2.SetPos(0);
			val.Load(val2);
			return ContainerTransfer.AddWhatFits(val, yields.Select((ItemData y) => y.Clone())).Count == 0;
		}

		private ReclaimContext BuildContext()
		{
			//IL_0142: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
			if (ReclaimerConfig.RecipeGate.Value == RecipeGateMode.Off)
			{
				if (s_allRecipes == null)
				{
					s_allRecipes = new HashSet<string>(from r in ObjectDB.instance.m_recipes
						select r?.m_item?.m_itemData?.m_shared?.m_name into n
						where n != null
						select n);
					s_allMaterials = new HashSet<string>(from i in ObjectDB.instance.m_items
						select (i == null) ? null : i.GetComponent<ItemDrop>()?.m_itemData?.m_shared?.m_name into n
						where n != null
						select n);
				}
				return new ReclaimContext
				{
					KnownRecipes = s_allRecipes,
					KnownMaterials = s_allMaterials,
					Position = ((Component)this).transform.position,
					StationRadius = ReclaimerConfig.StationRadius.Value,
					SkipLedgerGate = true
				};
			}
			return new ReclaimContext
			{
				KnownRecipes = RecipeLedger.Current.Recipes,
				KnownMaterials = RecipeLedger.Current.Materials,
				Position = ((Component)this).transform.position,
				StationRadius = ReclaimerConfig.StationRadius.Value
			};
		}

		private static string CantReclaim(ItemData item, ReclaimResult result)
		{
			string text = Localization.instance.Localize(item.m_shared.m_name);
			if (result.Sacrifice)
			{
				return "Can't sacrifice " + text + ": " + (result.Reasons.FirstOrDefault() ?? result.Status.ToString());
			}
			return result.Status switch
			{
				ReclaimStatus.RecipeUnknown => "Can't reclaim " + text + ": nobody on this world knows its recipe", 
				ReclaimStatus.NoRecipe => "Can't reclaim " + text + ": it has no recipe", 
				_ => "Can't reclaim " + text + ": " + (result.Reasons.FirstOrDefault() ?? result.Status.ToString()), 
			};
		}

		private static string FuelName()
		{
			GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(ReclaimerConfig.FuelItem.Value);
			return ((itemPrefab == null) ? null : itemPrefab.GetComponent<ItemDrop>()?.m_itemData.m_shared.m_name) ?? "$item_coal";
		}

		private static string FuelDisplayName()
		{
			return Localization.instance.Localize(FuelName());
		}

		private static Inventory LoadInventory(ZDO zdo, string key, int width, int height)
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Expected O, but got Unknown
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Expected O, but got Unknown
			Inventory val = new Inventory("DvergrReclaimer", (Sprite)null, width, height);
			byte[] byteArray = zdo.GetByteArray(key, (byte[])null);
			if (byteArray != null)
			{
				val.Load(new ZPackage(byteArray));
			}
			return val;
		}

		private static void SaveInventory(ZDO zdo, string key, Inventory inventory)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Expected O, but got Unknown
			ZPackage val = new ZPackage();
			inventory.Save(val);
			zdo.Set(key, val.GetArray());
		}

		private static void Report(ZDO zdo, MachineState state, string status)
		{
			zdo.Set("dvergrreclaimer_state", (int)state);
			zdo.Set("dvergrreclaimer_status", status ?? "");
		}

		internal PanelState PanelView(Inventory display)
		{
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
			display.RemoveAll();
			ZDO zDO = _nview.GetZDO();
			int num = Mathf.FloorToInt(zDO.GetFloat("dvergrreclaimer_fuel", 0f));
			GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(ReclaimerConfig.FuelItem.Value);
			if (num > 0 && (Object)(object)itemPrefab != (Object)null)
			{
				ItemData val = itemPrefab.GetComponent<ItemDrop>().m_itemData.Clone();
				val.m_dropPrefab = itemPrefab;
				val.m_stack = num;
				display.AddItem(val, new Vector2i(0, 0));
			}
			ItemData val2 = LoadInventory(zDO, "dvergrreclaimer_processing", 1, 1).GetAllItems().FirstOrDefault();
			if (val2 != null)
			{
				display.AddItem(val2, new Vector2i(2, 0));
			}
			List<ItemData> list = Preview(val2);
			int num2 = 4;
			for (int i = 0; i < list.Count && i < num2; i++)
			{
				display.AddItem(list[i].Clone(), new Vector2i(4 + i, 0));
			}
			string text = zDO.GetString("dvergrreclaimer_status", "");
			string text2 = ((zDO.GetInt("dvergrreclaimer_state", 0) == 2 && text.Length > 0) ? ("<color=#ff9a76>" + text + "</color>") : ((val2 == null) ? "Put gear in the crate to reclaim it" : (Processing.Verb(val2) + " " + Localization.instance.Localize(val2.m_shared.m_name))));
			int num3 = zDO.GetInt("dvergrreclaimer_outputStacks", 0);
			if (num3 > 0)
			{
				text2 += string.Format("  ·  {0} stack{1} waiting to unload", num3, (num3 == 1) ? "" : "s");
			}
			float num4 = ((val2 == null) ? 0f : Mathf.Clamp01(zDO.GetFloat("dvergrreclaimer_bank", 0f) / Mathf.Max(0.01f, ReclaimerConfig.SecondsPerItem.Value)));
			return new PanelState
			{
				Status = text2,
				YieldHeader = ((list.Count > num2) ? $"Produces (+{list.Count - num2} more)" : "Produces"),
				FuelAmount = $"{num}/{ReclaimerConfig.MaxFuel.Value}",
				Progress = $"{Mathf.FloorToInt(num4 * 100f)}%",
				ProcessingHeader = ((val2 != null) ? Processing.Verb(val2) : "Reclaiming")
			};
		}

		private List<ItemData> Preview(ItemData processing)
		{
			if (processing == null || !RnrEngine.Available)
			{
				_previewKey = null;
				return new List<ItemData>();
			}
			string text = $"{processing.m_shared.m_name}#{processing.m_quality}#{processing.m_stack}";
			if (text != _previewKey || Time.time - _previewTime > 2f)
			{
				ReclaimResult reclaimResult = Processing.Analyze(processing, BuildContext());
				_preview = (reclaimResult.Ok ? reclaimResult.Yields : new List<ItemData>());
				_previewKey = text;
				_previewTime = Time.time;
			}
			return _preview;
		}

		internal string HoverText()
		{
			ZNetView nview = _nview;
			ZDO val = ((nview != null) ? nview.GetZDO() : null);
			if (val == null)
			{
				return "";
			}
			StringBuilder stringBuilder = new StringBuilder();
			if (ReclaimerConfig.FuelPerItem.Value > 0)
			{
				stringBuilder.Append(string.Format("\n{0}: {1}/{2}", FuelDisplayName(), Mathf.FloorToInt(val.GetFloat("dvergrreclaimer_fuel", 0f)), ReclaimerConfig.MaxFuel.Value));
			}
			string fuelName = FuelName();
			stringBuilder.Append($"\nQueued: {_crate.GetInventory().GetAllItems().Count((ItemData i) => i.m_shared.m_name != fuelName)}");
			string text = val.GetString("dvergrreclaimer_processingName", "");
			if (!string.IsNullOrEmpty(text))
			{
				ItemData val2 = LoadInventory(val, "dvergrreclaimer_processing", 1, 1).GetAllItems().FirstOrDefault();
				string arg = ((val2 != null) ? Processing.Verb(val2) : "Reclaiming");
				float num = Mathf.Clamp01(val.GetFloat("dvergrreclaimer_bank", 0f) / Mathf.Max(0.01f, ReclaimerConfig.SecondsPerItem.Value));
				stringBuilder.Append($"\n{arg}: {Localization.instance.Localize(text)} ({num:P0})");
			}
			int num2 = val.GetInt("dvergrreclaimer_outputStacks", 0);
			if (num2 > 0)
			{
				stringBuilder.Append(string.Format("\nWaiting to unload: {0} stack{1}", num2, (num2 == 1) ? "" : "s"));
			}
			string text2 = val.GetString("dvergrreclaimer_status", "");
			if (val.GetInt("dvergrreclaimer_state", 0) == 2 && text2.Length > 0)
			{
				stringBuilder.Append("\n<color=#ff9a76>" + text2 + "</color>");
			}
			return stringBuilder.ToString();
		}
	}
}
namespace DvergrReclaimer.Logistics
{
	internal static class ChestFinder
	{
		[HarmonyPatch(typeof(Container), "Awake")]
		private static class ContainerAwakePatch
		{
			private static void Postfix(Container __instance)
			{
				if ((Object)(object)__instance.m_nview != (Object)null && __instance.m_nview.GetZDO() != null)
				{
					Loaded.Add(__instance);
				}
			}
		}

		private static readonly HashSet<Container> Loaded = new HashSet<Container>();

		public static IEnumerable<Container> Nearby(Vector3 position, float radius, long creator)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			Loaded.RemoveWhere((Container c) => (Object)(object)c == (Object)null);
			return from c in Loaded
				where Vector3.Distance(((Component)c).transform.position, position) <= radius && Usable(c, creator)
				orderby Vector3.Distance(((Component)c).transform.position, position)
				select c;
		}

		private static bool Usable(Container chest, long creator)
		{
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			ZNetView nview = chest.m_nview;
			if ((Object)(object)nview == (Object)null || !nview.IsValid() || chest.m_lastRevision == uint.MaxValue)
			{
				return false;
			}
			if (nview.GetZDO().GetInt(ZDOVars.s_inUse, 0) == 1)
			{
				return false;
			}
			if ((Object)(object)((Component)chest).GetComponent<ReclaimerMachine>() != (Object)null || (Object)(object)((Component)chest).GetComponent<TombStone>() != (Object)null || (Object)(object)((Component)chest).GetComponentInParent<Ship>() != (Object)null || (Object)(object)((Component)chest).GetComponentInParent<Vagon>() != (Object)null)
			{
				return false;
			}
			Piece component = ((Component)chest).GetComponent<Piece>();
			if ((Object)(object)component != (Object)null && component.IsPlacedByPlayer() && chest.CheckAccess(creator))
			{
				return WardAllows(((Component)chest).transform.position, creator);
			}
			return false;
		}

		private static bool WardAllows(Vector3 point, long creator)
		{
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			foreach (PrivateArea allArea in PrivateArea.m_allAreas)
			{
				if ((Object)(object)allArea != (Object)null && allArea.IsEnabled() && allArea.IsInside(point, 0f) && allArea.m_piece.GetCreator() != creator && !allArea.IsPermitted(creator))
				{
					return false;
				}
			}
			return true;
		}
	}
	internal static class ContainerTransfer
	{
		[HarmonyPatch(typeof(Container), "Awake")]
		private static class ContainerAwakePatch
		{
			private static void Postfix(Container __instance)
			{
				ZNetView nview = __instance.m_nview;
				if (!((Object)(object)nview == (Object)null) && nview.GetZDO() != null)
				{
					nview.Register<long, ZPackage>("DvergrReclaimer_Deposit", (Action<long, long, ZPackage>)delegate(long sender, long id, ZPackage pkg)
					{
						OnDeposit(__instance, sender, id, pkg);
					});
					nview.Register<long, string, int>("DvergrReclaimer_Withdraw", (Action<long, long, string, int>)delegate(long sender, long id, string name, int count)
					{
						OnWithdraw(__instance, sender, id, name, count);
					});
				}
			}
		}

		private const string DepositRpc = "DvergrReclaimer_Deposit";

		private const string WithdrawRpc = "DvergrReclaimer_Withdraw";

		private const string ResultRpc = "DvergrReclaimer_TransferResult";

		private const int PackWidth = 8;

		private static readonly Dictionary<long, Action<List<ItemData>>> Pending = new Dictionary<long, Action<List<ItemData>>>();

		private static long _lastRequestId;

		public static int PendingCount => Pending.Count;

		internal static void Register()
		{
			Pending.Clear();
			ZRoutedRpc.instance.Register<long, ZPackage>("DvergrReclaimer_TransferResult", (Action<long, long, ZPackage>)OnResult);
		}

		public static void Deposit(Container chest, List<ItemData> items, Action<List<ItemData>> onLeftovers, bool forceRpc = false)
		{
			ZNetView nview = chest.m_nview;
			if (!forceRpc && nview.IsOwner())
			{
				onLeftovers(AddWhatFits(chest.GetInventory(), items));
				return;
			}
			long num = ++_lastRequestId;
			Pending[num] = onLeftovers;
			nview.InvokeRPC("DvergrReclaimer_Deposit", new object[2]
			{
				num,
				Pack(items)
			});
		}

		public static void Withdraw(Container chest, string itemName, int count, Action<List<ItemData>> onWithdrawn, bool forceRpc = false)
		{
			ZNetView nview = chest.m_nview;
			if (!forceRpc && nview.IsOwner())
			{
				onWithdrawn(RemoveUpTo(chest.GetInventory(), itemName, count));
				return;
			}
			long num = ++_lastRequestId;
			Pending[num] = onWithdrawn;
			nview.InvokeRPC("DvergrReclaimer_Withdraw", new object[3] { num, itemName, count });
		}

		internal static List<ItemData> AddWhatFits(Inventory inventory, IEnumerable<ItemData> items)
		{
			List<ItemData> list = new List<ItemData>();
			foreach (ItemData item in items)
			{
				SharedData shared = item.m_shared;
				if (shared.m_maxStackSize <= 1)
				{
					if (!inventory.HaveEmptySlot() || !inventory.AddItem(item))
					{
						list.Add(item);
					}
					continue;
				}
				int num = Math.Min(item.m_stack, inventory.FindFreeStackSpace(shared.m_name, (float)item.m_worldLevel) + inventory.GetEmptySlots() * shared.m_maxStackSize);
				int num2 = num;
				while (num2 > 0)
				{
					ItemData val = item.Clone();
					val.m_stack = Math.Min(num2, shared.m_maxStackSize);
					num2 -= val.m_stack;
					inventory.AddItem(val);
				}
				if (num < item.m_stack)
				{
					ItemData val2 = item.Clone();
					val2.m_stack = item.m_stack - num;
					list.Add(val2);
				}
			}
			return list;
		}

		internal static List<ItemData> RemoveUpTo(Inventory inventory, string itemName, int count)
		{
			List<ItemData> list = new List<ItemData>();
			foreach (ItemData item in (from i in inventory.GetAllItems()
				where i.m_shared.m_name == itemName
				select i).ToList())
			{
				if (count <= 0)
				{
					break;
				}
				if (item.m_stack <= count)
				{
					inventory.RemoveItem(item);
					list.Add(item);
					count -= item.m_stack;
				}
				else
				{
					ItemData val = item.Clone();
					val.m_stack = count;
					inventory.RemoveItem(item, count);
					list.Add(val);
					count = 0;
				}
			}
			return list;
		}

		internal static ZPackage Pack(IEnumerable<ItemData> items)
		{
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a2: Expected O, but got Unknown
			//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ea: Expected O, but got Unknown
			//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0104: Unknown result type (might be due to invalid IL or missing references)
			//IL_010c: Expected O, but got Unknown
			List<ItemData> list = new List<ItemData>();
			foreach (ItemData item in items)
			{
				int val = Math.Max(1, item.m_shared.m_maxStackSize);
				int num = item.m_stack;
				while (num > 0)
				{
					ItemData val2 = item.Clone();
					val2.m_stack = Math.Min(num, val);
					num -= val2.m_stack;
					list.Add(val2);
				}
			}
			int num2 = Math.Max(1, (list.Count + 8 - 1) / 8);
			Inventory val3 = new Inventory("DvergrReclaimerTransfer", (Sprite)null, 8, num2);
			for (int i = 0; i < list.Count; i++)
			{
				list[i].m_gridPos = new Vector2i(i % 8, i / 8);
				val3.m_inventory.Add(list[i]);
			}
			ZPackage val4 = new ZPackage();
			val3.Save(val4);
			ZPackage val5 = new ZPackage();
			val5.Write(8);
			val5.Write(num2);
			val5.Write(val4);
			return val5;
		}

		internal static List<ItemData> Unpack(ZPackage pkg)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			int num = pkg.ReadInt();
			int num2 = pkg.ReadInt();
			Inventory val = new Inventory("DvergrReclaimerTransfer", (Sprite)null, num, num2);
			val.Load(pkg.ReadPackage());
			return val.GetAllItems().ToList();
		}

		private static void OnDeposit(Container chest, long sender, long requestId, ZPackage pkg)
		{
			List<ItemData> list = Unpack(pkg);
			List<ItemData> items = (chest.m_nview.IsOwner() ? AddWhatFits(chest.GetInventory(), list) : list);
			ZRoutedRpc.instance.InvokeRoutedRPC(sender, "DvergrReclaimer_TransferResult", new object[2]
			{
				requestId,
				Pack(items)
			});
		}

		private static void OnWithdraw(Container chest, long sender, long requestId, string itemName, int count)
		{
			List<ItemData> items = (chest.m_nview.IsOwner() ? RemoveUpTo(chest.GetInventory(), itemName, count) : new List<ItemData>());
			ZRoutedRpc.instance.InvokeRoutedRPC(sender, "DvergrReclaimer_TransferResult", new object[2]
			{
				requestId,
				Pack(items)
			});
		}

		private static void OnResult(long sender, long requestId, ZPackage pkg)
		{
			if (Pending.TryGetValue(requestId, out var value))
			{
				Pending.Remove(requestId);
				value(Unpack(pkg));
			}
		}
	}
}
namespace DvergrReclaimer.Ledger
{
	internal static class LedgerSync
	{
		[HarmonyPatch(typeof(Player), "OnSpawned")]
		private static class PlayerOnSpawnedPatch
		{
			private static void Postfix(Player __instance)
			{
				if ((Object)(object)__instance == (Object)(object)Player.m_localPlayer)
				{
					ReportFull(__instance);
				}
			}
		}

		[HarmonyPatch(typeof(Player), "AddKnownRecipe")]
		private static class PlayerAddKnownRecipePatch
		{
			private static void Prefix(Player __instance, Recipe recipe, out bool __state)
			{
				__state = __instance.IsRecipeKnown(recipe.m_item.m_itemData.m_shared.m_name);
			}

			private static void Postfix(Player __instance, Recipe recipe, bool __state)
			{
				string name = recipe.m_item.m_itemData.m_shared.m_name;
				if (!__state && (Object)(object)__instance == (Object)(object)Player.m_localPlayer && __instance.IsRecipeKnown(name))
				{
					ReportNew(__instance, name, null);
				}
			}
		}

		[HarmonyPatch(typeof(Player), "AddKnownItem")]
		private static class PlayerAddKnownItemPatch
		{
			private static void Prefix(Player __instance, ItemData item, out bool __state)
			{
				__state = __instance.IsKnownMaterial(item.m_shared.m_name);
			}

			private static void Postfix(Player __instance, ItemData item, bool __state)
			{
				string name = item.m_shared.m_name;
				if (!__state && (Object)(object)__instance == (Object)(object)Player.m_localPlayer && __instance.IsKnownMaterial(name))
				{
					ReportNew(__instance, null, name);
				}
			}
		}

		private const string ReportRpc = "DvergrReclaimer_LedgerReport";

		private const string SyncRpc = "DvergrReclaimer_LedgerSync";

		private static string _savePath;

		private static bool _reportedFull;

		public static int FullSyncsReceived { get; private set; }

		public static int DeltaSyncsReceived { get; private set; }

		public static string SavePath => _savePath;

		internal static void Register()
		{
			ZRoutedRpc.instance.Register<ZPackage>("DvergrReclaimer_LedgerReport", (Action<long, ZPackage>)OnReport);
			ZRoutedRpc.instance.Register<ZPackage>("DvergrReclaimer_LedgerSync", (Action<long, ZPackage>)OnSync);
			_reportedFull = false;
			FullSyncsReceived = 0;
			DeltaSyncsReceived = 0;
			if (ZNet.instance.IsServer())
			{
				World world = ZNet.World;
				_savePath = Path.Combine(Paths.ConfigPath, "greatcoltini.DvergrReclaimer", $"{Sanitize(world.m_name)}_{world.m_uid}.ledger");
				RecipeLedger.Replace(RecipeLedger.Load(_savePath));
				RecipeLedger current = RecipeLedger.Current;
				DvergrReclaimerPlugin.Log.LogInfo((object)$"Loaded recipe ledger: {current.Recipes.Count} recipes from {current.Contributors.Count} players ({_savePath})");
			}
			else
			{
				_savePath = null;
				RecipeLedger.Replace(new RecipeLedger());
			}
		}

		internal static void ReportFull(Player player)
		{
			SendReport(player.GetPlayerID(), player.m_knownRecipes, player.m_knownMaterial, full: true);
			_reportedFull = true;
		}

		internal static void ReportNew(Player player, string recipe, string material)
		{
			if (_reportedFull)
			{
				string[] recipes = ((recipe == null) ? new string[0] : new string[1] { recipe });
				string[] materials = ((material == null) ? new string[0] : new string[1] { material });
				SendReport(player.GetPlayerID(), recipes, materials, full: false);
			}
		}

		internal static void SendReport(long playerId, ICollection<string> recipes, ICollection<string> materials, bool full)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Expected O, but got Unknown
			ZPackage val = new ZPackage();
			val.Write(playerId);
			val.Write(full);
			RecipeLedger.WriteSet(val, recipes);
			RecipeLedger.WriteSet(val, materials);
			ZRoutedRpc.instance.InvokeRoutedRPC("DvergrReclaimer_LedgerReport", new object[1] { val });
		}

		private static void OnReport(long sender, ZPackage pkg)
		{
			if (ZNet.instance.IsServer())
			{
				long num = pkg.ReadLong();
				bool flag = pkg.ReadBool();
				List<string> list = new List<string>();
				List<string> list2 = new List<string>();
				RecipeLedger.ReadSet(pkg, list);
				RecipeLedger.ReadSet(pkg, list2);
				HashSet<string> hashSet = new HashSet<string>(from r in ObjectDB.instance.m_recipes
					select r?.m_item?.m_itemData?.m_shared?.m_name into n
					where n != null
					select n);
				HashSet<string> hashSet2 = new HashSet<string>(from i in ObjectDB.instance.m_items
					select (i == null) ? null : i.GetComponent<ItemDrop>()?.m_itemData?.m_shared?.m_name into n
					where n != null
					select n);
				RecipeLedger recipeLedger = RecipeLedger.Current.Merge(num, list.Where(hashSet.Contains), list2.Where(hashSet2.Contains));
				if (!recipeLedger.IsEmpty)
				{
					RecipeLedger.Current.Save(_savePath);
					ZRoutedRpc.instance.InvokeRoutedRPC(0L, "DvergrReclaimer_LedgerSync", new object[1] { BuildSync(recipeLedger, full: false) });
					DvergrReclaimerPlugin.Log.LogInfo((object)$"Recipe ledger grew by {recipeLedger.Recipes.Count} recipes and {recipeLedger.Materials.Count} materials from player {num}");
				}
				if (flag)
				{
					ZRoutedRpc.instance.InvokeRoutedRPC(sender, "DvergrReclaimer_LedgerSync", new object[1] { BuildSync(RecipeLedger.Current, full: true) });
				}
			}
		}

		private static ZPackage BuildSync(RecipeLedger ledger, bool full)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Expected O, but got Unknown
			ZPackage val = new ZPackage();
			val.Write(full);
			ledger.Write(val);
			return val;
		}

		private static void OnSync(long sender, ZPackage pkg)
		{
			if (!ZNet.instance.IsServer())
			{
				bool num = pkg.ReadBool();
				RecipeLedger recipeLedger = RecipeLedger.Read(pkg);
				if (num)
				{
					RecipeLedger.Replace(recipeLedger);
					FullSyncsReceived++;
				}
				else
				{
					RecipeLedger.Current.UnionWith(recipeLedger);
					DeltaSyncsReceived++;
				}
			}
		}

		private static string Sanitize(string name)
		{
			char[] invalidFileNameChars = Path.GetInvalidFileNameChars();
			foreach (char oldChar in invalidFileNameChars)
			{
				name = name.Replace(oldChar, '_');
			}
			return name;
		}
	}
	internal sealed class RecipeLedger
	{
		public readonly HashSet<string> Recipes = new HashSet<string>();

		public readonly HashSet<string> Materials = new HashSet<string>();

		public readonly HashSet<long> Contributors = new HashSet<long>();

		public static RecipeLedger Current { get; private set; } = new RecipeLedger();

		public bool IsEmpty
		{
			get
			{
				if (Recipes.Count == 0 && Materials.Count == 0)
				{
					return Contributors.Count == 0;
				}
				return false;
			}
		}

		internal static void Replace(RecipeLedger ledger)
		{
			Current = ledger ?? new RecipeLedger();
		}

		public RecipeLedger Merge(long playerId, IEnumerable<string> recipes, IEnumerable<string> materials)
		{
			RecipeLedger recipeLedger = new RecipeLedger();
			if (Contributors.Add(playerId))
			{
				recipeLedger.Contributors.Add(playerId);
			}
			foreach (string recipe in recipes)
			{
				if (Recipes.Add(recipe))
				{
					recipeLedger.Recipes.Add(recipe);
				}
			}
			foreach (string material in materials)
			{
				if (Materials.Add(material))
				{
					recipeLedger.Materials.Add(material);
				}
			}
			return recipeLedger;
		}

		public void UnionWith(RecipeLedger other)
		{
			Recipes.UnionWith(other.Recipes);
			Materials.UnionWith(other.Materials);
			Contributors.UnionWith(other.Contributors);
		}

		public void Write(ZPackage pkg)
		{
			pkg.Write(Contributors.Count);
			foreach (long contributor in Contributors)
			{
				pkg.Write(contributor);
			}
			WriteSet(pkg, Recipes);
			WriteSet(pkg, Materials);
		}

		public static RecipeLedger Read(ZPackage pkg)
		{
			RecipeLedger recipeLedger = new RecipeLedger();
			int num = pkg.ReadInt();
			for (int i = 0; i < num; i++)
			{
				recipeLedger.Contributors.Add(pkg.ReadLong());
			}
			ReadSet(pkg, recipeLedger.Recipes);
			ReadSet(pkg, recipeLedger.Materials);
			return recipeLedger;
		}

		internal static void WriteSet(ZPackage pkg, ICollection<string> values)
		{
			pkg.Write(values.Count);
			foreach (string value in values)
			{
				pkg.Write(value);
			}
		}

		internal static void ReadSet(ZPackage pkg, ICollection<string> into)
		{
			int num = pkg.ReadInt();
			for (int i = 0; i < num; i++)
			{
				into.Add(pkg.ReadString());
			}
		}

		public void Save(string path)
		{
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.AppendLine("# Dvergr Reclaimer recipe ledger: p = player ID, r = recipe, m = material");
			foreach (long item in Contributors.OrderBy((long id) => id))
			{
				stringBuilder.Append("p\t").AppendLine(item.ToString());
			}
			foreach (string item2 in Recipes.OrderBy<string, string>((string r) => r, StringComparer.Ordinal))
			{
				stringBuilder.Append("r\t").AppendLine(item2);
			}
			foreach (string item3 in Materials.OrderBy<string, string>((string m) => m, StringComparer.Ordinal))
			{
				stringBuilder.Append("m\t").AppendLine(item3);
			}
			Directory.CreateDirectory(Path.GetDirectoryName(path));
			string text = path + ".tmp";
			File.WriteAllText(text, stringBuilder.ToString(), Encoding.UTF8);
			if (File.Exists(path))
			{
				File.Delete(path);
			}
			File.Move(text, path);
		}

		public static RecipeLedger Load(string path)
		{
			RecipeLedger recipeLedger = new RecipeLedger();
			if (!File.Exists(path))
			{
				return recipeLedger;
			}
			string[] array = File.ReadAllLines(path, Encoding.UTF8);
			foreach (string text in array)
			{
				if (text.Length < 3 || text[1] != '\t')
				{
					continue;
				}
				string text2 = text.Substring(2);
				switch (text[0])
				{
				case 'p':
				{
					if (long.TryParse(text2, out var result))
					{
						recipeLedger.Contributors.Add(result);
					}
					break;
				}
				case 'r':
					recipeLedger.Recipes.Add(text2);
					break;
				case 'm':
					recipeLedger.Materials.Add(text2);
					break;
				}
			}
			return recipeLedger;
		}
	}
}
namespace DvergrReclaimer.Engine
{
	internal static class EpicLootSacrifice
	{
		internal const string EpicLootGuid = "randyknapp.mods.epicloot";

		private static MethodInfo s_getProducts;

		private static FieldInfo s_productItem;

		private static FieldInfo s_productAmount;

		private static Type s_tableType;

		private static MethodInfo s_isFeatureUnlocked;

		private static MethodInfo s_getFeatureValue;

		private static object s_sacrificeFeature;

		public static bool Available { get; private set; }

		public static string Problem { get; private set; } = "not probed yet";

		public static bool Probe()
		{
			Available = false;
			if (!Chainloader.PluginInfos.ContainsKey("randyknapp.mods.epicloot"))
			{
				Problem = "Epic Loot is not installed";
				return false;
			}
			try
			{
				Type type = AccessTools.TypeByName("EpicLoot.Crafting.EnchantCostsHelper");
				s_getProducts = ((type == null) ? null : AccessTools.Method(type, "GetSacrificeProducts", new Type[1] { typeof(ItemData) }, (Type[])null));
				Type type2 = s_getProducts?.ReturnType.GetGenericArguments().FirstOrDefault();
				s_productItem = ((type2 == null) ? null : AccessTools.Field(type2, "Item"));
				s_productAmount = ((type2 == null) ? null : AccessTools.Field(type2, "Amount"));
				if (s_getProducts == null || !s_getProducts.IsStatic || s_productItem?.FieldType != typeof(string) || s_productAmount?.FieldType != typeof(int))
				{
					Problem = "EnchantCostsHelper.GetSacrificeProducts(ItemData) changed";
					return false;
				}
				s_tableType = AccessTools.TypeByName("EpicLoot_UnityLib.EnchantingTable");
				Type type3 = AccessTools.TypeByName("EpicLoot_UnityLib.EnchantingFeature");
				s_isFeatureUnlocked = ((s_tableType == null || type3 == null) ? null : AccessTools.Method(s_tableType, "IsFeatureUnlocked", new Type[1] { type3 }, (Type[])null));
				s_getFeatureValue = ((s_tableType == null || type3 == null) ? null : AccessTools.Method(s_tableType, "GetFeatureCurrentValue", new Type[1] { type3 }, (Type[])null));
				if (s_isFeatureUnlocked == null || s_getFeatureValue?.ReturnType != typeof(Tuple<float, float>) || !Enum.IsDefined(type3, "Sacrifice"))
				{
					Problem = "EnchantingTable's feature API changed";
					return false;
				}
				s_sacrificeFeature = Enum.Parse(type3, "Sacrifice");
				Problem = null;
				Available = true;
				return true;
			}
			catch (Exception ex)
			{
				Problem = ex.Message;
				return false;
			}
		}

		public static bool Handles(ItemData item)
		{
			if (Available && ReclaimerConfig.SacrificeWithoutRecipe.Value && item != null && (Object)(object)ObjectDB.instance.GetRecipe(item) == (Object)null)
			{
				return Products(item) != null;
			}
			return false;
		}

		public static ReclaimResult Run(ItemData item, ReclaimContext context, bool commit)
		{
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			ReclaimResult reclaimResult = new ReclaimResult
			{
				Sacrifice = true
			};
			List<ItemData> list = Products(item);
			if (list == null)
			{
				reclaimResult.Status = ReclaimStatus.NoRecipe;
				reclaimResult.Reasons.Add("Epic Loot doesn't accept it as a sacrifice");
				return reclaimResult;
			}
			float num = 0f;
			if (ReclaimerConfig.SacrificeNeedsTable.Value)
			{
				Component val = FindTable(context.Position, context.StationRadius);
				if ((Object)(object)val == (Object)null)
				{
					reclaimResult.Status = ReclaimStatus.Blocked;
					reclaimResult.Reasons.Add($"it needs an Enchanting Table within {context.StationRadius:0} m");
					return reclaimResult;
				}
				num = BonusChance(val);
			}
			bool flag = commit && Random.value < num;
			foreach (ItemData item2 in list)
			{
				item2.m_stack *= item.m_stack * ((!flag) ? 1 : 2);
				reclaimResult.Yields.Add(item2);
			}
			reclaimResult.Doubled = flag;
			reclaimResult.Status = ReclaimStatus.Ok;
			return reclaimResult;
		}

		private static List<ItemData> Products(ItemData item)
		{
			if (!(s_getProducts.Invoke(null, new object[1] { item }) is IList { Count: not 0 } list))
			{
				return null;
			}
			List<ItemData> list2 = new List<ItemData>();
			foreach (object item2 in list)
			{
				GameObject itemPrefab = ObjectDB.instance.GetItemPrefab((string)s_productItem.GetValue(item2));
				int num = (int)s_productAmount.GetValue(item2);
				ItemDrop val = (((Object)(object)itemPrefab != (Object)null) ? itemPrefab.GetComponent<ItemDrop>() : null);
				if (!((Object)(object)val == (Object)null) && num > 0)
				{
					ItemData val2 = val.m_itemData.Clone();
					val2.m_dropPrefab = itemPrefab;
					val2.m_stack = num;
					list2.Add(val2);
				}
			}
			if (list2.Count <= 0)
			{
				return null;
			}
			return list2;
		}

		private static Component FindTable(Vector3 position, float radius)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			List<Piece> list = new List<Piece>();
			Piece.GetAllPiecesInRadius(position, radius, list);
			return (from p in list
				select ((Component)p).GetComponent(s_tableType) into t
				where (Object)(object)t != (Object)null && (bool)s_isFeatureUnlocked.Invoke(t, new object[1] { s_sacrificeFeature })
				orderby Vector3.Distance(t.transform.position, position)
				select t).FirstOrDefault();
		}

		private static float BonusChance(Component table)
		{
			Tuple<float, float> tuple = (Tuple<float, float>)s_getFeatureValue.Invoke(table, new object[1] { s_sacrificeFeature });
			if (!float.IsNaN(tuple.Item1))
			{
				return Mathf.Clamp01(tuple.Item1 / 100f);
			}
			return 0f;
		}
	}
	internal readonly struct PlayerContextScope : IDisposable
	{
		private static readonly FieldInfo KnownRecipesField = AccessTools.Field(typeof(Player), "m_knownRecipes");

		private static readonly FieldInfo KnownMaterialField = AccessTools.Field(typeof(Player), "m_knownMaterial");

		private static readonly FieldInfo CurrentStationField = AccessTools.Field(typeof(Player), "m_currentStation");

		private readonly Player _player;

		private readonly object _recipes;

		private readonly object _materials;

		private readonly object _station;

		public PlayerContextScope(Player player, HashSet<string> recipes, HashSet<string> materials, CraftingStation station)
		{
			_player = player;
			_recipes = KnownRecipesField.GetValue(player);
			_materials = KnownMaterialField.GetValue(player);
			_station = CurrentStationField.GetValue(player);
			KnownRecipesField.SetValue(player, recipes);
			KnownMaterialField.SetValue(player, materials);
			CurrentStationField.SetValue(player, station);
		}

		public void Dispose()
		{
			KnownRecipesField.SetValue(_player, _recipes);
			KnownMaterialField.SetValue(_player, _materials);
			CurrentStationField.SetValue(_player, _station);
		}

		internal static bool Probe(out string problem)
		{
			if (KnownRecipesField?.FieldType != typeof(HashSet<string>))
			{
				problem = "Player.m_knownRecipes was not found";
				return false;
			}
			if (KnownMaterialField?.FieldType != typeof(HashSet<string>))
			{
				problem = "Player.m_knownMaterial was not found";
				return false;
			}
			if (CurrentStationField?.FieldType != typeof(CraftingStation))
			{
				problem = "Player.m_currentStation was not found";
				return false;
			}
			problem = null;
			return true;
		}
	}
	internal static class Processing
	{
		private static string s_verbItem;

		private static string s_verb;

		private static float s_verbTime = float.MinValue;

		public static ReclaimResult Analyze(ItemData item, ReclaimContext context)
		{
			if (!EpicLootSacrifice.Handles(item))
			{
				return RnrEngine.Analyze(item, context);
			}
			return EpicLootSacrifice.Run(item, context, commit: false);
		}

		public static ReclaimResult Process(ItemData item, ReclaimContext context)
		{
			if (!EpicLootSacrifice.Handles(item))
			{
				return RnrEngine.Reclaim(item, context);
			}
			return EpicLootSacrifice.Run(item, context, commit: true);
		}

		public static string Verb(ItemData item)
		{
			if (item.m_shared.m_name != s_verbItem || Time.time - s_verbTime > 1f)
			{
				s_verb = (EpicLootSacrifice.Handles(item) ? "Sacrificing" : "Reclaiming");
				s_verbItem = item.m_shared.m_name;
				s_verbTime = Time.time;
			}
			return s_verb;
		}
	}
	[HarmonyPatch(typeof(InventoryGui), "Update")]
	internal static class ReclaimAllButton
	{
		private stat