Decompiled source of DynamicStorageMaterials v2.0.4

plugins/DynamicStorageMaterials/DynamicStorageMaterials.dll

Decompiled a month ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security.Cryptography;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using Jotunn;
using Jotunn.Configs;
using Jotunn.Entities;
using Jotunn.Extensions;
using Jotunn.Managers;
using Jotunn.Utils;
using Microsoft.CodeAnalysis;
using TMPro;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Events;
using UnityEngine.Rendering;
using UnityEngine.Sprites;
using UnityEngine.TextCore.LowLevel;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.6.2", FrameworkDisplayName = ".NET Framework 4.6.2")]
[assembly: AssemblyCompany("DynamicStorageMaterials")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("2.0.4.0")]
[assembly: AssemblyInformationalVersion("2.0.4")]
[assembly: AssemblyProduct("DynamicStorageMaterials")]
[assembly: AssemblyTitle("DynamicStorageMaterials")]
[assembly: AssemblyVersion("2.0.4.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace DynamicStorageMaterials
{
	internal sealed class AppearanceRefreshQueue
	{
		private const float QuietSeconds = 0.12f;

		private const float MaximumWaitSeconds = 0.25f;

		private float firstRequest;

		private float lastRequest;

		internal bool Pending { get; private set; }

		internal void Request(float now)
		{
			if (!Pending)
			{
				firstRequest = now;
			}
			lastRequest = now;
			Pending = true;
		}

		internal bool TryTake(float now)
		{
			if (!Pending || (now < lastRequest + 0.12f && now < firstRequest + 0.25f))
			{
				return false;
			}
			Pending = false;
			return true;
		}
	}
	internal sealed class AppearanceSettings
	{
		internal const string FlintExperimentalLayout = "Flint - Experimental";

		internal const string ObsidianExperimentalLayout = "Obsidian - Experimental";

		internal const string PyramidLayout = "Pyramid";

		internal const string ModelDefaultMaterial = "Model Default";

		internal static readonly Dictionary<string, string> MaterialSources = new Dictionary<string, string>(StringComparer.Ordinal)
		{
			{ "Flint", "Flint" },
			{ "Obsidian", "Obsidian" },
			{ "Resin", "Resin" },
			{ "Greydwarf Eye", "GreydwarfEye" },
			{ "Surtling Core", "SurtlingCore" },
			{ "Crystal", "Crystal" },
			{ "Freeze Gland", "FreezeGland" },
			{ "Needle", "Needle" },
			{ "Soft Tissue", "Softtissue" },
			{ "Black Core", "BlackCore" },
			{ "Sulfur", "SulfurStone" },
			{ "Ice", "Ice" },
			{ "Frostcore", "FrostCore" },
			{ "Wolf Fang", "WolfFang" },
			{ "Coal", "Coal" },
			{ "Stone", "Stone" },
			{ "Black Marble", "BlackMarble" },
			{ "Grausten", "Grausten" }
		};

		internal static readonly Dictionary<string, string> ModelSources = new Dictionary<string, string>(StringComparer.Ordinal)
		{
			{ "Flint", "Flint" },
			{ "Obsidian", "Obsidian" },
			{ "Resin", "Resin" },
			{ "Greydwarf Eye", "GreydwarfEye" },
			{ "Surtling Core", "SurtlingCore" },
			{ "Crystal", "Crystal" },
			{ "Freeze Gland", "FreezeGland" },
			{ "Needle", "Needle" },
			{ "Soft Tissue", "Softtissue" },
			{ "Black Core", "BlackCore" },
			{ "Sulfur", "SulfurStone" },
			{ "Ice", "Ice" },
			{ "Frostcore", "FrostCore" },
			{ "Coal", "Coal" },
			{ "Stone", "Stone" },
			{ "Black Marble", "BlackMarble" },
			{ "Grausten", "Grausten" }
		};

		private readonly bool isBar;

		private readonly ModelTuningSettings modelTuning;

		private readonly ConfigEntry<string> preset;

		private readonly ConfigEntry<string> shape;

		private readonly ConfigEntry<string> model;

		private readonly ConfigEntry<string> material;

		private readonly ConfigEntry<float> layoutScale;

		private readonly ConfigEntry<float> layoutHeight;

		private readonly ConfigEntry<float> modelScale;

		private readonly ConfigEntry<float> visualVerticalOffset;

		private readonly ConfigEntry<string> finish;

		private readonly ConfigEntry<string> barArrangement;

		private readonly ConfigEntry<int> barAcross;

		private readonly ConfigEntry<int> barDeep;

		private readonly ConfigEntry<int> barLayers;

		private readonly ConfigEntry<string> fillBasis;

		private readonly ConfigEntry<string> looksFullAt;

		private readonly List<PileAppearancePreset> pilePresets = new List<PileAppearancePreset>();

		private bool updatingPreset;

		private string observedPresetValue;

		private string observedAppearanceSignature;

		private bool updatingBarDimensions;

		internal static IEnumerable<string> VisibleModelChoices => from pair in ModelSources
			where MaterialDisplayScope.Selectable(MaterialAdmissionPolicy.Group(pair.Value)) && !MaterialItemNames.IsLegacyModelAlias(pair.Key, pair.Value)
			select pair.Key;

		internal IReadOnlyList<PileAppearancePreset> EditorPresets => pilePresets;

		internal string[] EditorLayouts
		{
			get
			{
				ConfigEntry<string> obj = shape;
				return (((obj != null) ? ((ConfigEntryBase)obj).Description.AcceptableValues : null) as AcceptableValueList<string>)?.AcceptableValues ?? new string[1] { "Default" };
			}
		}

		internal string SectionName { get; }

		internal static string DisplayLayoutName(string layout)
		{
			if (string.Equals(layout, "Flint", StringComparison.Ordinal))
			{
				return "Flint - Experimental";
			}
			if (string.Equals(layout, "Obsidian", StringComparison.Ordinal))
			{
				return "Obsidian - Experimental";
			}
			return layout;
		}

		internal static void ExpandModelSources()
		{
			string[] all = MaterialCatalogIds.All;
			foreach (string text in all)
			{
				if (!ModelSources.Values.Contains(text))
				{
					ModelSources[MaterialItemNames.Fallback(text)] = text;
				}
				if (MaterialItemNames.LegacyModelIds.Contains(text))
				{
					ModelSources[text] = text;
				}
			}
		}

		internal static bool IsGeneratedPyramidLayout(string layout)
		{
			return string.Equals(layout, "Pyramid", StringComparison.Ordinal);
		}

		internal static bool IsIndependentPileLayout(string layout)
		{
			if (!MaterialPackedLayouts.Contains(layout))
			{
				switch (layout)
				{
				default:
					return layout == "Settled diamond pile";
				case "Neat pyramid":
				case "Neat pyramid 2 x 3":
				case "Neat pyramid 2 x 2":
				case "Fabric stack":
					break;
				}
			}
			return true;
		}

		internal static string[] PileLayoutChoices(string nativeShape)
		{
			return new string[1] { DisplayLayoutName(nativeShape) }.Concat(MaterialPileDisplay.Layouts.Keys.Select(DisplayLayoutName)).Distinct().ToArray();
		}

		internal static string NormalizeGeneratedLayout(string layout)
		{
			if (string.Equals(layout, "Neat Stacked", StringComparison.Ordinal) || string.Equals(layout, "Misaligned Stacked", StringComparison.Ordinal))
			{
				return "Pyramid";
			}
			return layout;
		}

		internal static string ModelNameForPrefab(string prefabName)
		{
			foreach (KeyValuePair<string, string> modelSource in ModelSources)
			{
				if (string.Equals(modelSource.Value, prefabName, StringComparison.Ordinal))
				{
					return modelSource.Key;
				}
			}
			return null;
		}

		internal AppearanceSettings(ConfigFile config, string section, bool isBar, string nativeShape, string defaultShape, string defaultModelPrefab, float defaultSize, float defaultHeight, string legacySection = null, float defaultModelScale = 1f, float defaultVerticalOffset = 0f, string defaultMaterial = null)
		{
			//IL_0285: Unknown result type (might be due to invalid IL or missing references)
			//IL_028f: Expected O, but got Unknown
			//IL_032a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0334: Expected O, but got Unknown
			//IL_0380: Unknown result type (might be due to invalid IL or missing references)
			//IL_038a: Expected O, but got Unknown
			//IL_03d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_03e0: Expected O, but got Unknown
			//IL_01d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e0: Expected O, but got Unknown
			//IL_0546: Unknown result type (might be due to invalid IL or missing references)
			//IL_0550: Expected O, but got Unknown
			//IL_058a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0594: Expected O, but got Unknown
			//IL_0806: Unknown result type (might be due to invalid IL or missing references)
			//IL_0810: Expected O, but got Unknown
			//IL_0693: Unknown result type (might be due to invalid IL or missing references)
			//IL_069d: Expected O, but got Unknown
			//IL_0722: Unknown result type (might be due to invalid IL or missing references)
			//IL_072c: Expected O, but got Unknown
			//IL_0894: Unknown result type (might be due to invalid IL or missing references)
			//IL_089e: Expected O, but got Unknown
			//IL_0977: Unknown result type (might be due to invalid IL or missing references)
			//IL_0981: Expected O, but got Unknown
			//IL_0cc9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0cd3: Expected O, but got Unknown
			//IL_0da8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0db2: Expected O, but got Unknown
			//IL_0aae: Unknown result type (might be due to invalid IL or missing references)
			//IL_0ab8: Expected O, but got Unknown
			//IL_0b0c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0b16: Expected O, but got Unknown
			//IL_0b6a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0b74: Expected O, but got Unknown
			//IL_0c0f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0c19: Expected O, but got Unknown
			ExpandModelSources();
			this.isBar = isBar;
			SectionName = section;
			if (!isBar)
			{
				modelTuning = new ModelTuningSettings(config, section, advanced: true, (section == "Black Core Pile (Storage)") ? MaterialSettledDiamondPile.ApprovedTuning() : ((section == "Sulfur Pile (Storage)") ? new AppearanceSnapshot
				{
					ModelScaleVariation = 3.33f,
					ModelReverseChance = 25f
				} : null));
			}
			Func<string, string> func = delegate(string key)
			{
				object obj = ReadSavedValue(config, section, key);
				if (obj == null)
				{
					if (!string.IsNullOrEmpty(legacySection))
					{
						return ReadSavedValue(config, legacySection, key);
					}
					obj = null;
				}
				return (string)obj;
			};
			Func<string, float?> func2 = delegate(string key)
			{
				float? num8 = ReadSavedFloat(config, section, key);
				return (num8.HasValue || string.IsNullOrEmpty(legacySection)) ? num8 : ReadSavedFloat(config, legacySection, key);
			};
			string defaultModel = ModelNameForPrefab(defaultModelPrefab) ?? ModelSources.Keys.First();
			bool flag = string.Equals(section, "Obsidian Pile (Storage)", StringComparison.Ordinal);
			PileAppearancePreset pileAppearancePreset = null;
			if (!isBar)
			{
				pileAppearancePreset = CreateDefaultPreset(section, string.IsNullOrEmpty(defaultShape) ? nativeShape : defaultShape, defaultModel, defaultSize, defaultHeight, defaultModelScale, defaultVerticalOffset, defaultMaterial);
				pilePresets.Add(pileAppearancePreset);
				AddAdditionalPresets(section, pilePresets);
			}
			if (!isBar)
			{
				string[] source = PileLayoutChoices(nativeShape);
				string text = NormalizeGeneratedLayout(DisplayLayoutName(func("Layout") ?? func("Mesh Shape")));
				string text2 = (source.Contains(text) ? text : pileAppearancePreset.Layout);
				shape = config.Bind<string>(section, "Layout", text2, new ConfigDescription("Pile placement / fill framework for the next pile. This changes where models sit, not which model is used.", (AcceptableValueBase)(object)new AcceptableValueList<string>(source.ToArray()), new object[1]
				{
					new ConfigurationManagerAttributes
					{
						Browsable = false,
						Order = 90
					}
				}));
			}
			else
			{
				string text3 = func("Bar Arrangement");
				barArrangement = config.Bind<string>(section, "Bar Arrangement", "Vanilla Cross-Stacked", new ConfigDescription("Visual arrangement saved into each newly placed bar stack. Cross-stacked styles alternate each layer by 90 degrees; Parallel Rows keeps every bar facing the same direction. Brick Stacked alternates full layers with centered N-1 layers so bars bridge the joins below instead of hanging over empty space. The Sideways brick variants use the same bond on the other horizontal axis. Misaligned variants keep the same structure but add small deterministic hand-stacked variation.", (AcceptableValueBase)(object)new AcceptableValueList<string>(new string[8] { "Vanilla Cross-Stacked", "Misaligned Cross-Stacked", "Parallel Rows", "Misaligned Parallel Rows", "Brick Stacked", "Misaligned Brick Stacked", "Brick Stacked Sideways", "Misaligned Brick Stacked Sideways" }), new object[1]
				{
					new ConfigurationManagerAttributes
					{
						Browsable = false,
						Order = 100
					}
				}));
				barArrangement.Value = NormalizeBarArrangement(string.IsNullOrEmpty(text3) ? barArrangement.Value : text3);
				string value = func("Bars Across");
				string value2 = func("Bars Deep");
				string value3 = func("Layers High");
				barAcross = config.Bind<int>(section, "Bars Across", 2, new ConfigDescription("Horizontal footprint from left to right. Cross-stacked arrangements use 2x2 woven modules, so odd values are rounded down to the nearest even value (minimum 2). The server-wide visual bar cap is set in General Settings.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 12), new object[1]
				{
					new ConfigurationManagerAttributes
					{
						Browsable = false,
						Order = 90
					}
				}));
				barDeep = config.Bind<int>(section, "Bars Deep", 2, new ConfigDescription("Horizontal footprint from front to back. Cross-stacked and Brick Stacked arrangements require an even depth, so odd values are rounded down to the nearest even value (minimum 2). Other arrangements can use any value from 1 to 12. The server-wide visual bar cap is set in General Settings.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 12), new object[1]
				{
					new ConfigurationManagerAttributes
					{
						Browsable = false,
						Order = 80
					}
				}));
				barLayers = config.Bind<int>(section, "Layers High", 5, new ConfigDescription("Number of vertical layers when the visual is full. The server-wide visual bar cap is set in General Settings.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 20), new object[1]
				{
					new ConfigurationManagerAttributes
					{
						Browsable = false,
						Order = 70
					}
				}));
				if (string.IsNullOrEmpty(value) && int.TryParse(func("Columns"), out var result))
				{
					barAcross.Value = Mathf.Clamp(result, 1, 12);
				}
				if (string.IsNullOrEmpty(value3) && int.TryParse(func("Rows"), out var result2))
				{
					barLayers.Value = Mathf.Clamp(result2, 1, 20);
				}
				if (string.IsNullOrEmpty(value2))
				{
					barDeep.Value = 2;
				}
				barArrangement.Value = NormalizeBarArrangement(barArrangement.Value);
				SanitizeBarDimensions();
				barArrangement.SettingChanged += OnBarLayoutValueChanged;
				barAcross.SettingChanged += OnBarLayoutValueChanged;
				barDeep.SettingChanged += OnBarLayoutValueChanged;
				barLayers.SettingChanged += OnBarLayoutValueChanged;
			}
			if (!isBar)
			{
				model = config.Bind<string>(section, "Model", pileAppearancePreset.Model, new ConfigDescription("Actual item model repeated through the selected pile layout for the next placement.", (AcceptableValueBase)(object)new AcceptableValueList<string>(ModelSources.Keys.ToArray()), new object[1]
				{
					new ConfigurationManagerAttributes
					{
						Browsable = false,
						Order = 60,
						IsAdvanced = true
					}
				}));
				if (flag)
				{
					ConfigEntry<bool> val = config.Bind<bool>("Internal Migrations", "Obsidian Model POC Migrated 0.2.9", false, new ConfigDescription("Internal one-time migration marker.", (AcceptableValueBase)null, new object[1]
					{
						new ConfigurationManagerAttributes
						{
							Browsable = false
						}
					}));
					if (!val.Value)
					{
						if (string.Equals(model.Value, "Flint", StringComparison.Ordinal))
						{
							model.Value = "Obsidian";
						}
						val.Value = true;
					}
				}
			}
			if (isBar)
			{
				string value4 = func("Fill Based On") ?? ReadSavedValue(config, "Storage Capacity", "Ingot Stack Fill Based On") ?? "Occupied Slots";
				string value5 = func("Looks Full At") ?? ReadSavedValue(config, "Storage Capacity", "Ingot Stack Looks Full At") ?? "100%";
				fillBasis = config.Bind<string>(section, "Fill Based On", DynamicStorageMaterialsPlugin.NormalizeBarFillBasis(value4), new ConfigDescription("How this stack's visual growth is measured. Occupied Slots counts used inventory slots; Stored Bar Count uses the actual number of ingots stored. Saved into each placed stack.", (AcceptableValueBase)(object)new AcceptableValueList<string>(new string[2] { "Occupied Slots", "Stored Bar Count" }), new object[1]
				{
					new ConfigurationManagerAttributes
					{
						Browsable = false,
						Order = 50
					}
				}));
				looksFullAt = config.Bind<string>(section, "Looks Full At", DynamicStorageMaterialsPlugin.NormalizeBarLooksFullAt(value5), new ConfigDescription("How quickly this stack reaches its fully configured visual. 50% means the visual is full at half of the selected fill basis; Always Full disables dynamic visual fill. Saved into each placed stack.", (AcceptableValueBase)(object)new AcceptableValueList<string>(new string[6] { "10%", "25%", "50%", "75%", "100%", "Always Full" }), new object[1]
				{
					new ConfigurationManagerAttributes
					{
						Browsable = false,
						Order = 45
					}
				}));
			}
			else
			{
				string text4 = func("Looks Full At");
				string value6 = ReadSavedValue(config, "Storage Capacity", "Ore Pile Looks Full At");
				string value7 = text4;
				if (string.IsNullOrEmpty(value7))
				{
					string text5 = DynamicStorageMaterialsPlugin.NormalizePileLooksFullAt(value6);
					value7 = (string.Equals(text5, "Always Full", StringComparison.Ordinal) ? "Match Storage Capacity" : text5);
				}
				looksFullAt = config.Bind<string>(section, "Looks Full At", DynamicStorageMaterialsPlugin.NormalizePileLooksFullAt(value7), new ConfigDescription("How quickly this pile reaches its full visual. Match Storage Capacity follows the real slot capacity; 10/20/40/60 slots reaches full at that many occupied slots; Always Full disables dynamic visual fill. Saved into each placed pile.", (AcceptableValueBase)(object)new AcceptableValueList<string>(new string[6] { "Match Storage Capacity", "10 slots", "20 slots", "40 slots", "60 slots", "Always Full" }), new object[1]
				{
					new ConfigurationManagerAttributes
					{
						Browsable = false,
						Order = 10
					}
				}));
				if (string.Equals(section, "Flint Pile (Storage)", StringComparison.Ordinal) || string.Equals(section, "Obsidian Pile (Storage)", StringComparison.Ordinal) || string.Equals(section, "Frostcore Pile (Storage)", StringComparison.Ordinal))
				{
					string text6 = section + " Looks Full At Migrated 1.0.1";
					ConfigEntry<bool> val2 = config.Bind<bool>("Internal Migrations", text6, false, new ConfigDescription("Internal one-time migration marker.", (AcceptableValueBase)null, new object[1]
					{
						new ConfigurationManagerAttributes
						{
							Browsable = false
						}
					}));
					if (!val2.Value)
					{
						if (string.Equals(looksFullAt.Value, "Always Full", StringComparison.Ordinal))
						{
							looksFullAt.Value = "Match Storage Capacity";
						}
						val2.Value = true;
					}
				}
			}
			List<string> list = new List<string> { isBar ? "Default" : "Model Default" };
			list.AddRange(MaterialSources.Keys);
			material = config.Bind<string>(section, "Material", isBar ? "Default" : pileAppearancePreset.Material, new ConfigDescription(isBar ? "Vanilla material for the next placement. Default keeps the stack's native material." : "Material override for the next placement. Model Default uses the native material of whichever Model is selected.", (AcceptableValueBase)(object)new AcceptableValueList<string>(list.ToArray()), new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Browsable = false,
					Order = 40,
					IsAdvanced = true
				}
			}));
			if (!isBar && string.Equals(func("Material"), "Default", StringComparison.Ordinal))
			{
				material.Value = "Model Default";
			}
			if (!isBar)
			{
				float num = func2("Size") ?? pileAppearancePreset.LayoutScale;
				float num2 = func2("Height") ?? pileAppearancePreset.LayoutHeight;
				float num3 = func2("Layout Scale") ?? num;
				float num4 = func2("Layout Height") ?? num2;
				float num5 = func2("Model Scale") ?? pileAppearancePreset.ModelScale;
				layoutScale = config.Bind<float>(section, "Layout Scale", num3, new ConfigDescription("Scales the spacing / footprint of the pile layout without enlarging the individual models.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 3f), new object[1]
				{
					new ConfigurationManagerAttributes
					{
						Browsable = false,
						Order = 80
					}
				}));
				layoutHeight = config.Bind<float>(section, "Layout Height", num4, new ConfigDescription("Scales only the vertical spacing of the pile layout. The individual item models are not stretched.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 8f), new object[1]
				{
					new ConfigurationManagerAttributes
					{
						Browsable = false,
						Order = 70
					}
				}));
				modelScale = config.Bind<float>(section, "Model Scale", num5, new ConfigDescription("Uniformly scales each repeated item model without changing the pile layout spacing. 1.0 matches that item's native dropped-world size.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.25f, 3f), new object[1]
				{
					new ConfigurationManagerAttributes
					{
						Browsable = false,
						Order = 50
					}
				}));
				float num6 = func2("Visual Vertical Offset") ?? func2("Visual Y Offset") ?? pileAppearancePreset.VisualVerticalOffset;
				visualVerticalOffset = config.Bind<float>(section, "Visual Vertical Offset", num6, new ConfigDescription("Moves only the pile visuals vertically after layout/model scaling. Negative values sink floating models; the piece root and container stay in place.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(-0.5f, 0.5f), new object[1]
				{
					new ConfigurationManagerAttributes
					{
						Browsable = false,
						Order = 20
					}
				}));
			}
			string text7 = func("Finish");
			finish = config.Bind<string>(section, "Finish", isBar ? "As Material" : pileAppearancePreset.Finish, new ConfigDescription("Advanced material finish for the visible models. Matte/Satin/Glossy adjust smoothness; Metallic and Polished Metal also force a metallic response. Debug Emissive is an intentionally extreme bright test used to verify that finish changes are reaching the rendered model.", (AcceptableValueBase)(object)new AcceptableValueList<string>(new string[7] { "As Material", "Matte", "Satin", "Glossy", "Metallic", "Polished Metal", "Debug Emissive" }), new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Browsable = false,
					Order = 30,
					IsAdvanced = true
				}
			}));
			if (text7 == "Original")
			{
				finish.Value = "As Material";
			}
			if (text7 == "Shiny")
			{
				finish.Value = "Glossy";
			}
			if (isBar)
			{
				return;
			}
			List<string> list2 = pilePresets.Select((PileAppearancePreset entry) => entry.Name).ToList();
			list2.Add("Custom");
			preset = config.Bind<string>(section, "Preset", pileAppearancePreset.Name, new ConfigDescription("Curated appearance recipe. Selecting a preset populates the settings below; changing any individual value automatically changes this to Custom.", (AcceptableValueBase)(object)new AcceptableValueList<string>(list2.ToArray()), new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Browsable = false,
					Order = 100
				}
			}));
			RefreshPresetFromValues();
			modelTuning.Changed += OnAppearanceValueChanged;
			preset.SettingChanged += OnPresetChanged;
			shape.SettingChanged += OnAppearanceValueChanged;
			model.SettingChanged += OnAppearanceValueChanged;
			material.SettingChanged += OnAppearanceValueChanged;
			layoutScale.SettingChanged += OnAppearanceValueChanged;
			layoutHeight.SettingChanged += OnAppearanceValueChanged;
			modelScale.SettingChanged += OnAppearanceValueChanged;
			visualVerticalOffset.SettingChanged += OnAppearanceValueChanged;
			finish.SettingChanged += OnAppearanceValueChanged;
			looksFullAt.SettingChanged += OnAppearanceValueChanged;
			RememberObservedPresetState();
			ConfigEntryBase[] array = (ConfigEntryBase[])(object)new ConfigEntryBase[10]
			{
				(ConfigEntryBase)preset,
				(ConfigEntryBase)shape,
				(ConfigEntryBase)model,
				(ConfigEntryBase)material,
				(ConfigEntryBase)layoutScale,
				(ConfigEntryBase)layoutHeight,
				(ConfigEntryBase)modelScale,
				(ConfigEntryBase)visualVerticalOffset,
				(ConfigEntryBase)finish,
				(ConfigEntryBase)looksFullAt
			};
			for (int num7 = 0; num7 < array.Length; num7++)
			{
				foreach (ConfigurationManagerAttributes item in array[num7].Description.Tags.OfType<ConfigurationManagerAttributes>())
				{
					item.IsAdvanced = true;
				}
			}
		}

		private static PileAppearancePreset CreateDefaultPreset(string section, string fallbackLayout, string defaultModel, float defaultLayoutScale, float defaultLayoutHeight, float defaultModelScale, float defaultVerticalOffset, string defaultMaterial)
		{
			string text = DisplayLayoutName(string.IsNullOrEmpty(fallbackLayout) ? "Stone" : fallbackLayout);
			string text2 = (string.IsNullOrEmpty(defaultMaterial) ? "Model Default" : defaultMaterial);
			return new PileAppearancePreset("Default (" + text + ")", text, defaultModel, text2, "As Material", defaultLayoutScale, defaultLayoutHeight, defaultModelScale, defaultVerticalOffset);
		}

		private static void AddAdditionalPresets(string section, List<PileAppearancePreset> presets)
		{
			if (section == "Black Core Pile (Storage)")
			{
				presets.Add(MaterialSettledDiamondPile.Preset());
			}
		}

		private void OnPresetChanged(object sender, EventArgs args)
		{
			if (!updatingPreset && !isBar && preset != null)
			{
				ApplySelectedPresetOrRefresh();
				RememberObservedPresetState();
			}
		}

		private void OnAppearanceValueChanged(object sender, EventArgs args)
		{
			if (!updatingPreset)
			{
				RefreshPresetFromValues();
				RememberObservedPresetState();
			}
		}

		internal void SynchronizePresetSelection()
		{
			if (updatingPreset || isBar || preset == null)
			{
				return;
			}
			string value = preset.Value;
			string a = BuildAppearanceSignature();
			bool flag = !string.Equals(value, observedPresetValue, StringComparison.Ordinal);
			bool flag2 = !string.Equals(a, observedAppearanceSignature, StringComparison.Ordinal);
			if (flag || flag2)
			{
				if (flag)
				{
					ApplySelectedPresetOrRefresh();
				}
				else
				{
					RefreshPresetFromValues();
				}
				RememberObservedPresetState();
			}
		}

		private void ApplySelectedPresetOrRefresh()
		{
			if (string.Equals(preset.Value, "Custom", StringComparison.Ordinal))
			{
				RefreshPresetFromValues();
				return;
			}
			PileAppearancePreset pileAppearancePreset = pilePresets.FirstOrDefault((PileAppearancePreset entry) => string.Equals(entry.Name, preset.Value, StringComparison.Ordinal));
			if (pileAppearancePreset == null)
			{
				RefreshPresetFromValues();
			}
			else
			{
				ApplyPreset(pileAppearancePreset);
			}
		}

		private void ApplyPreset(PileAppearancePreset selected)
		{
			if (selected == null)
			{
				return;
			}
			updatingPreset = true;
			try
			{
				modelTuning.Reset();
				shape.Value = selected.Layout;
				model.Value = selected.Model;
				material.Value = selected.Material;
				layoutScale.Value = selected.LayoutScale;
				layoutHeight.Value = selected.LayoutHeight;
				modelScale.Value = selected.ModelScale;
				visualVerticalOffset.Value = selected.VisualVerticalOffset;
				finish.Value = selected.Finish;
				if (selected.Layout != "Settled diamond pile")
				{
					looksFullAt.Value = selected.LooksFullAt;
				}
				preset.Value = selected.Name;
			}
			finally
			{
				updatingPreset = false;
			}
		}

		private void RefreshPresetFromValues()
		{
			if (isBar || preset == null)
			{
				return;
			}
			PileAppearancePreset pileAppearancePreset = pilePresets.FirstOrDefault(MatchesPreset);
			string text = ((pileAppearancePreset != null) ? pileAppearancePreset.Name : "Custom");
			if (string.Equals(preset.Value, text, StringComparison.Ordinal))
			{
				return;
			}
			updatingPreset = true;
			try
			{
				preset.Value = text;
			}
			finally
			{
				updatingPreset = false;
			}
		}

		private void RememberObservedPresetState()
		{
			if (!isBar && preset != null)
			{
				observedPresetValue = preset.Value;
				observedAppearanceSignature = BuildAppearanceSignature();
			}
		}

		private string BuildAppearanceSignature()
		{
			if (isBar)
			{
				return string.Empty;
			}
			return string.Join("|", modelTuning?.Identity ?? string.Empty, shape.Value ?? string.Empty, model.Value ?? string.Empty, material.Value ?? string.Empty, finish.Value ?? string.Empty, layoutScale.Value.ToString("R", CultureInfo.InvariantCulture), layoutHeight.Value.ToString("R", CultureInfo.InvariantCulture), modelScale.Value.ToString("R", CultureInfo.InvariantCulture), visualVerticalOffset.Value.ToString("R", CultureInfo.InvariantCulture), looksFullAt.Value ?? string.Empty);
		}

		private bool MatchesPreset(PileAppearancePreset candidate)
		{
			if (candidate == null)
			{
				return false;
			}
			if ((modelTuning == null || modelTuning.MatchesDefaults) && string.Equals(shape.Value, candidate.Layout, StringComparison.Ordinal) && string.Equals(model.Value, candidate.Model, StringComparison.Ordinal) && string.Equals(material.Value, candidate.Material, StringComparison.Ordinal) && string.Equals(finish.Value, candidate.Finish, StringComparison.Ordinal) && Mathf.Abs(layoutScale.Value - candidate.LayoutScale) <= 0.0005f && Mathf.Abs(layoutHeight.Value - candidate.LayoutHeight) <= 0.0005f && Mathf.Abs(modelScale.Value - candidate.ModelScale) <= 0.0005f && Mathf.Abs(visualVerticalOffset.Value - candidate.VisualVerticalOffset) <= 0.0005f)
			{
				if (!(candidate.Layout == "Settled diamond pile"))
				{
					return string.Equals(looksFullAt.Value, candidate.LooksFullAt, StringComparison.Ordinal);
				}
				return true;
			}
			return false;
		}

		internal static string NormalizeBarArrangement(string value)
		{
			if (string.IsNullOrEmpty(value))
			{
				return "Vanilla Cross-Stacked";
			}
			if (string.Equals(value, "Vanilla", StringComparison.Ordinal))
			{
				return "Vanilla Cross-Stacked";
			}
			if (string.Equals(value, "Irregular", StringComparison.Ordinal) || string.Equals(value, "Misaligned", StringComparison.Ordinal))
			{
				return "Misaligned Parallel Rows";
			}
			if (string.Equals(value, "Aligned", StringComparison.Ordinal) || string.Equals(value, "Side by Side", StringComparison.Ordinal) || string.Equals(value, "Long Side by Side", StringComparison.Ordinal))
			{
				return "Parallel Rows";
			}
			switch (value)
			{
			case "Misaligned Brick Stacked":
			case "Misaligned Cross-Stacked":
			case "Misaligned Parallel Rows":
			case "Brick Stacked":
			case "Parallel Rows":
			case "Vanilla Cross-Stacked":
			case "Brick Stacked Sideways":
			case "Misaligned Brick Stacked Sideways":
				return value;
			default:
				return "Vanilla Cross-Stacked";
			}
		}

		internal static bool IsCrossStackedArrangement(string arrangement)
		{
			arrangement = NormalizeBarArrangement(arrangement);
			if (!(arrangement == "Vanilla Cross-Stacked"))
			{
				return arrangement == "Misaligned Cross-Stacked";
			}
			return true;
		}

		internal static bool IsBrickStackedArrangement(string arrangement)
		{
			arrangement = NormalizeBarArrangement(arrangement);
			switch (arrangement)
			{
			default:
				return arrangement == "Misaligned Brick Stacked Sideways";
			case "Brick Stacked":
			case "Misaligned Brick Stacked":
			case "Brick Stacked Sideways":
				return true;
			}
		}

		internal static bool IsBrickStackedSidewaysArrangement(string arrangement)
		{
			arrangement = NormalizeBarArrangement(arrangement);
			if (!(arrangement == "Brick Stacked Sideways"))
			{
				return arrangement == "Misaligned Brick Stacked Sideways";
			}
			return true;
		}

		internal static bool IsMisalignedArrangement(string arrangement)
		{
			arrangement = NormalizeBarArrangement(arrangement);
			switch (arrangement)
			{
			default:
				return arrangement == "Misaligned Brick Stacked Sideways";
			case "Misaligned Cross-Stacked":
			case "Misaligned Parallel Rows":
			case "Misaligned Brick Stacked":
				return true;
			}
		}

		internal static int NormalizeCrossStackDimension(int value)
		{
			value = Mathf.Clamp(value, 2, 12);
			if ((value & 1) != 0)
			{
				value--;
			}
			return Mathf.Max(2, value);
		}

		internal static int NormalizeBrickDeepDimension(int value)
		{
			value = Mathf.Clamp(value, 2, 12);
			if ((value & 1) != 0)
			{
				value--;
			}
			return Mathf.Max(2, value);
		}

		internal static int CalculateBarVisualCount(string arrangement, int across, int deep, int layers)
		{
			across = Mathf.Clamp(across, 1, 12);
			deep = Mathf.Clamp(deep, 1, 12);
			layers = Mathf.Clamp(layers, 1, 20);
			arrangement = NormalizeBarArrangement(arrangement);
			if (IsCrossStackedArrangement(arrangement))
			{
				across = NormalizeCrossStackDimension(across);
				deep = NormalizeCrossStackDimension(deep);
				return across * deep / 2 * layers;
			}
			if (IsBrickStackedArrangement(arrangement))
			{
				deep = NormalizeBrickDeepDimension(deep);
				bool num = IsBrickStackedSidewaysArrangement(arrangement);
				int num2 = (layers + 1) / 2;
				int num3 = layers / 2;
				int num4 = across * deep;
				int num5 = 0;
				int num6;
				if (num)
				{
					num6 = ((across > 1) ? (across - 1) : across) * deep;
					if (across > 1)
					{
						num5 = deep;
					}
				}
				else
				{
					num6 = across * ((deep > 1) ? (deep - 1) : deep);
					if (deep > 1)
					{
						num5 = (across + 1) / 2 * 2;
					}
				}
				return num2 * num4 + num3 * (num6 + num5);
			}
			return across * deep * layers;
		}

		internal static void SanitizeBarDimensionsToLimit(string arrangement, ref int across, ref int deep, ref int layers, int maxVisualBars)
		{
			arrangement = NormalizeBarArrangement(arrangement);
			bool flag = IsCrossStackedArrangement(arrangement);
			bool flag2 = IsBrickStackedArrangement(arrangement);
			across = Mathf.Clamp(across, 1, 12);
			deep = Mathf.Clamp(deep, 1, 12);
			layers = Mathf.Clamp(layers, 1, 20);
			if (flag)
			{
				across = NormalizeCrossStackDimension(across);
				deep = NormalizeCrossStackDimension(deep);
			}
			else if (flag2)
			{
				deep = NormalizeBrickDeepDimension(deep);
			}
			maxVisualBars = Mathf.Clamp(maxVisualBars, 10, 400);
			while (layers > 1 && CalculateBarVisualCount(arrangement, across, deep, layers) > maxVisualBars)
			{
				layers--;
			}
			int num = ((!(flag || flag2)) ? 1 : 2);
			int num2 = ((!(flag || flag2)) ? 1 : 2);
			int num3 = ((!flag) ? 1 : 2);
			int num4 = ((!flag) ? 1 : 2);
			while (deep > num && CalculateBarVisualCount(arrangement, across, deep, layers) > maxVisualBars)
			{
				deep -= num2;
			}
			while (across > num3 && CalculateBarVisualCount(arrangement, across, deep, layers) > maxVisualBars)
			{
				across -= num4;
			}
			while (layers > 1 && CalculateBarVisualCount(arrangement, across, deep, layers) > maxVisualBars)
			{
				layers--;
			}
		}

		private void OnBarLayoutValueChanged(object sender, EventArgs args)
		{
			if (!updatingBarDimensions)
			{
				SanitizeBarDimensions();
			}
		}

		private void SanitizeBarDimensions()
		{
			if (!isBar || barAcross == null || barDeep == null || barLayers == null || barArrangement == null)
			{
				return;
			}
			updatingBarDimensions = true;
			try
			{
				barArrangement.Value = NormalizeBarArrangement(barArrangement.Value);
				int across = barAcross.Value;
				int deep = barDeep.Value;
				int layers = barLayers.Value;
				SanitizeBarDimensionsToLimit(barArrangement.Value, ref across, ref deep, ref layers, DynamicStorageMaterialsPlugin.CurrentMaxVisualBarsPerStack);
				barAcross.Value = across;
				barDeep.Value = deep;
				barLayers.Value = layers;
			}
			finally
			{
				updatingBarDimensions = false;
			}
		}

		private static string ReadSavedValue(ConfigFile config, string section, string key)
		{
			try
			{
				if (!File.Exists(config.ConfigFilePath))
				{
					return null;
				}
				bool flag = false;
				string text = key + " = ";
				foreach (string item in File.ReadLines(config.ConfigFilePath))
				{
					string text2 = item.Trim();
					if (text2.StartsWith("[", StringComparison.Ordinal) && text2.EndsWith("]", StringComparison.Ordinal))
					{
						flag = text2.Substring(1, text2.Length - 2) == section;
					}
					else if (flag && text2.StartsWith(text, StringComparison.Ordinal))
					{
						return text2.Substring(text.Length).Trim();
					}
				}
			}
			catch (IOException)
			{
			}
			catch (UnauthorizedAccessException)
			{
			}
			return null;
		}

		private static float? ReadSavedFloat(ConfigFile config, string section, string key)
		{
			string text = ReadSavedValue(config, section, key);
			if (string.IsNullOrEmpty(text))
			{
				return null;
			}
			if (float.TryParse(text, NumberStyles.Float, CultureInfo.InvariantCulture, out var result))
			{
				return result;
			}
			if (float.TryParse(text, out var result2))
			{
				return result2;
			}
			return null;
		}

		internal AppearanceSnapshot DefinedDefaults()
		{
			PileAppearancePreset pileAppearancePreset = pilePresets.FirstOrDefault();
			if (pileAppearancePreset == null)
			{
				return null;
			}
			AppearanceSnapshot obj = ((modelTuning != null) ? modelTuning.DefinedDefaults : new AppearanceSnapshot());
			obj.Preset = pileAppearancePreset.Name;
			obj.Shape = pileAppearancePreset.Layout;
			obj.Model = pileAppearancePreset.Model;
			obj.Material = pileAppearancePreset.Material;
			obj.Finish = pileAppearancePreset.Finish;
			obj.LayoutScale = pileAppearancePreset.LayoutScale;
			obj.LayoutHeight = pileAppearancePreset.LayoutHeight;
			obj.ModelScale = pileAppearancePreset.ModelScale;
			obj.VisualVerticalOffset = pileAppearancePreset.VisualVerticalOffset;
			return obj;
		}

		internal AppearanceSnapshot Capture()
		{
			AppearanceSnapshot appearanceSnapshot = new AppearanceSnapshot
			{
				Preset = ((preset == null) ? "Custom" : preset.Value),
				Shape = ((shape == null) ? "Default" : shape.Value),
				Model = ((model == null) ? "Default" : model.Value),
				Material = material.Value,
				Tint = "#FFFFFF",
				Finish = finish.Value,
				BarArrangement = ((barArrangement == null) ? "Vanilla Cross-Stacked" : NormalizeBarArrangement(barArrangement.Value)),
				BarAcross = ((barAcross == null) ? 2 : Mathf.Clamp(barAcross.Value, 1, 12)),
				BarDeep = ((barDeep == null) ? 2 : Mathf.Clamp(barDeep.Value, 1, 12)),
				BarLayers = ((barLayers == null) ? 5 : Mathf.Clamp(barLayers.Value, 1, 20)),
				FillBasis = ((isBar && fillBasis != null) ? DynamicStorageMaterialsPlugin.NormalizeBarFillBasis(fillBasis.Value) : "Occupied Slots"),
				LooksFullAt = (isBar ? DynamicStorageMaterialsPlugin.NormalizeBarLooksFullAt((looksFullAt != null) ? looksFullAt.Value : "100%") : DynamicStorageMaterialsPlugin.NormalizePileLooksFullAt((looksFullAt != null) ? looksFullAt.Value : "Match Storage Capacity"))
			};
			if (isBar)
			{
				appearanceSnapshot.Scale = 1f;
				int across = appearanceSnapshot.BarAcross;
				int deep = appearanceSnapshot.BarDeep;
				int layers = appearanceSnapshot.BarLayers;
				SanitizeBarDimensionsToLimit(appearanceSnapshot.BarArrangement, ref across, ref deep, ref layers, DynamicStorageMaterialsPlugin.CurrentMaxVisualBarsPerStack);
				appearanceSnapshot.BarAcross = across;
				appearanceSnapshot.BarDeep = deep;
				appearanceSnapshot.BarLayers = layers;
			}
			else
			{
				appearanceSnapshot.LayoutScale = Mathf.Clamp(layoutScale.Value, 0.5f, 3f);
				appearanceSnapshot.LayoutHeight = Mathf.Clamp(layoutHeight.Value, 0.1f, 8f);
				appearanceSnapshot.ModelScale = Mathf.Clamp(modelScale.Value, 0.25f, 3f);
				appearanceSnapshot.VisualVerticalOffset = Mathf.Clamp(visualVerticalOffset.Value, -0.5f, 0.5f);
			}
			modelTuning?.Capture(appearanceSnapshot);
			return appearanceSnapshot;
		}
	}
	internal sealed class PileAppearancePreset
	{
		internal string Name { get; }

		internal string Layout { get; }

		internal string Model { get; }

		internal string Material { get; }

		internal string Finish { get; }

		internal float LayoutScale { get; }

		internal float LayoutHeight { get; }

		internal float ModelScale { get; }

		internal float VisualVerticalOffset { get; }

		internal string LooksFullAt { get; }

		internal PileAppearancePreset(string name, string layout, string model, string material, string finish, float layoutScale, float layoutHeight, float modelScale, float visualVerticalOffset, string looksFullAt = "Match Storage Capacity")
		{
			Name = name;
			Layout = layout;
			Model = model;
			Material = material;
			Finish = finish;
			LayoutScale = layoutScale;
			LayoutHeight = layoutHeight;
			ModelScale = modelScale;
			VisualVerticalOffset = visualVerticalOffset;
			LooksFullAt = DynamicStorageMaterialsPlugin.NormalizePileLooksFullAt(looksFullAt);
		}
	}
	internal sealed class AppearanceSnapshot
	{
		internal float ModelPitch;

		internal float ModelYaw;

		internal float ModelRoll;

		internal float ModelScaleVariation;

		internal float ModelReverseChance;

		internal float StrapOuterRotation = 180f;

		internal int WoodBottomLogs;

		internal int WoodRows;

		internal float WoodTwist;

		internal float WoodStagger;

		internal string Preset = "Custom";

		internal string Shape = "Default";

		internal string Model = "Default";

		internal string Material = "Default";

		internal float Scale = 1f;

		internal float LayoutScale = 1f;

		internal float LayoutHeight = 1f;

		internal float LayoutWidth = 1f;

		internal float LayoutDepth = 1f;

		internal float ModelScale = 1f;

		internal float VisualVerticalOffset;

		internal string Tint = "#FFFFFF";

		internal string Finish = "As Material";

		internal string BarArrangement = "Vanilla Cross-Stacked";

		internal int BarAcross = 2;

		internal int BarDeep = 2;

		internal int BarLayers = 5;

		internal string FillBasis = "Occupied Slots";

		internal string LooksFullAt = "Match Storage Capacity";

		internal string Identity => Shape + "|" + Model + "|" + Material + "|" + Scale.ToString("R", CultureInfo.InvariantCulture) + "|" + LayoutScale.ToString("R", CultureInfo.InvariantCulture) + "|" + LayoutHeight.ToString("R", CultureInfo.InvariantCulture) + "|" + ModelScale.ToString("R", CultureInfo.InvariantCulture) + "|" + VisualVerticalOffset.ToString("R", CultureInfo.InvariantCulture) + "|" + Tint + "|" + Finish + "|" + BarArrangement + "|" + BarAcross.ToString(CultureInfo.InvariantCulture) + "|" + BarDeep.ToString(CultureInfo.InvariantCulture) + "|" + BarLayers.ToString(CultureInfo.InvariantCulture) + "|" + FillBasis + "|" + LooksFullAt + "|" + ModelTuning.Identity(this);

		internal AppearanceSnapshot Copy()
		{
			return (AppearanceSnapshot)MemberwiseClone();
		}

		internal static string NormalizeTint(string value)
		{
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			string text = (value ?? "").Trim();
			if (!text.StartsWith("#", StringComparison.Ordinal))
			{
				text = "#" + text;
			}
			Color val = default(Color);
			if (!ColorUtility.TryParseHtmlString(text, ref val))
			{
				return "#FFFFFF";
			}
			return "#" + ColorUtility.ToHtmlStringRGB(val);
		}
	}
	internal sealed class ConfigurationManagerAttributes
	{
		public string DispName;

		public int? Order;

		public bool? IsAdminOnly;

		public bool? ReadOnly;

		public bool? Browsable;

		public bool? IsAdvanced;

		public Action<ConfigEntryBase> CustomDrawer;
	}
	[BepInPlugin("com.muji.DynamicStorageMaterials", "Dynamic Storage Materials", "2.0.4")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[NetworkCompatibility(/*Could not decode attribute arguments.*/)]
	internal sealed class DynamicStorageMaterialsPlugin : BaseUnityPlugin
	{
		private sealed class BarDefinition
		{
			public string BasePiecePrefab { get; }

			public string DestroyedVfxPrefab { get; }

			public string ResourceItemPrefab { get; }

			public string NewPrefabName { get; }

			public string DisplayName { get; }

			public int BuildCost { get; }

			public BarDefinition(string basePiecePrefab, string destroyedVfxPrefab, string resourceItemPrefab, string newPrefabName, string displayName, int buildCost)
			{
				BasePiecePrefab = basePiecePrefab;
				DestroyedVfxPrefab = destroyedVfxPrefab;
				ResourceItemPrefab = resourceItemPrefab;
				NewPrefabName = newPrefabName;
				DisplayName = displayName;
				BuildCost = buildCost;
			}
		}

		private sealed class TemplateDefinition
		{
			public string BasePilePrefab { get; }

			public string MaterialSourcePrefab { get; }

			public string ResourceItemPrefab { get; }

			public string NewPrefabName { get; }

			public string DisplayName { get; }

			public int BuildCost { get; }

			public float LocalScaleMultiplier { get; }

			public string DefaultShape { get; }

			public float DefaultSize { get; }

			public float DefaultHeight { get; }

			public float DefaultModelScale { get; }

			public float DefaultVerticalOffset { get; }

			public string VisualModelPrefab { get; }

			public string DefaultMaterial { get; }

			public bool ForceResourceModelGeometry { get; }

			public string ConfigSectionName { get; }

			public string LegacyConfigSectionName { get; }

			public TemplateDefinition(string basePilePrefab, string materialSourcePrefab, string resourceItemPrefab, string newPrefabName, string displayName, int buildCost, float localScaleMultiplier = 1f, string defaultShape = null, float defaultSize = 1f, float defaultHeight = 1f, string configSectionName = null, string legacyConfigSectionName = null, float defaultModelScale = 1f, float defaultVerticalOffset = 0f, string visualModelPrefab = null, string defaultMaterial = null, bool forceResourceModelGeometry = false)
			{
				BasePilePrefab = basePilePrefab;
				MaterialSourcePrefab = materialSourcePrefab;
				ResourceItemPrefab = resourceItemPrefab;
				NewPrefabName = newPrefabName;
				DisplayName = displayName;
				BuildCost = buildCost;
				LocalScaleMultiplier = localScaleMultiplier;
				DefaultShape = defaultShape;
				DefaultSize = defaultSize;
				DefaultHeight = defaultHeight;
				DefaultModelScale = defaultModelScale;
				DefaultVerticalOffset = defaultVerticalOffset;
				VisualModelPrefab = (string.IsNullOrEmpty(visualModelPrefab) ? resourceItemPrefab : visualModelPrefab);
				DefaultMaterial = (string.IsNullOrEmpty(defaultMaterial) ? "Model Default" : defaultMaterial);
				ForceResourceModelGeometry = forceResourceModelGeometry;
				ConfigSectionName = (string.IsNullOrEmpty(configSectionName) ? displayName : configSectionName);
				LegacyConfigSectionName = legacyConfigSectionName;
			}
		}

		public const string PluginGuid = "com.muji.DynamicStorageMaterials";

		public const string PluginName = "Dynamic Storage Materials";

		public const string PluginVersion = "2.0.4";

		private Harmony placementHarmony;

		private Harmony restrictionHarmony;

		private readonly List<CustomPiece> piecesNeedingIcons = new List<CustomPiece>();

		private readonly List<GameObject> forgeBuildPiecePrefabs = new List<GameObject>();

		private bool pieceIconsQueued;

		private ConfigEntry<bool> showMeshDebugHover;

		private ConfigEntry<bool> debugLogging;

		private ConfigEntry<bool> showOlderPileNotice;

		private ConfigEntry<bool> showTechnicalDebugHover;

		private ConfigEntry<bool> showDroppedModelDebugHover;

		private ConfigEntry<bool> showStorageDebugTooltip;

		private ConfigEntry<bool> allowDeletionOfProtectedPiles;

		private ConfigEntry<bool> enableApplyConfigDebug;

		private ConfigEntry<int> orePileStorageCapacity;

		private ConfigEntry<int> ingotStackStorageCapacity;

		private ConfigEntry<int> maxVisualBarsPerStack;

		private ConfigEntry<int> maximumWoodLogs;

		private ConfigEntry<bool> showForgeBuildPieces;

		private ConfigEntry<string> additionalMaterialPrefabs;

		private ConfigEntry<KeyCode> appearanceEditorKey;

		private PlacedAppearance configApplyHoverTarget;

		private float configApplyHoverTargetUntil;

		private PlacedAppearance debugHoverTarget;

		private float debugHoverTargetUntil;

		private PlacedAppearance debugOverrideTarget;

		private static bool hoverErrorLogged;

		private float nextPresetConfigSync;

		internal const float ObsidianVisualGroundOffset = -0.0508f;

		private ConfigEntry<bool> forceConfigPileAppearance;

		private MaterialBarrelAppearance genericDebugHoverTarget;

		private float genericDebugHoverUntil;

		internal const int AbsoluteMaxVisualBarsPerStack = 400;

		private readonly List<BarDefinition> barDefinitions = new List<BarDefinition>();

		private readonly List<TemplateDefinition> templateDefinitions = new List<TemplateDefinition>
		{
			new TemplateDefinition("flint_pile", "Flint", "Flint", "DSM_FlintPile", "Flint Pile (Storage)", 10, 1f, "Grausten", 0.7f, 0.62f, null, null, 1f, -0.13f),
			new TemplateDefinition("flint_pile", "Obsidian", "Obsidian", "DSM_ObsidianPile", "Obsidian Pile (Storage)", 10, 1f, "Flint - Experimental", 0.75f, 1.1f, null, null, 1f, -0.15f, null, "Obsidian", forceResourceModelGeometry: true),
			new TemplateDefinition("coal_pile", "Resin", "Resin", "DSM_ResinPile", "Resin Pile (Storage)", 10, 1f, "Coal", 0.85f, 0.75f, null, null, 1.3f, -0.28f),
			new TemplateDefinition("stone_pile", "GreydwarfEye", "GreydwarfEye", "DSM_GreydwarfEyePile", "Greydwarf Eye Pile (Storage)", 10, 1f, "Coal", 0.8f, 0.7f, null, null, 0.8f, -0.11f),
			new TemplateDefinition("coal_pile", "SurtlingCore", "SurtlingCore", "DSM_SurtlingCorePile", "Surtling Core Pile (Storage)", 5, 1f, "Coal", 1f, 0.83f, null, null, 1f, -0.12f),
			new TemplateDefinition("stone_pile", "Crystal", "Crystal", "DSM_CrystalPile", "Crystal Pile (Storage)", 5, 1f, "Obsidian - Experimental", 0.9f, 1.5f, null, null, 1.15f, -0.22f),
			new TemplateDefinition("stone_pile", "FreezeGland", "FreezeGland", "DSM_FreezeGlandPile", "Freeze Gland Pile (Storage)", 5, 1f, "Stone", 1f, 0.9f, null, null, 1.1f, -0.19f),
			new TemplateDefinition("coal_pile", "Needle", "Needle", "DSM_NeedlePile", "Needle Pile (Storage)", 10, 1f, "Grausten", 0.5f, 0.5f, null, null, 0.5f, -0.06f),
			new TemplateDefinition("stone_pile", "Softtissue", "Softtissue", "DSM_SoftTissuePile", "Soft Tissue Pile (Storage)", 10, 1f, "Black Marble", 1.08f, 0.93f, null, null, 1f, -0.37f),
			new TemplateDefinition("coal_pile", "BlackCore", "BlackCore", "DSM_BlackCorePile", "Black Core Pile (Storage)", 3, 1f, "Settled diamond pile", 1f, 1f, null, null, 1f, -0.18f),
			new TemplateDefinition("stone_pile", "SulfurStone", "SulfurStone", "DSM_SulfurPile", "Sulfur Pile (Storage)", 10, 1f, "Coal", 0.73f, 0.5f, null, null, 1f, -0.14f),
			new TemplateDefinition("stone_pile", "Ice", "Ice", "DSM_IcePile", "Ice Pile (Storage)", 10, 1f, "Obsidian - Experimental", 0.69f, 1.35f, null, null, 1f, -0.16f),
			new TemplateDefinition("coal_pile", "FrostCore", "FrostCore", "DSM_FrostcorePile", "Frostcore Pile (Storage)", 3, 1f, "Grausten", 0.5f, 0.74f, null, null, 1f, -0.44f)
		};

		internal bool AllowDeletionOfProtectedPiles
		{
			get
			{
				if (allowDeletionOfProtectedPiles != null)
				{
					return allowDeletionOfProtectedPiles.Value;
				}
				return false;
			}
		}

		internal static int CurrentMaximumWoodLogs => MaterialLogLayoutMath.Limit(Current?.maximumWoodLogs?.Value ?? 100);

		internal string AdditionalMaterialPrefabs => additionalMaterialPrefabs?.Value ?? "";

		internal KeyCode AppearanceEditorKey => (KeyCode)(((??)appearanceEditorKey?.Value) ?? 112);

		internal bool CanEditMaterialSupport
		{
			get
			{
				if ((Object)(object)ZNet.instance != (Object)null)
				{
					return ZNet.instance.LocalPlayerIsAdminOrHost();
				}
				return false;
			}
		}

		internal static DynamicStorageMaterialsPlugin Current { get; private set; }

		internal bool ForceConfigPileAppearance
		{
			get
			{
				if (forceConfigPileAppearance != null)
				{
					return forceConfigPileAppearance.Value;
				}
				return false;
			}
		}

		internal bool ShowAppearanceDebugHover => EnableApplyConfigDebug;

		internal bool DebugLoggingEnabled
		{
			get
			{
				if (debugLogging != null)
				{
					return debugLogging.Value;
				}
				return false;
			}
		}

		internal bool ShowOlderPileNotice
		{
			get
			{
				if (showOlderPileNotice != null)
				{
					return showOlderPileNotice.Value;
				}
				return false;
			}
		}

		internal bool ShowMeshDebugHover
		{
			get
			{
				if (showMeshDebugHover != null)
				{
					return showMeshDebugHover.Value;
				}
				return false;
			}
		}

		internal bool ShowTechnicalDebugHover
		{
			get
			{
				if (showTechnicalDebugHover != null)
				{
					return showTechnicalDebugHover.Value;
				}
				return false;
			}
		}

		internal bool ShowDroppedModelDebugHover
		{
			get
			{
				if (showDroppedModelDebugHover != null)
				{
					return showDroppedModelDebugHover.Value;
				}
				return false;
			}
		}

		internal bool ShowStorageDebugTooltip
		{
			get
			{
				if (showStorageDebugTooltip != null)
				{
					return showStorageDebugTooltip.Value;
				}
				return false;
			}
		}

		internal bool EnableApplyConfigDebug
		{
			get
			{
				if (enableApplyConfigDebug != null)
				{
					return enableApplyConfigDebug.Value;
				}
				return false;
			}
		}

		internal int OrePileStorageCapacity => NormalizeStorageCapacity((orePileStorageCapacity != null) ? orePileStorageCapacity.Value : 20);

		internal bool CanEditServerCapacity
		{
			get
			{
				if ((Object)(object)ZNet.instance != (Object)null)
				{
					return ZNet.instance.IsServer();
				}
				return false;
			}
		}

		internal bool ShowLegacyPieces
		{
			get
			{
				if (showForgeBuildPieces != null)
				{
					return showForgeBuildPieces.Value;
				}
				return true;
			}
			set
			{
				if (showForgeBuildPieces != null)
				{
					showForgeBuildPieces.Value = value;
				}
			}
		}

		internal int IngotStackStorageCapacity => NormalizeStorageCapacity((ingotStackStorageCapacity != null) ? ingotStackStorageCapacity.Value : 20);

		internal int MaxVisualBarsPerStack => Mathf.Clamp((maxVisualBarsPerStack != null) ? maxVisualBarsPerStack.Value : 120, 10, 400);

		internal static int CurrentMaxVisualBarsPerStack
		{
			get
			{
				if (!((Object)(object)Current != (Object)null))
				{
					return 120;
				}
				return Current.MaxVisualBarsPerStack;
			}
		}

		internal bool ShowAnyDebugHover
		{
			get
			{
				if (!EnableApplyConfigDebug && !ShowMeshDebugHover)
				{
					return ShowTechnicalDebugHover;
				}
				return true;
			}
		}

		internal bool SetMaximumWoodLogs(int limit)
		{
			if (!CanEditServerCapacity || maximumWoodLogs == null || limit < 1 || limit > 512)
			{
				return false;
			}
			maximumWoodLogs.Value = limit;
			return true;
		}

		internal bool AddMaterialSupport(string id)
		{
			if (!CanEditMaterialSupport || !MaterialTrialCatalog.TryValidateItem(id, out var _))
			{
				return false;
			}
			if (MaterialTrialCatalog.Validated.Contains(id))
			{
				return true;
			}
			return CommitMaterialSupport(id);
		}

		private bool CommitMaterialSupport(string id)
		{
			if (additionalMaterialPrefabs == null || !MaterialTrialCatalog.TryValidateItem(id, out var _))
			{
				return false;
			}
			string[] value = (from x in AdditionalMaterialPrefabs.Split(new char[1] { ',' })
				select x.Trim() into x
				where x.Length > 0
				select x).Concat(new string[1] { id }).Distinct<string>(StringComparer.Ordinal).ToArray();
			string text = string.Join(",", value);
			if (text.Length > 4096)
			{
				return false;
			}
			additionalMaterialPrefabs.Value = text;
			return MaterialTrialCatalog.Validated.Contains(id);
		}

		internal bool SetEditorCapacity(int slots)
		{
			if (!CanEditServerCapacity || orePileStorageCapacity == null || (slots != 10 && slots != 20 && slots != 40 && slots != 60))
			{
				return false;
			}
			orePileStorageCapacity.Value = slots;
			return true;
		}

		internal void QueueBarrelIcon(CustomPiece piece)
		{
			piecesNeedingIcons.Add(piece);
		}

		private void Awake()
		{
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Expected O, but got Unknown
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0103: Unknown result type (might be due to invalid IL or missing references)
			//IL_010d: Expected O, but got Unknown
			//IL_021c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0226: Expected O, but got Unknown
			Current = this;
			maximumWoodLogs = ConfigFileExtensions.BindConfig<int>(((BaseUnityPlugin)this).Config, "Performance", "Maximum logs per pile", 100, "Server/admin visual performance limit (1-512). Limits displayed logs in standalone wood piles. Stored items and storage capacity are unchanged. Complete rows are retained; saved layout sizes are kept.", true, (int?)100, (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 512), (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			new ConsoleCommand("dsm_log_limit", "Show or set maximum visible logs per wood pile: dsm_log_limit [1-512]", (ConsoleEvent)delegate(ConsoleEventArgs args)
			{
				if (args.Args.Length == 1)
				{
					args.Context.AddString("Maximum logs per pile: " + CurrentMaximumWoodLogs);
				}
				else
				{
					args.Context.AddString((args.Args.Length == 2 && int.TryParse(args.Args[1], out var result) && SetMaximumWoodLogs(result)) ? ("Maximum visible logs updated to " + CurrentMaximumWoodLogs + ". Storage capacity is unchanged.") : "Use dsm_log_limit 1-512 on the host or server console.");
				}
			}, false, false, true, false, false, false, (ConsoleOptionsFetcher)null, false, false, true);
			AcceptableValueList<int> val = new AcceptableValueList<int>(new int[4] { 10, 20, 40, 60 });
			int value = ReadSavedInt("Storage Capacity", "Ore Pile Slots", 20);
			bool flag = ReadSavedBool("Build Menu", "Show Material Build Pieces", fallback: false);
			orePileStorageCapacity = ConfigFileExtensions.BindConfig<int>(((BaseUnityPlugin)this).Config, "General Settings", "Material Pile Slots", NormalizeStorageCapacity(value), "Server-controlled slot count for Dynamic Storage Materials piles. Options: 10, 20, 40, or 60.", true, (int?)100, (AcceptableValueBase)(object)val, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			showForgeBuildPieces = ((BaseUnityPlugin)this).Config.Bind<bool>("General Settings", "Show Material Build Pieces", flag, new ConfigDescription("Client-local only. Hides the thirteen legacy Materials blueprints from the Hammer build menu when off. New installations default to hidden; saved visibility preferences are retained. The new Material Storage stays visible. Existing placed storage remains registered and fully usable.", (AcceptableValueBase)null, new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Order = 90
				}
			}));
			orePileStorageCapacity.SettingChanged += OnStorageCapacityChanged;
			showForgeBuildPieces.SettingChanged += OnShowForgeBuildPiecesChanged;
			MaterialBuildCosts.Bind(((BaseUnityPlugin)this).Config);
			additionalMaterialPrefabs = ConfigFileExtensions.BindConfig<string>(((BaseUnityPlugin)this).Config, "General Settings", "Additional Material Prefab IDs", "", "Server/admin controlled. Exact, case-sensitive ItemDrop prefab IDs separated by commas. Keep entries while items are stored; missing entries never delete saved contents.", true, (int?)85, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			additionalMaterialPrefabs.SettingChanged += delegate
			{
				MaterialTrialCatalog.Validate();
			};
			appearanceEditorKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Appearance Editor", "Editor Key", (KeyCode)112, "Key used for the Materials Appearance Editor and hover prompt.");
			StorageViewerPreference.Bind(((BaseUnityPlugin)this).Config);
			MaterialPreferredStyle.Bind(((BaseUnityPlugin)this).Config);
			MaterialQuickBuild.Bind(((BaseUnityPlugin)this).Config);
			MaterialEffectsPreference.Bind(((BaseUnityPlugin)this).Config);
			BindAppearanceEditorSettings();
			MaterialPileConfiguration.Bind(((BaseUnityPlugin)this).Config);
			BindDebugSettings();
			InstallPlacementHook();
			InstallHoverHook();
			restrictionHarmony = new Harmony("com.muji.DynamicStorageMaterials.restrictions");
			if (!ForgeInventoryRestrictionPatches.Install(restrictionHarmony))
			{
				((BaseUnityPlugin)this).Logger.LogError((object)"One or more Materials item restriction hooks could not be installed.");
			}
			if (!ForgeStorageCapacityPatches.Install(restrictionHarmony))
			{
				((BaseUnityPlugin)this).Logger.LogError((object)"Materials storage-capacity safety hook could not be installed.");
			}
			MaterialBarrelPatches.Install(restrictionHarmony);
			MaterialBuildMenu.Install(restrictionHarmony);
			try
			{
				MaterialPlacementCycleDebug.Install(restrictionHarmony);
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("Placement cycle debug unavailable: " + ex.GetBaseException().Message));
			}
			try
			{
				MaterialQuickBuild.Install(restrictionHarmony);
			}
			catch (Exception ex2)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("Quick storage build unavailable: " + ex2.GetBaseException().Message));
			}
			try
			{
				MaterialBuildCosts.Install(restrictionHarmony);
			}
			catch (Exception ex3)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("Material Storage construction cost options unavailable: " + ex3.GetBaseException().Message));
			}
			try
			{
				MaterialPreferredStyle.Install(restrictionHarmony);
			}
			catch (Exception ex4)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("Remember display style unavailable: " + ex4.GetBaseException().Message));
			}
			((Component)this).gameObject.AddComponent<MaterialsAppearanceEditor>();
			PrefabManager.OnPrefabsRegistered += OnPrefabsRegistered;
			PieceManager.OnPiecesRegistered += OnPiecesRegistered;
			if (MaterialLogging.DebugEnabled)
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)"Dynamic Storage Materials 2.0.4 loaded; waiting for prefabs.");
			}
		}

		private void BindAppearanceEditorSettings()
		{
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Expected O, but got Unknown
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Expected O, but got Unknown
			MaterialConfigMigration materialConfigMigration = new MaterialConfigMigration(((BaseUnityPlugin)this).Config);
			bool saveOnConfigSet = ((BaseUnityPlugin)this).Config.SaveOnConfigSet;
			((BaseUnityPlugin)this).Config.SaveOnConfigSet = false;
			try
			{
				enableApplyConfigDebug = materialConfigMigration.Bind("Appearance Editor", "Pile Appearance Editor", "Advanced Debug", "Apply config to legacy pile with R", initial: false, new ConfigDescription("Legacy tool: hover a placed original fixed-material pile and press R to permanently save the current config recipe on that owned piece. Not needed for the P-key window. For temporary live tuning use Force configuration pile appearance.", (AcceptableValueBase)null, new object[1]
				{
					new ConfigurationManagerAttributes
					{
						Order = 90,
						IsAdvanced = true
					}
				}));
				forceConfigPileAppearance = ((BaseUnityPlugin)this).Config.Bind<bool>("Advanced Debug", "Force configuration pile appearance", false, new ConfigDescription("Temporary LOCAL visual override: all loaded Materials piles (including Barrel/Crate contents) use current material configuration values live. OFF restores saved per-piece appearances. No inventory or saved appearance writes; new materials are not auto-validated.", (AcceptableValueBase)null, new object[1]
				{
					new ConfigurationManagerAttributes
					{
						Order = 120
					}
				}));
				forceConfigPileAppearance.SettingChanged += delegate
				{
					PlacedAppearance[] array = Object.FindObjectsOfType<PlacedAppearance>();
					foreach (PlacedAppearance placedAppearance in array)
					{
						if (Object.op_Implicit((Object)(object)placedAppearance))
						{
							placedAppearance.ForceAppearanceRefresh();
						}
					}
				};
				if (materialConfigMigration.Migrated)
				{
					((BaseUnityPlugin)this).Config.Save();
				}
			}
			finally
			{
				((BaseUnityPlugin)this).Config.SaveOnConfigSet = saveOnConfigSet;
			}
		}

		private void BindDebugSettings()
		{
			//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cd: Expected O, but got Unknown
			//IL_0105: Unknown result type (might be due to invalid IL or missing references)
			//IL_010f: Expected O, but got Unknown
			//IL_0146: Unknown result type (might be due to invalid IL or missing references)
			//IL_0150: Expected O, but got Unknown
			//IL_0187: Unknown result type (might be due to invalid IL or missing references)
			//IL_0191: Expected O, but got Unknown
			debugLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "Enable debug logging", false, "Print Materials informational messages, benign warnings and detailed diagnostics. OFF logs only errors. Client-local; does not change other mods or BepInEx logging.");
			showOlderPileNotice = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "Show older-pile notice", true, "Shows a short amber notice on older supported fixed-material piles. Turn OFF to hide it during testing or normal play. Client-local; does not hide real storage errors or change storage behavior.");
			allowDeletionOfProtectedPiles = ConfigFileExtensions.BindConfig<bool>(((BaseUnityPlugin)this).Config, "Advanced Debug", "Allow deletion of protected piles", false, "LAST RESORT. Server/admin controlled. Permits deletion of Materials piles whose saved contents cannot be loaded. Unloadable contents may be permanently lost and may not be dropped. Back up the world first and turn this OFF afterwards. Does not bypass an active inventory load.", true, (int?)(-100), (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			MaterialPlacementCycleDebug.Bind(((BaseUnityPlugin)this).Config);
			MaterialFullnessDebug.Bind(((BaseUnityPlugin)this).Config);
			showStorageDebugTooltip = ((BaseUnityPlugin)this).Config.Bind<bool>("Advanced Debug", "Show Storage Debug Tooltip", false, new ConfigDescription("Advanced/debug only. Shows live DynamicStorage Materials capacity, grid and item-safety diagnostics in the hover text. Real storage-safety warnings remain visible even when this is disabled.", (AcceptableValueBase)null, new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Order = 110,
					IsAdvanced = true
				}
			}));
			showMeshDebugHover = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "Show Mesh Debug On Hover", false, new ConfigDescription("Mesh stage inspector used for Flint/Obsidian tuning. While looking at a Materials pile, [ and ] select the real fill sequence; Left/Right select a layer inside that sequence; Up locks that layer on; Down hides it; Right Ctrl resets all sequence/layer overrides.", (AcceptableValueBase)null, new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Order = 100
				}
			}));
			showTechnicalDebugHover = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "Show Technical Debug On Hover", false, new ConfigDescription("Show snapshot and ZDO ownership troubleshooting information.", (AcceptableValueBase)null, new object[1]
			{
				new ConfigurationManagerAttributes
				{
					IsAdvanced = true
				}
			}));
			showDroppedModelDebugHover = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "Show Dropped Model Debug On Hover", false, new ConfigDescription("Append DSM model-size diagnostics to supported loose/dropped items so Model Scale 1.0 can be verified against the actual world item.", (AcceptableValueBase)null, new object[1]
			{
				new ConfigurationManagerAttributes
				{
					IsAdvanced = true
				}
			}));
		}

		private void OnStorageCapacityChanged(object sender, EventArgs args)
		{
			ForgeStorageCapacity.RefreshAll();
			ForgeVisualStack.RefreshAll();
			if (MaterialLogging.DebugEnabled)
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)$"Material pile storage capacity updated: {OrePileStorageCapacity} slots.");
			}
		}

		private void OnMaximumVisualBarsChanged(object sender, EventArgs args)
		{
			PlacedAppearance[] array = Object.FindObjectsOfType<PlacedAppearance>();
			foreach (PlacedAppearance placedAppearance in array)
			{
				if (Object.op_Implicit((Object)(object)placedAppearance))
				{
					placedAppearance.ForceAppearanceRefresh();
				}
			}
		}

		private void OnShowForgeBuildPiecesChanged(object sender, EventArgs args)
		{
			ApplyForgeBuildPieceVisibility();
		}

		private void ApplyForgeBuildPieceVisibility()
		{
			if (showForgeBuildPieces == null || PieceManager.Instance == null)
			{
				return;
			}
			PieceTable pieceTable = PieceManager.Instance.GetPieceTable(PieceTables.Hammer);
			if (!Object.op_Implicit((Object)(object)pieceTable) || pieceTable.m_pieces == null)
			{
				return;
			}
			bool value = showForgeBuildPieces.Value;
			int num = 0;
			foreach (GameObject forgeBuildPiecePrefab in forgeBuildPiecePrefabs)
			{
				if (Object.op_Implicit((Object)(object)forgeBuildPiecePrefab))
				{
					bool flag = pieceTable.m_pieces.Contains(forgeBuildPiecePrefab);
					if (value && !flag)
					{
						pieceTable.m_pieces.Add(forgeBuildPiecePrefab);
						num++;
					}
					else if (!value && flag)
					{
						pieceTable.m_pieces.Remove(forgeBuildPiecePrefab);
						num++;
					}
				}
			}
			if (MaterialLogging.DebugEnabled)
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)("Materials Hammer blueprints " + (value ? "shown" : "hidden") + ((num > 0) ? $" ({num} menu entries updated)." : ".")));
			}
		}

		private string ReadSavedValue(string section, string key)
		{
			try
			{
				if (!File.Exists(((BaseUnityPlugin)this).Config.ConfigFilePath))
				{
					return null;
				}
				bool flag = false;
				string text = key + " = ";
				foreach (string item in File.ReadLines(((BaseUnityPlugin)this).Config.ConfigFilePath))
				{
					string text2 = item.Trim();
					if (text2.StartsWith("[", StringComparison.Ordinal) && text2.EndsWith("]", StringComparison.Ordinal))
					{
						flag = text2.Substring(1, text2.Length - 2) == section;
					}
					else if (flag && text2.StartsWith(text, StringComparison.Ordinal))
					{
						return text2.Substring(text.Length).Trim();
					}
				}
			}
			catch (IOException)
			{
			}
			catch (UnauthorizedAccessException)
			{
			}
			return null;
		}

		private int ReadSavedInt(string section, string key, int fallback)
		{
			if (!int.TryParse(ReadSavedValue(section, key), out var result))
			{
				return fallback;
			}
			return result;
		}

		private bool ReadSavedBool(string section, string key, bool fallback)
		{
			if (!bool.TryParse(ReadSavedValue(section, key), out var result))
			{
				return fallback;
			}
			return result;
		}

		internal static string NormalizeBarFillBasis(string value)
		{
			if (!string.Equals(value, "Stored Bar Count", StringComparison.Ordinal))
			{
				return "Occupied Slots";
			}
			return "Stored Bar Count";
		}

		internal static string NormalizeBarLooksFullAt(string value)
		{
			if (string.Equals(value, "Always Full", StringComparison.Ordinal))
			{
				return "Always Full";
			}
			switch (value)
			{
			case "10%":
			case "25%":
			case "50%":
			case "75%":
			case "100%":
				return value;
			default:
				return "100%";
			}
		}

		internal static int ParseBarLooksFullPercent(string value)
		{
			return NormalizeBarLooksFullAt(value) switch
			{
				"Always Full" => -1, 
				"10%" => 10, 
				"25%" => 25, 
				"50%" => 50, 
				"75%" => 75, 
				_ => 100, 
			};
		}

		internal static string NormalizePileLooksFullAt(string value)
		{
			string text = (value ?? string.Empty).Trim();
			if (text.Length == 0 || string.Equals(text, "Match Storage Capacity", StringComparison.OrdinalIgnoreCase))
			{
				return "Match Storage Capacity";
			}
			if (string.Equals(text, "Always Full", StringComparison.OrdinalIgnoreCase))
			{
				return "Always Full";
			}
			if (string.Equals(text, "10", StringComparison.OrdinalIgnoreCase) || string.Equals(text, "10 slots", StringComparison.OrdinalIgnoreCase))
			{
				return "10 slots";
			}
			if (string.Equals(text, "20", StringComparison.OrdinalIgnoreCase) || string.Equals(text, "20 slots", StringComparison.OrdinalIgnoreCase))
			{
				return "20 slots";
			}
			if (string.Equals(text, "40", StringComparison.OrdinalIgnoreCase) || string.Equals(text, "40 slots", StringComparison.OrdinalIgnoreCase))
			{
				return "40 slots";
			}
			if (string.Equals(text, "60", StringComparison.OrdinalIgnoreCase) || string.Equals(text, "60 slots", StringComparison.OrdinalIgnoreCase))
			{
				return "60 slots";
			}
			return "Match Storage Capacity";
		}

		internal static int ParsePileLooksFullSlots(string value)
		{
			return NormalizePileLooksFullAt(value) switch
			{
				"Always Full" => -1, 
				"10 slots" => 10, 
				"20 slots" => 20, 
				"40 slots" => 40, 
				"60 slots" => 60, 
				_ => 0, 
			};
		}

		internal static int NormalizeStorageCapacity(int value)
		{
			if (value == 10)
			{
				return 10;
			}
			if (value == 20)
			{
				return 20;
			}
			if (value == 40)
			{
				return 40;
			}
			if (value == 60)
			{
				return 60;
			}
			if (value >= 60)
			{
				return 60;
			}
			return 20;
		}

		private static int NormalizeSafeCapacity(int value)
		{
			switch (value)
			{
			case 10:
				return 10;
			case 20:
				return 20;
			case 40:
				return 40;
			case 60:
				return 60;
			default:
				_ = 80;
				return 80;
			}
		}

		internal static void GetSafeStorageGrid(int capacity, out int width, out int height)
		{
			switch (NormalizeSafeCapacity(capacity))
			{
			case 10:
				width = 5;
				height = 2;
				break;
			case 20:
				width = 5;
				height = 4;
				break;
			case 40:
				width = 8;
				height = 5;
				break;
			case 60:
				width = 6;
				height = 10;
				break;
			case 80:
				width = 8;
				height = 10;
				break;
			default:
				width = 8;
				height = 10;
				break;
			}
		}

		internal static void GetStorageGrid(int capacity, out int width, out int height)
		{
			GetSafeStorageGrid(capacity, out width, out height);
		}

		private void LateUpdate()
		{
			try
			{
				MaterialPreferredStyle.Tick();
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("Remember display style: " + ex.GetBaseException().Message));
			}
			try
			{
				MaterialPlacementCycleDebug.Tick();
			}
			catch (Exception ex2)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("Placement cycle debug refresh failed: " + ex2.GetBaseException().Message));
			}
			try
			{
				MaterialQuickBuild.Tick();
			}
			catch (Exception ex3)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("Quick storage build: " + ex3.GetBaseException().Message));
			}
			if (Time.unscaledTime >= nextPresetConfigSync)
			{
				nextPresetConfigSync = Time.unscaledTime + 0.1f;
				PlacedAppearance.SynchronizeConfigPresets();
			}
			bool flag = EnableApplyConfigDebug && Object.op_Implicit((Object)(object)configApplyHoverTarget) && Time.unscaledTime <= configApplyHoverTargetUntil;
			if (!EnableApplyConfigDebug)
			{
				configApplyHoverTarget = null;
			}
			else if (flag && Input.GetKeyDown((KeyCode)114))
			{
				configApplyHoverTarget.ApplyCurrentConfigToPlacedPiece();
			}
			if (!ShowMeshDebugHover)
			{
				if (Object.op_Implicit((Object)(object)debugOverrideTarget))
				{
					debugOverrideTarget.ClearDebugMeshOverride();
				}
				debugOverrideTarget = null;
				debugHoverTarget = null;
				return;
			}
			if (Object.op_Implicit((Object)(object)debugOverrideTarget) && !debugOverrideTarget.RefreshDebugMeshOverride())
			{
				debugOverrideTarget = null;
			}
			if (Object.op_Implicit((Object)(object)genericDebugHoverTarget) && Time.unscaledTime <= genericDebugHoverUntil)
			{
				genericDebugHoverTarget.HandleDebugKeys();
			}
			if (!Object.op_Implicit((Object)(object)debugHoverTarget) || !(Time.unscaledTime <= debugHoverTargetUntil))
			{
				return;
			}
			int num = (Input.GetKeyDown((KeyCode)275) ? 1 : (Input.GetKeyDown((KeyCode)276) ? (-1) : 0));
			int num2 = (Input.GetKeyDown((KeyCode)93) ? 1 : (Input.GetKeyDown((KeyCode)91) ? (-1) : 0));
			bool keyDown = Input.GetKeyDown((KeyCode)273);
			bool keyDown2 = Input.GetKeyDown((KeyCode)274);
			bool keyDown3 = Input.GetKeyDown((KeyCode)305);
			if (num != 0 || num2 != 0 || keyDown || keyDown2 || keyDown3)
			{
				if (Object.op_Implicit((Object)(object)debugOverrideTarget) && (Object)(object)debugOverrideTarget != (Object)(object)debugHoverTarget)
				{
					debugOverrideTarget.ClearDebugMeshOverride();
					debugOverrideTarget = null;
				}
				if (num2 != 0 && debugHoverTarget.DebugSelectSequence(num2))
				{
					debugOverrideTarget = debugHoverTarget;
				}
				if (num != 0)
				{
					debugHoverTarget.DebugSelectMesh(num);
				}
				if (keyDown3 && debugHoverTarget.DebugResetSequenceOverrides() && (Object)(object)debugOverrideTarget == (Object)(object)debugHoverTarget)
				{
					debugOverrideTarget = null;
				}
				if (keyDown && debugHoverTarget.DebugSetSelectedMeshVisible(visible: true))
				{
					debugOverrideTarget = debugHoverTarget;
				}
				else if (keyDown2 && debugHoverTarget.DebugSetSelectedMeshVisible(visible: false))
				{
					debugOverrideTarget = debugHoverTarget;
				}
			}
		}

		private void OnDestroy()
		{
			StorageLabelFontAssets.Dispose();
			StorageLabelIconHalo.Dispose();
			PrefabManager.OnPrefabsRegistered -= OnPrefabsRegistered;
			PieceManager.OnPiecesRegistered -= OnPiecesRegistered;
			Harmony obj = placementHarmony;
			if (obj != null)
			{
				obj.UnpatchSelf();
			}
			Harmony obj2 = restrictionHarmony;
			if (obj2 != null)
			{
				obj2.UnpatchSelf();
			}
			if ((Object)(object)Current == (Object)(object)this)
			{
				Current = null;
			}
		}

		private void InstallPlacementHook()
		{
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Expected O, but got Unknown
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a1: Expected O, but got Unknown
			MethodInfo[] array = (from methodInfo2 in typeof(Piece).GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
				where methodInfo2.Name == "SetCreator" && methodInfo2.GetParameters().Length != 0 && methodInfo2.GetParameters()[0].ParameterType == typeof(long)
				select methodInfo2).ToArray();
			if (array.Length == 0)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)"No Piece.SetCreator overload with a long creator ID was found; appearance snapshots cannot be saved.");
				return;
			}
			if (placementHarmony == null)
			{
				placementHarmony = new Harmony("com.muji.DynamicStorageMaterials.appearance");
			}
			MethodInfo method = typeof(DynamicStorageMaterialsPlugin).GetMethod("CaptureNewPiece", BindingFlags.Static | BindingFlags.NonPublic);
			MethodInfo[] array2 = array;
			foreach (MethodInfo methodInfo in array2)
			{
				try
				{
					placementHarmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(method), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
					if (MaterialLogging.DebugEnabled)
					{
						((BaseUnityPlugin)this).Logger.LogInfo((object)$"Appearance placement hook installed on Piece.{methodInfo}.");
					}
				}
				catch (Exception arg)
				{
					((BaseUnityPlugin)this).Logger.LogError((object)$"Could not install appearance hook on Piece.{methodInfo}: {arg}");
				}
			}
		}

		private void InstallHoverHook()
		{
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Expected O, but got Unknown
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Expected O, but got Unknown
			//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0101: Expected O, but got Unknown
			MethodInfo method = typeof(Container).GetMethod("GetHoverText", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null);
			if (method == null || method.ReturnType != typeof(string))
			{
				((BaseUnityPlugin)this).Logger.LogError((object)"Container.GetHoverText() not found; appearance hover debug is unavailable.");
				return;
			}
			try
			{
				if (placementHarmony == null)
				{
					placementHarmony = new Harmony("com.muji.DynamicStorageMaterials.appearance");
				}
				MethodInfo method2 = typeof(DynamicStorageMaterialsPlugin).GetMethod("AddAppearanceDebugHover", BindingFlags.Static | BindingFlags.NonPublic);
				placementHarmony.Patch((MethodBase)method, (HarmonyMethod)null, new HarmonyMethod(method2), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				MethodInfo method3 = typeof(ItemDrop).GetMethod("GetHoverText", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null);
				if (method3 != null && method3.ReturnType == typeof(string))
				{
					MethodInfo method4 = typeof(DynamicStorageMaterialsPlugin).GetMethod("AddDroppedModelDebugHover", BindingFlags.Static | BindingFlags.NonPublic);
					placementHarmony.Patch((MethodBase)method3, (HarmonyMethod)null, new HarmonyMethod(method4), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				}
			}
			catch (Exception arg)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)$"Could not install appearance editor hover: {arg}");
			}
		}

		private static void AddAppearanceDebugHover(Container __instance, ref string __result)
		{
			if ((Object)(object)Current == (Object)null || !Object.op_Implicit((Object)(object)__instance))
			{
				return;
			}
			try
			{
				__result = (__result ?? string.Empty) + MaterialIssueDiagnostics.ForHover(((Component)__instance).transform);
				if (Current.ShowOlderPileNotice && !Object.op_Implicit((Object)(object)((Component)__instance).GetComponent<MaterialBarrelStorage>()) && Object.op_Implicit((Object)(object)((Component)__instance).GetComponent<PlacedAppearance>()))
				{
					__result += "\n<color=#D9B46F>Older pile. New: Material Storage in the build menu.</color>";
				}
				ForgeStorageCapacity component = ((Component)__instance).GetComponent<ForgeStorageCapacity>();
				if (Object.op_Implicit((Object)(object)component))
				{
					__result = (__result ?? string.Empty) + component.SafetyWarning;
					if (Current.ShowStorageDebugTooltip)
					{
						__result = (__result ?? string.Empty) + component.DebugStatus;
					}
				}
				MaterialBarrelAppearance component2 = ((Component)__instance).GetComponent<MaterialBarrelAppearance>();
				if (Object.op_Implicit((Object)(object)component2) && (Current.ShowAnyDebugHover || Current.ForceConfigPileAppearance))
				{
					__result = (__result ?? string.Empty) + component2.DebugHoverText(Current.ShowAppearanceDebugHover, Current.ShowMeshDebugHover, Current.ShowTechnicalDebugHover);
					if (Current.ShowMeshDebugHover)
					{
						Current.genericDebugHoverTarget = component2;
						Current.genericDebugHoverUntil = Time.unscaledTime + 0.35f;
					}
				}
				if (!Current.ShowAnyDebugHover)
				{
					return;
				}
				PlacedAppearance component3 = ((Component)__instance).GetComponent<PlacedAppearance>();
				if (Object.op_Implicit((Object)(object)component3))
				{
					if (Current.ShowMeshDebugHover)
					{
						Current.debugHoverTarget = component3;
						Current.debugHoverTargetUntil = Time.unscaledTime + 0.35f;
					}
					if (Current.EnableApplyConfigDebug)
					{
						Current.configApplyHoverTarget = component3;
						Current.configApplyHoverTargetUntil = Time.unscaledTime + 0.35f;
					}
					__result = (__result ?? string.Empty) + component3.GetDebugHoverText(Current.ShowAppearanceDebugHover, Current.ShowMeshDebugHover, Current.ShowTechnicalDebugHover, Current.EnableApplyConfigDebug);
				}
			}
			catch (Exception arg)
			{
				if (!hoverErrorLogged)
				{
					((BaseUnityPlugin)Current).Logger.LogError((object)$"Materials storage/appearance hover failed: {arg}");
					hoverErrorLogged = true;
				}
			}
		}

		private static void AddDroppedModelDebugHover(ItemDrop __instance, ref string __result)
		{
			//IL_0121: 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)
			if ((Object)(object)Current == (Object)null || !Current.ShowDroppedModelDebugHover || !Object.op_Implicit((Object)(object)__instance))
			{
				return;
			}
			try
			{
				string text = null;
				if (__instance.m_itemData != null && Object.op_Implicit((Object)(object)__instance.m_itemData.m_dropPrefab))
				{
					text = ((Object)__instance.m_itemData.m_dropPrefab).name;
				}
				if (string.IsNullOrEmpty(text))
				{
					text = ((Object)((Component)__instance).gameObject).name ?? string.Empty;
					int num = text.IndexOfAny(new char[2] { '(', ' ' });
					if (num > 0)
					{
						text = text.Substring(0, num);
					}
				}
				string value = AppearanceSettings.ModelNameForPrefab(text);
				if (string.IsNullOrEmpty(value))
				{
					value = (MaterialTrialCatalog.Validated.Contains(text) ? MaterialTrialCatalog.Name(text) : null);
				}
				if (string.IsNullOrEmpty(value))
				{
					return;
				}
				Vector3 size;
				bool flag = ForgePileStageDefinitions.TryMeasureModelInstanceWorldSize(((Component)__instance).gameObject, out size);
				Vector3 size2;
				bool flag2 = ForgePileStageDefinitions.TryGetNativeModelWorldSize(text, out size2);
				if (flag || flag2)
				{
					StringBuilder stringBuilder = new StringBuilder();
					stringBuilder.Append("\n<color=#8AD7FF>DSM DROPPED MODEL DEBUG — ").Append(value).Append("</color>");
					if (flag)
					{
						stringBuilder.Append("\nDropped rendered size: ").Append(FormatDebugSize(size)).Append(" m");
					}
					if (flag2)
					{
						stringBuilder.Append("\nDSM Model Scale 1.0: ").Append(FormatDebugSize(size2)).Append(" m");
					}
					__result = (__result ?? string.Empty) + stringBuilder.ToString();
				}
			}
			catch (Exception ex)
			{
				if (!hoverErrorLogged)
				{
					((BaseUnityPlugin)Current).Logger.LogError((object)("DSM dropped-model hover debug failed: " + ex));
					hoverErrorLogged = true;
				}
			}
		}

		private static string FormatDebugSize(Vector3 size)
		{
			return size.x.ToString("0.###", CultureInfo.InvariantCulture) + " × " + size.y.ToString("0.###", CultureInfo.InvariantCulture) + " × " + size.z.ToString("0.###", CultureInfo.InvariantCulture);
		}

		private static void CaptureNewPiece(Piece __instance)
		{
			try
			{
				if (Object.op_Implicit((Object)(object)__instance))
				{
					PlacedAppearance component = ((Component)__instance).GetComponent<PlacedAppearance>();
					if (Object.op_Implicit((Object)(object)component) && Object.op_Implicit((Object)(object)Player.m_localPlayer))
					{
						component.SavePlacementDefaults();
					}
					ForgeStorageCapacity component2 = ((Component)__instance).GetComponent<ForgeStorageCapacity>();
					if (Object.op_Implicit((Object)(object)component2))
					{
						component2.InitializeNewPlacement();
					}
				}
			}
			catch (Exception arg)
			{
				DynamicStorageMaterialsPlugin current = Current;
				if (current != null)
				{
					((BaseUnityPlugin)current).Logger.LogError((object)$"Materials placement initialization failed: {arg}");
				}
			}
		}

		private void OnPiecesRegistered()
		{
			MaterialTrialCatalog.Validate();
			MaterialsItemCensus.Export();
			if (!pieceIconsQueued)
			{
				foreach (CustomPiece piecesNeedingIcon in piecesNeedingIcons)
				{
					if (piecesNeedingIcon != null && Object.op_Implicit((Object)(object)piecesNeedingIcon.PiecePrefab) && (Object)(object)piecesNeedingIcon.Piece != (Object)null)
					{
						((MonoBehaviour)this).StartCoroutine(RenderPieceIcon(piecesNeedingIcon));
					}
				}
				pieceIconsQueued = true;
			}
			ApplyForgeBuildPieceVisibility();
		}

		private IEnumerator RenderPieceIcon(CustomPiece piece)
		{
			yield return null;
			ForgeVisualStack[] componentsInChildren = ((Component)piece.Piece).GetComponentsInChildren<ForgeVisualStack>(true);
			ForgeVisualStack[] array = componentsInChildren;
			foreach (ForgeVisualStack forgeVisualStack in array)
			{
				if (Object.op_Implicit((Object)(object)forgeVisualStack))
				{
					forgeVisualStack.SetVisualsActive(55f);
				}
			}
			piece.Piece.m_icon = RenderManager.Instance.Render(new RenderRequest(piece.PiecePrefab)
			{
				Width = 64,
				Height = 64,
				Rotation = RenderManager.IsometricRotation * Quaternion.Euler(0f, -90f, 0f),
				UseCache = true,
				TargetPlugin = ((BaseUnityPlugin)this).Info.Metadata
			});
			array = componentsInChildren;
			foreach (ForgeVisualStack forgeVisualStack2 in array)
			{
				if (Object.op_Implicit((Object)(object)forgeVisualStack2))
				{
					forgeVisualStack2.SetVisualsActive(100f);
				}
			}
		}

		private void OnPrefabsRegistered()
		{
			PrefabManager.OnPrefabsRegistered -= OnPrefabsRegistered;
			int num = 0;
			PlacedAppearance.CacheSources();
			MaterialBarrelRegistration.Register();
			foreach (TemplateDefinition templateDefinition in templateDefinitions)
			{
				try
				{
					if (TryRegisterTemplatePile(templateDefinition))
					{
						num++;
					}
				}
				catch (Exception arg)
				{
					((BaseUnityPlugin)this).Logger.LogError((object)$"{templateDefinition.NewPrefabName}: {arg}");
				}
			}
			foreach (BarDefinition barDefinition in barDefinitions)
			{
				try
				{
					if (TryRegisterBarStack(barDefinition))
					{
						num++;
					}
				}
				catch (Exception arg2)
				{
					((BaseUnityPlugin)this).Logger.LogError((object)$"{barDefinition.NewPrefabName}: {arg2}");
				}
			}
			if (MaterialLogging.DebugEnabled)
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)(string.Format("{0}: registered {1} of ", "Dynamic Storage Materials", num) + $"{barDefinitions.Count + templateDefinitions.Count} storage pieces."));
			}
		}

		private bool TryRegisterBarStack(BarDefinition definition)
		{
			//IL_01ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_0211: Unknown result type (might be due to invalid IL or missing references)
			//IL_0223: Unknown result type (might be due to invalid IL or missing references)
			GameObject prefab = PrefabManager.Instance.GetPrefab(definition.BasePiecePrefab);
			if (!Object.op_Implicit((Object)(object)prefab))
			{
				return Fail(definition.NewPrefabName, "base piece '" + definition.BasePiecePrefab + "' not found");
			}
			GameObject val = FindDestroyedVfx(prefab, definition.DestroyedVfxPrefab);
			if (!Object.op_Implicit((Object)(object)val))
			{
				val = PrefabManager.Instance.GetPrefab(definition.DestroyedVfxPrefab);
			}
			if (!Object.op_Implicit((Object)(object)val))
			{
				return Fail(definition.NewPrefabName, "destroyed VFX '" + definition.DestroyedVfxPrefab + "' not found");
			}
			GameObject prefab2 = PrefabManager.Instance.GetPrefab(definition.ResourceItemPrefab);
			if (!Object.op_Implicit((Object)(object)prefab2))
			{
				return Fail(definition.NewPrefabName, "resource item '" + definition.ResourceItemPrefab + "' not found");
			}
			MeshRenderer componentInChildren = prefab2.GetComponentInChildren<MeshRenderer>(true);
			if (!Object.op_Implicit((Object)(object)componentInChildren) || !Object.op_Implicit((Object)(object)((Renderer)componentInChildren).sharedMaterial))
			{
				return Fail(definition.NewPrefabName, "resource item '" + definition.ResourceItemPrefab + "' has no bar material");
			}
			GameObject val2 = PrefabManager.Instance.CreateClonedPrefab(definition.NewPrefabName, prefab);
			if (!Object.op_Implicit((Object)(object)val2))
			{
				return Fail(definition.NewPrefabName, "CreateClonedPrefab returned null");
			}
			MeshRenderer[] componentsInChildren = val2.GetComponentsInChildren<MeshRenderer>(true);
			Component[] componentsInChildren2 = val2.GetComponentsInChildren<Component>(true);
			foreach (Component val3 in componentsInChildren2)
			{
				if (Object.op_Implicit((Object)(object)val3) && ((object)val3).GetType().Name == "SimpleMeshCombine")
				{
					Object.DestroyImmediate((Object)(object)val3);
				}
			}
			int layer = ((componentsInChildren.Length != 0) ? ((Component)componentsInChildren[0]).gameObject.layer : val2.layer);
			MeshRenderer[] array = componentsInChildren;
			for (int i = 0; i < array.Length; i++)
			{
				((Renderer)array[i]).enabled = false;
			}
			Transform transform = new GameObject("dynamic_bar_visuals").transform;
			((Component)transform).gameObject.layer = layer;
			transform.SetParent(val2.transform, false);
			transform.localPosition = val.transform.localPosition;
			transform.localRotation = val.transform.localRotation;
			transform.localScale = val.transform.localScale;
			int num = CopyBarMeshes(val, transform, ((Renderer)componentInChildren).sharedMaterial);
			if (num < 2)
			{
				return Fail(definition.NewPrefabName, $"destroyed VFX provided only {num} usable meshes");
			}
			if (!PrepareAsDynamicStorage(val2, prefab, definition.DisplayName, requireMultipleMeshes: true, transform, stripDestroyedVfx: false, isBarStack: true))
			{
				return Fail(definition.NewPrefabName, "failed to prepare dynamic storage prefab");
			}
			AttachAppearance(val2, definition.NewPrefabName, definition.ResourceItemPrefab, definition.DisplayName, isBar: true);
			if (!RegisterStandalonePiece(val2, definition.ResourceItemPrefab, definition.BuildCost))
			{
				return false;
			}
			if (MaterialLogging.DebugEnabled)
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)($"Registered {definition.DisplayName} with {num} active " + "meshes from " + definition.DestroyedVfxPrefab + "."));
			}
			return true;
		}

		private static int CopyBarMeshes(GameObject vfx, Transform targetRoot, Material barMaterial)
		{
			//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_015f: Unknown result type (might be due to invalid IL or missing references)
			int num = 0;
			MeshRenderer[] componentsInChildren = vfx.GetComponentsInChildren<MeshRenderer>(true);
			foreach (MeshRenderer val in componentsInChildren)
			{
				MeshFilter component = ((Component)val).GetComponent<MeshFilter>();
				if (!Object.op_Implicit((Object)(object)component) || !Object.op_Implicit((Object)(object)component.sharedMesh))
				{
					continue;
				}
				List<Transform> list = new List<Transform>();
				Transform val2 = ((Component)val).transform;
				while (Object.op_Implicit((Object)(object)val2) && (Object)(object)val2 != (Object)(object)vfx.transform)
				{
					list.Add(val2);
					val2 = val2.parent;
				}
				if (list.Count != 0)
				{
					Transform val3 = targetRoot;
					for (int num2 = list.Count - 1; num2 >= 0; num2--)
					{
						Transform val4 = list[num2];
						GameObject val5 = new GameObject($"bar_{num:D2}_{((Object)val4).name}")
						{
							layer = ((Component)targetRoot).gameObject.layer
						};
						Transform val6 = val3;
						val3 = val5.transform;
						val3.SetParent(val6, false);
						val3.localPosition = val4.localPosition;
						val3.localRotation = val4.localRotation;
						val3.localScale = val4.localScale;
					}
					((Component)val3).gameObject.AddComponent<MeshFilter>().sharedMesh = component.sharedMesh;
					MeshRenderer obj = ((Component)val3).gameObject.AddComponent<MeshRenderer>();
					int count = Math.Max(1, ((Renderer)val).sharedMaterials.Length);
					((Renderer)obj).sharedMaterials = Enumerable.Repeat<Material>(barMaterial, count).ToArray();
					((Renderer)obj).shadowCastingMode = ((Renderer)val).shadowCastingMode;
					((Renderer)obj).receiveShadows = ((Renderer)val).receiveShadows;
					((Renderer)obj).enabled = true;
					num++;
				}
			}
			return num;
		}

		private bool TryRegisterTemplatePile(TemplateDefinition definition)
		{
			//IL_012e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0139: Unknown result type (might be due to invalid IL or missing references)
			GameObject prefab = PrefabManager.Instance.GetPrefab(definition.BasePilePrefab);
			if (!Object.op_Implicit((Object)(object)prefab))
			{
				return Fail(definition.NewPrefabName, "base pile '" + definition.BasePilePrefab + "' not found");
			}
			GameObject prefab2 = PrefabManager.Instance.GetPrefab(definition.MaterialSourcePrefab);
			if (!Object.op_Implicit((Object)(object)prefab2))
			{
				return Fail(definition.NewPrefabName, "material source '" + definition.MaterialSourcePrefab + "' not found");
			}
			if (!Object.op_Implicit((Object)(object)PrefabManager.Instance.GetPrefab(definition.ResourceItemPrefab)))
			{
				return Fail(definition.NewPrefabName, "resource item '" + definition.ResourceItemPrefab + "' not found");
			}
			MeshRenderer componentInChildren = prefab2.GetComponentInChildren<MeshRenderer>(true);
			if (!Object.op_Implicit((Object)(object)componentInChildren) || !Object.op_Implicit((Object)(object)((Renderer)componentInChildren).sharedMaterial))
			{
				return Fail(definition.NewPrefabName, "'" + definition.MaterialSourcePrefab + "' has no usable material");
			}
			GameObject val = PrefabManager.Instance.CreateClonedPrefab(definition.NewPrefabName, prefab);
			if (!Object.op_Implicit((Object)(object)val))
			{
				return Fail(definition.NewPrefabName, "CreateClonedPrefab returned null");
			}
			if (definition.LocalScaleMultiplier != 1f)
			{
				Transform transform = val.transform;
				transform.localScale *= definition.LocalScaleMultiplier;
			}
			if (!PrepareAsDynamicStorage(val, prefab, definition.DisplayName, requireMultipleMeshes: false))
			{
				return Fail(definition.NewPrefabName, "failed to prepare dynamic storage prefab");
			}
			if (!InstallNativePileVisuals(val, definition.BasePilePrefab, definition.VisualModelPrefab, definition.ForceResourceModelGeometry))
			{
				return Fail(definition.NewPrefabName, "could not prepare native '" + definition.BasePilePrefab + "' fill stages");
			}
			AttachAppearance(val, definition.NewPrefabName, definition.VisualModelPrefab, definition.ConfigSectionName, isBar: false, definition.BasePilePrefab, definition.DefaultShape, definition.DefaultSize, definition.DefaultHeight, definition.DefaultModelScale, definition.DefaultVerticalOffset, definition.DefaultMaterial);
			if (!RegisterStandalonePiece(val, definition.ResourceItemPrefab, definition.BuildCost))
			{
				return false;
			}
			if (MaterialLogging.DebugEnabled)
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)("Registered " + definition.DisplayName + " from vanilla base pile " + definition.BasePilePrefab + "."));
			}
			return true;
		}

		private bool InstallNativePileVisuals(GameObject prefab, string visualPrefabName, string resourceItemPrefab, bool forceResourceModelGeometry = false)
		{
			//IL_0156: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bb: 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_01ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_05cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_05e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_0603: Unknown result type (might be due to invalid IL or missing references)
			//IL_060f: Unknown result type (might be due to invalid IL or missing references)
			GameObject prefab2 = PrefabManager.Instance.GetPrefab(visualPrefabName);
			GameObject prefab3 = PrefabManager.Instance.GetPrefab(resourceItemPrefab);
			MeshRenderer val = (Object.op_Implicit((Object)(object)prefab3) ? ((IEnumerable<MeshRenderer>)prefab3.GetComponentsInChildren<MeshRenderer>(true)).FirstOrDefault((Func<MeshRenderer, bool>)((MeshRenderer r) => Object.op_Implicit((Object)(object)((Component)r).GetComponent<MeshFilter>()) && Object.op_Implicit((Object)(object)((Component)r).GetComponent<MeshFilter>().sharedMesh)