Decompiled source of DynamicStorage Butchers Hall v1.0.0

plugins/DynamicStorageButchersHall/DynamicStorageButchersHall.dll

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

[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("DynamicStorageMeatRack")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("DynamicStorageMeatRack")]
[assembly: AssemblyTitle("DynamicStorageMeatRack")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class IsReadOnlyAttribute : Attribute
	{
	}
	[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 DynamicStorageMeatRack
{
	internal sealed class ConfigurationManagerAttributes
	{
		public int? Order;

		public bool? IsAdminOnly;

		public bool? ReadOnly;

		public bool? Browsable;

		public bool? IsAdvanced;
	}
	[BepInPlugin("com.muji.DynamicStorageMeatRack", "DynamicStorage Meat Rack", "1.0.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[NetworkCompatibility(/*Could not decode attribute arguments.*/)]
	internal sealed class DynamicStorageMeatRackPlugin : BaseUnityPlugin
	{
		private sealed class MeatVisualConfigEntries
		{
			internal ConfigEntry<float> Scale;

			internal ConfigEntry<float> LengthScale;

			internal ConfigEntry<float> WidthScale;

			internal ConfigEntry<float> ThicknessScale;

			internal ConfigEntry<float> RotationX;

			internal ConfigEntry<float> RotationY;

			internal ConfigEntry<float> RotationZ;

			internal ConfigEntry<float> HeightOffset;

			internal ConfigEntry<float> AnchorOffsetX;

			internal ConfigEntry<float> AnchorOffsetY;

			internal ConfigEntry<float> AnchorOffsetZ;

			internal ConfigEntry<float> RotationVarianceY;

			internal ConfigEntry<float> HeightAnchorVariance;

			internal ConfigEntry<float> ScaleVariance;
		}

		private sealed class HookAnchorConfigEntries
		{
			internal ConfigEntry<float> OffsetX;

			internal ConfigEntry<float> OffsetY;

			internal ConfigEntry<float> OffsetZ;
		}

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

		public const string PluginName = "DynamicStorage Meat Rack";

		public const string PluginVersion = "1.0.0";

		private const string FunctionalBaseName = "piece_chest_barrel";

		private const string RackVisualSourceName = "piece_cookingstation_iron";

		private const string MeatRackPrefabName = "DSMR_MeatRack";

		private static readonly Vector3[] DefaultHookAnchorOffsets = (Vector3[])(object)new Vector3[5]
		{
			new Vector3(0.5f, -0.7f, 0.01f),
			new Vector3(0.37f, -0.65f, -0.01f),
			new Vector3(0.03f, -0.7f, 0f),
			new Vector3(-0.2f, -0.65f, 0f),
			new Vector3(-0.4f, -0.7f, 0.01f)
		};

		private ConfigEntry<int> storageSlots;

		private ConfigEntry<MeatRackBehaviour> storageBehaviour;

		private ConfigEntry<float> rackScale;

		private ConfigEntry<bool> scaleMeatWithRack;

		private ConfigEntry<float> rackHeightOffset;

		private ConfigEntry<string> looksFullAt;

		private ConfigEntry<bool> advancedHookDebug;

		private ConfigEntry<bool> showStorageDebugTooltip;

		private ConfigEntry<bool> showHookAnchorOrb;

		private ConfigEntry<int> selectedHookAnchor;

		private readonly HookAnchorConfigEntries[] hookAnchorConfig = new HookAnchorConfigEntries[5];

		private readonly Dictionary<string, MeatVisualConfigEntries> meatVisualConfig = new Dictionary<string, MeatVisualConfigEntries>(StringComparer.Ordinal);

		private Harmony harmony;

		private GameObject registeredPrefab;

		private Vector3 registeredBaseScale = Vector3.one;

		private string cachedVisualProfilePayload = string.Empty;

		internal static DynamicStorageMeatRackPlugin Current { get; private set; }

		internal int StorageCapacity => NormalizeStorageCapacity((storageSlots != null) ? storageSlots.Value : 20);

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

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

		internal bool AppearanceEditorEnabled => AdvancedCalibrationEnabled;

		internal bool AdvancedHookDebugEnabled => AdvancedCalibrationEnabled;

		internal bool HookAnchorDebugOrbEnabled
		{
			get
			{
				if (AdvancedHookDebugEnabled && showHookAnchorOrb != null)
				{
					return showHookAnchorOrb.Value;
				}
				return false;
			}
		}

		internal int SelectedHookAnchorIndex => Mathf.Clamp(((selectedHookAnchor != null) ? selectedHookAnchor.Value : 3) - 1, 0, 4);

		private void Awake()
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Expected O, but got Unknown
			Current = this;
			BindConfig();
			harmony = new Harmony("com.muji.DynamicStorageMeatRack");
			MeatRackInventoryPatches.Install(harmony);
			MeatRackCapacityPatches.Install(harmony);
			MeatRackPlacementPatches.Install(harmony);
			MeatRackAppearanceEditorPatches.Install(harmony);
			MeatRackUnlocks.Install(harmony);
			PrefabManager.OnPrefabsRegistered += OnPrefabsRegistered;
			((BaseUnityPlugin)this).Logger.LogInfo((object)"DynamicStorage Meat Rack 1.0.0 loaded.");
		}

		private void OnDestroy()
		{
			PrefabManager.OnPrefabsRegistered -= OnPrefabsRegistered;
			if (harmony != null)
			{
				try
				{
					harmony.UnpatchSelf();
				}
				catch
				{
				}
			}
			if ((Object)(object)Current == (Object)(object)this)
			{
				Current = null;
			}
		}

		private void BindConfig()
		{
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Expected O, but got Unknown
			//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d3: Expected O, but got Unknown
			//IL_010b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0115: Expected O, but got Unknown
			//IL_015f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0169: Expected O, but got Unknown
			//IL_0201: Unknown result type (might be due to invalid IL or missing references)
			//IL_020b: Expected O, but got Unknown
			storageSlots = ConfigFileExtensions.BindConfig<int>(((BaseUnityPlugin)this).Config, "Server Settings", "Meat Rack Slots", 20, "Server-controlled Meat Rack inventory capacity. Existing racks always preserve stored items first; unsafe reductions are deferred or compacted safely. Options: 10, 20, 40, or 60 slots.", true, (int?)100, (AcceptableValueBase)(object)new AcceptableValueList<int>(new int[4] { 10, 20, 40, 60 }), (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			storageBehaviour = ((BaseUnityPlugin)this).Config.Bind<MeatRackBehaviour>("Rack Settings", "Storage Behaviour", MeatRackBehaviour.Auto, new ConfigDescription("Placement template saved per Meat Rack. Raw and cooked meat are always separate storage classes: the first class inserted locks an empty rack until it is emptied. Auto: one meat type grows across the rack; two or more types switch to a top-five mixed display. Single Meat: the first meat prefab locks the rack until empty. Mixed Meat: accepts all supported meats within the rack's current raw/cooked class and shows up to the five most abundant types.", (AcceptableValueBase)null, new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Order = 100
				}
			}));
			rackScale = ((BaseUnityPlugin)this).Config.Bind<float>("Rack Settings", "Rack Scale", 1f, new ConfigDescription("Placement-template scale for newly placed Meat Racks. Existing racks keep their saved scale.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 2f), new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Order = 95
				}
			}));
			scaleMeatWithRack = ((BaseUnityPlugin)this).Config.Bind<bool>("Rack Settings", "Scale Meat With Rack", true, new ConfigDescription("When enabled, hanging meat scales proportionally with Rack Scale. When disabled, meat keeps its calibrated world size. Saved per placed rack.", (AcceptableValueBase)null, new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Order = 94
				}
			}));
			rackHeightOffset = ((BaseUnityPlugin)this).Config.Bind<float>("Rack Settings", "Rack Visual Height Offset", 0f, new ConfigDescription("Vertical offset applied to the reconstructed Iron Cooking Station visual and its transplanted blocking colliders. The placed gameplay/container root remains fixed.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(-2f, 2f), new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Order = 90
				}
			}));
			looksFullAt = ((BaseUnityPlugin)this).Config.Bind<string>("Rack Settings", "Looks Full At (Single-Meat Display)", "50% Occupied Slots", new ConfigDescription("Controls when all five hooks are visually occupied in Single Meat mode, and in Auto mode while only one meat type is stored. Occupied-slot percentages scale with the rack's current effective capacity. Mixed display is unaffected.", (AcceptableValueBase)(object)new AcceptableValueList<string>(new string[10] { "25% Occupied Slots", "50% Occupied Slots", "75% Occupied Slots", "100% Occupied Slots", "10 Meat", "20 Meat", "40 Meat", "60 Meat", "100 Meat", "Always Full" }), new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Order = 85
				}
			}));
			BindHookAnchorCalibrationConfig();
			int num = 0;
			foreach (MeatDefinition definition in MeatCatalog.Definitions)
			{
				BindMeatVisualConfig(definition, num++);
			}
			storageSlots.SettingChanged += delegate
			{
				MeatRackCapacity.RefreshAll();
				MeatRackStorage.RefreshAllVisuals();
			};
			rackScale.SettingChanged += delegate
			{
				RefreshPlacementTemplateScale();
			};
			rackHeightOffset.SettingChanged += delegate
			{
				MeatRackStorage.RefreshAllVisuals();
			};
			RebuildCachedVisualProfile();
		}

		private MeatVisualTuning GetDefaultMeatVisualTuning(string prefabName)
		{
			return MeatCatalog.CalibrationPrefabName(prefabName) switch
			{
				"RawMeat" => new MeatVisualTuning(1.12f, 1f, 1f, 1f, 18f, -156f, 6f, 0.33f, 0.03f, 0.03f, 0.02f, 180f, 0.05f, 0.35f), 
				"DeerMeat" => new MeatVisualTuning(1.2f, 1f, 1f, 1f, 0f, -62f, 0f, 0.1f, 0f, -0.03f, 0f, 180f, 0.16f, 0.4f), 
				"NeckTail" => new MeatVisualTuning(1f, 1f, 1f, 1f, 174f, 48f, 0f, 0.13f, 0.04f, 0.02f, 0.02f, 75f, 0.07f, 0.38f), 
				"BjornMeat" => new MeatVisualTuning(1.2f, 1f, 1f, 0.65f, -9f, 180f, 0f, 0.14f, -0.02f, 0f, -0.05f, 39f, 0.1f, 0.5f), 
				"SerpentMeat" => new MeatVisualTuning(0.75f, 1f, 1f, 0.7f, 0f, 60f, 36f, 0.23f, 0f, 0f, 0f, 24f, 0f, 0.2f), 
				"WolfMeat" => new MeatVisualTuning(1f, 1f, 1f, 1f, 0f, 54f, -6f, 0.33f, 0.03f, -0.05f, 0.02f, 180f, 0.06f, 0.4f), 
				"LoxMeat" => new MeatVisualTuning(1f, 1f, 1f, 1f, -162f, -72f, -9f, 0.33f, 0.03f, -0.02f, 0.05f, 180f, 0.09f, 0.35f), 
				"ChickenMeat" => new MeatVisualTuning(1.25f, 1f, 1f, 1f, 3f, -3f, 0f, 0.03f, 0f, 0.12f, -0.02f, 180f, 0.05f, 0.15f), 
				"HareMeat" => new MeatVisualTuning(0.8f, 1f, 1f, 1f, 6f, 12f, 0f, 0.4f, 0f, 0.05f, 0.03f, 64.5f, 0f, 0.3f), 
				"BugMeat" => new MeatVisualTuning(1f, 1f, 1f, 1f, 3f, 0f, 3f, 0f, -0.03f, 0.13f, 0f, 49.5f, 0.05f, 0.35f), 
				"AsksvinMeat" => new MeatVisualTuning(1f, 1f, 1f, 1f, 174f, 48f, 0f, 0.13f, 0.04f, 0.02f, 0.02f, 75f, 0.07f, 0.38f), 
				"VoltureMeat" => new MeatVisualTuning(0.75f, 1f, 1f, 1f, 0f, 33f, 0f, 0.1f, -0.03f, 0f, 0f, 180f, 0.05f, 0.3f), 
				"BoneMawSerpentMeat" => new MeatVisualTuning(0.75f, 1f, 1f, 0.7f, 0f, 60f, 36f, 0.23f, 0f, 0f, 0f, 24f, 0f, 0.2f), 
				"MooseMeat" => new MeatVisualTuning(0.57f, 0.9f, 1f, 1f, 1f, 126f, 0f, 0.2f, 0.03f, 0f, 0.12f, 39f, 0.05f, 0.5f), 
				"SealBlubber" => new MeatVisualTuning(1.5f, 1f, 1f, 1f, 0f, -126f, 15f, 0.13f, 0f, 0.03f, 0.08f, 141f, 0.05f, 0.33f), 
				_ => new MeatVisualTuning(1f, 1f, 1f, 1f, 0f, 0f, 0f, 0f), 
			};
		}

		private void BindMeatVisualConfig(MeatDefinition definition, int index)
		{
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Expected O, but got Unknown
			//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ee: Expected O, but got Unknown
			//IL_0141: Unknown result type (might be due to invalid IL or missing references)
			//IL_014b: Expected O, but got Unknown
			//IL_019e: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a8: Expected O, but got Unknown
			//IL_01fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0205: Expected O, but got Unknown
			//IL_0258: Unknown result type (might be due to invalid IL or missing references)
			//IL_0262: Expected O, but got Unknown
			//IL_02b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_02bf: Expected O, but got Unknown
			//IL_0312: Unknown result type (might be due to invalid IL or missing references)
			//IL_031c: Expected O, but got Unknown
			//IL_036f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0379: Expected O, but got Unknown
			//IL_03cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_03d6: Expected O, but got Unknown
			//IL_0429: Unknown result type (might be due to invalid IL or missing references)
			//IL_0433: Expected O, but got Unknown
			//IL_0486: Unknown result type (might be due to invalid IL or missing references)
			//IL_0490: Expected O, but got Unknown
			//IL_04e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_04ed: Expected O, but got Unknown
			//IL_0540: Unknown result type (might be due to invalid IL or missing references)
			//IL_054a: Expected O, but got Unknown
			string text = "Advanced Meat Visual - " + (index + 1).ToString("00") + " " + definition.DisplayName;
			MeatVisualConfigEntries meatVisualConfigEntries = new MeatVisualConfigEntries();
			MeatVisualTuning defaultMeatVisualTuning = GetDefaultMeatVisualTuning(definition.PrefabName);
			meatVisualConfigEntries.Scale = ((BaseUnityPlugin)this).Config.Bind<float>(text, "Scale", defaultMeatVisualTuning.Scale, new ConfigDescription("Base scale for this meat model when hanging on the rack.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.05f, 5f), new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Order = 100,
					IsAdvanced = true
				}
			}));
			meatVisualConfigEntries.LengthScale = ((BaseUnityPlugin)this).Config.Bind<float>(text, "Length Scale (Long Axis)", defaultMeatVisualTuning.LengthScale, new ConfigDescription("Shape multiplier along this meat model's longest source-model axis. Use this to make a cut longer/shorter without making it uniformly chunkier.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.25f, 3f), new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Order = 98,
					IsAdvanced = true
				}
			}));
			meatVisualConfigEntries.WidthScale = ((BaseUnityPlugin)this).Config.Bind<float>(text, "Width Scale", defaultMeatVisualTuning.WidthScale, new ConfigDescription("Shape multiplier along this meat model's second-largest source-model axis. This changes width independently from overall Scale.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.25f, 3f), new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Order = 97,
					IsAdvanced = true
				}
			}));
			meatVisualConfigEntries.ThicknessScale = ((BaseUnityPlugin)this).Config.Bind<float>(text, "Thickness / Height Scale", defaultMeatVisualTuning.ThicknessScale, new ConfigDescription("Shape multiplier along this meat model's smallest source-model axis. Use this to flatten or thicken the cut independently from length/width.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.25f, 3f), new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Order = 96,
					IsAdvanced = true
				}
			}));
			meatVisualConfigEntries.RotationX = ((BaseUnityPlugin)this).Config.Bind<float>(text, "Rotation X", defaultMeatVisualTuning.RotationX, new ConfigDescription("Base X-axis rotation applied after the automatic long-axis hanging orientation.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(-180f, 180f), new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Order = 95,
					IsAdvanced = true
				}
			}));
			meatVisualConfigEntries.RotationY = ((BaseUnityPlugin)this).Config.Bind<float>(text, "Rotation Y (Twist)", defaultMeatVisualTuning.RotationY, new ConfigDescription("Base Y-axis twist around the vertical hanging axis after automatic long-axis orientation.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(-180f, 180f), new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Order = 92,
					IsAdvanced = true
				}
			}));
			meatVisualConfigEntries.RotationZ = ((BaseUnityPlugin)this).Config.Bind<float>(text, "Rotation Z", defaultMeatVisualTuning.RotationZ, new ConfigDescription("Base Z-axis rotation applied after the automatic long-axis hanging orientation.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(-180f, 180f), new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Order = 90,
					IsAdvanced = true
				}
			}));
			meatVisualConfigEntries.HeightOffset = ((BaseUnityPlugin)this).Config.Bind<float>(text, "Height Offset", defaultMeatVisualTuning.HeightOffset, new ConfigDescription("Base vertical offset for this meat model after it is aligned to the rack hook.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(-2f, 2f), new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Order = 85,
					IsAdvanced = true
				}
			}));
			meatVisualConfigEntries.AnchorOffsetX = ((BaseUnityPlugin)this).Config.Bind<float>(text, "Anchor Offset X", defaultMeatVisualTuning.AnchorOffsetX, new ConfigDescription("Additional per-meat local offset from the selected rack hook anchor on the X axis. Use this to fine-tune where the meat actually attaches without changing the shared hook calibration.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(-1f, 1f), new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Order = 84,
					IsAdvanced = true
				}
			}));
			meatVisualConfigEntries.AnchorOffsetY = ((BaseUnityPlugin)this).Config.Bind<float>(text, "Anchor Offset Y", defaultMeatVisualTuning.AnchorOffsetY, new ConfigDescription("Additional per-meat local offset from the selected rack hook anchor on the Y axis. Useful for getting awkward cuts to sit directly on the hook.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(-1f, 1f), new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Order = 83,
					IsAdvanced = true
				}
			}));
			meatVisualConfigEntries.AnchorOffsetZ = ((BaseUnityPlugin)this).Config.Bind<float>(text, "Anchor Offset Z", defaultMeatVisualTuning.AnchorOffsetZ, new ConfigDescription("Additional per-meat local offset from the selected rack hook anchor on the Z axis.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(-1f, 1f), new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Order = 82,
					IsAdvanced = true
				}
			}));
			meatVisualConfigEntries.RotationVarianceY = ((BaseUnityPlugin)this).Config.Bind<float>(text, "Rotation Y Variance", defaultMeatVisualTuning.RotationVarianceY, new ConfigDescription("Deterministic rack-specific +/- twist variance around the meat's Y axis. Useful for meats that can hang naturally at multiple twists.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 180f), new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Order = 81,
					IsAdvanced = true
				}
			}));
			meatVisualConfigEntries.HeightAnchorVariance = ((BaseUnityPlugin)this).Config.Bind<float>(text, "Height Anchor Variance", defaultMeatVisualTuning.HeightAnchorVariance, new ConfigDescription("Deterministic vertical variation applied inward into the hook while keeping the calibrated attachment point fixed.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 0.3f), new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Order = 80,
					IsAdvanced = true
				}
			}));
			meatVisualConfigEntries.ScaleVariance = ((BaseUnityPlugin)this).Config.Bind<float>(text, "Scale Variance", defaultMeatVisualTuning.ScaleVariance, new ConfigDescription("Deterministic rack-specific +/- proportional scale variation for this meat. 0.10 means up to +/-10%.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 0.5f), new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Order = 78,
					IsAdvanced = true
				}
			}));
			meatVisualConfigEntries.Scale.SettingChanged += delegate
			{
				RebuildCachedVisualProfile();
				MeatRackStorage.RefreshAllVisuals();
			};
			meatVisualConfigEntries.LengthScale.SettingChanged += delegate
			{
				RebuildCachedVisualProfile();
				MeatRackStorage.RefreshAllVisuals();
			};
			meatVisualConfigEntries.WidthScale.SettingChanged += delegate
			{
				RebuildCachedVisualProfile();
				MeatRackStorage.RefreshAllVisuals();
			};
			meatVisualConfigEntries.ThicknessScale.SettingChanged += delegate
			{
				RebuildCachedVisualProfile();
				MeatRackStorage.RefreshAllVisuals();
			};
			meatVisualConfigEntries.RotationX.SettingChanged += delegate
			{
				RebuildCachedVisualProfile();
				MeatRackStorage.RefreshAllVisuals();
			};
			meatVisualConfigEntries.RotationY.SettingChanged += delegate
			{
				RebuildCachedVisualProfile();
				MeatRackStorage.RefreshAllVisuals();
			};
			meatVisualConfigEntries.RotationZ.SettingChanged += delegate
			{
				RebuildCachedVisualProfile();
				MeatRackStorage.RefreshAllVisuals();
			};
			meatVisualConfigEntries.HeightOffset.SettingChanged += delegate
			{
				RebuildCachedVisualProfile();
				MeatRackStorage.RefreshAllVisuals();
			};
			meatVisualConfigEntries.AnchorOffsetX.SettingChanged += delegate
			{
				RebuildCachedVisualProfile();
				MeatRackStorage.RefreshAllVisuals();
			};
			meatVisualConfigEntries.AnchorOffsetY.SettingChanged += delegate
			{
				RebuildCachedVisualProfile();
				MeatRackStorage.RefreshAllVisuals();
			};
			meatVisualConfigEntries.AnchorOffsetZ.SettingChanged += delegate
			{
				RebuildCachedVisualProfile();
				MeatRackStorage.RefreshAllVisuals();
			};
			meatVisualConfigEntries.RotationVarianceY.SettingChanged += delegate
			{
				RebuildCachedVisualProfile();
				MeatRackStorage.RefreshAllVisuals();
			};
			meatVisualConfigEntries.HeightAnchorVariance.SettingChanged += delegate
			{
				RebuildCachedVisualProfile();
				MeatRackStorage.RefreshAllVisuals();
			};
			meatVisualConfigEntries.ScaleVariance.SettingChanged += delegate
			{
				RebuildCachedVisualProfile();
				MeatRackStorage.RefreshAllVisuals();
			};
			meatVisualConfig[definition.PrefabName] = meatVisualConfigEntries;
		}

		internal MeatVisualTuning GetMeatVisualTuning(string prefabName)
		{
			prefabName = MeatCatalog.CalibrationPrefabName(prefabName);
			if (!string.IsNullOrEmpty(prefabName) && meatVisualConfig.TryGetValue(prefabName, out var value))
			{
				return new MeatVisualTuning(Mathf.Clamp(value.Scale.Value, 0.05f, 5f), Mathf.Clamp(value.LengthScale.Value, 0.25f, 3f), Mathf.Clamp(value.WidthScale.Value, 0.25f, 3f), Mathf.Clamp(value.ThicknessScale.Value, 0.25f, 3f), NormalizeAngle(value.RotationX.Value), NormalizeAngle(value.RotationY.Value), NormalizeAngle(value.RotationZ.Value), Mathf.Clamp(value.HeightOffset.Value, -2f, 2f), Mathf.Clamp(value.AnchorOffsetX.Value, -1f, 1f), Mathf.Clamp(value.AnchorOffsetY.Value, -1f, 1f), Mathf.Clamp(value.AnchorOffsetZ.Value, -1f, 1f), Mathf.Clamp(value.RotationVarianceY.Value, 0f, 180f), Mathf.Clamp(value.HeightAnchorVariance.Value, 0f, 0.3f), Mathf.Clamp(value.ScaleVariance.Value, 0f, 0.5f));
			}
			return new MeatVisualTuning(1f, 1f, 1f, 1f, 0f, 0f, 0f, 0f);
		}

		private void BindHookAnchorCalibrationConfig()
		{
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Expected O, but got Unknown
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Expected O, but got Unknown
			//IL_00db: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e5: Expected O, but got Unknown
			//IL_012f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0139: Expected O, but got Unknown
			//IL_01e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_024c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0256: Expected O, but got Unknown
			//IL_0268: Unknown result type (might be due to invalid IL or missing references)
			//IL_02bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c5: Expected O, but got Unknown
			//IL_02d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_032a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0334: Expected O, but got Unknown
			showStorageDebugTooltip = ((BaseUnityPlugin)this).Config.Bind<bool>("Advanced Debug", "Show Storage Debug Tooltip", false, new ConfigDescription("Advanced/debug only. Shows live Meat Rack 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
				}
			}));
			advancedHookDebug = ((BaseUnityPlugin)this).Config.Bind<bool>("Advanced Debug", "Enable Calibration Tools", false, new ConfigDescription("Advanced/debug only. Enables the Meat Rack calibration hover, apply hotkey, hook-anchor orb controls and live advanced meat/hook tuning. Leave disabled for normal play.", (AcceptableValueBase)null, new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Order = 100,
					IsAdvanced = true
				}
			}));
			showHookAnchorOrb = ((BaseUnityPlugin)this).Config.Bind<bool>("Advanced Debug - Hook Anchor Calibration", "Show Anchor Orb", false, new ConfigDescription("Advanced/debug only: show a small emissive/lighted orb at the selected meat-hook anchor on the focused Meat Rack.", (AcceptableValueBase)null, new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Order = 100,
					IsAdvanced = true
				}
			}));
			selectedHookAnchor = ((BaseUnityPlugin)this).Config.Bind<int>("Advanced Debug - Hook Anchor Calibration", "Selected Hook", 3, new ConfigDescription("Advanced/debug only. Hook anchor selected for the debug orb. Hooks are numbered left-to-right 1 through 5.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 5), new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Order = 95,
					IsAdvanced = true
				}
			}));
			advancedHookDebug.SettingChanged += delegate
			{
				MeatRackVisual.RefreshAllDebugMarkers();
			};
			showHookAnchorOrb.SettingChanged += delegate
			{
				MeatRackVisual.RefreshAllDebugMarkers();
			};
			selectedHookAnchor.SettingChanged += delegate
			{
				MeatRackVisual.RefreshAllDebugMarkers();
			};
			for (int num = 0; num < 5; num++)
			{
				int num2 = num + 1;
				string text = "Advanced Debug - Hook Anchor " + num2;
				HookAnchorConfigEntries hookAnchorConfigEntries = new HookAnchorConfigEntries();
				Vector3 val = DefaultHookAnchorOffsets[num];
				hookAnchorConfigEntries.OffsetX = ((BaseUnityPlugin)this).Config.Bind<float>(text, "Offset X", val.x, new ConfigDescription("Advanced/debug calibration offset from the automatically estimated Hook " + num2 + " X position. Defaults retain the refined hook-inside calibration values for the vanilla Iron Cooking Station.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(-1.5f, 1.5f), new object[1]
				{
					new ConfigurationManagerAttributes
					{
						Order = 100,
						IsAdvanced = true
					}
				}));
				hookAnchorConfigEntries.OffsetY = ((BaseUnityPlugin)this).Config.Bind<float>(text, "Offset Y", val.y, new ConfigDescription("Advanced/debug calibration offset from the automatically estimated Hook " + num2 + " Y position.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(-1.5f, 1.5f), new object[1]
				{
					new ConfigurationManagerAttributes
					{
						Order = 95,
						IsAdvanced = true
					}
				}));
				hookAnchorConfigEntries.OffsetZ = ((BaseUnityPlugin)this).Config.Bind<float>(text, "Offset Z", val.z, new ConfigDescription("Advanced/debug calibration offset from the automatically estimated Hook " + num2 + " Z position.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(-1.5f, 1.5f), new object[1]
				{
					new ConfigurationManagerAttributes
					{
						Order = 90,
						IsAdvanced = true
					}
				}));
				hookAnchorConfigEntries.OffsetX.SettingChanged += delegate
				{
					RebuildCachedVisualProfile();
					MeatRackStorage.RefreshAllVisuals();
					MeatRackVisual.RefreshAllDebugMarkers();
				};
				hookAnchorConfigEntries.OffsetY.SettingChanged += delegate
				{
					RebuildCachedVisualProfile();
					MeatRackStorage.RefreshAllVisuals();
					MeatRackVisual.RefreshAllDebugMarkers();
				};
				hookAnchorConfigEntries.OffsetZ.SettingChanged += delegate
				{
					RebuildCachedVisualProfile();
					MeatRackStorage.RefreshAllVisuals();
					MeatRackVisual.RefreshAllDebugMarkers();
				};
				hookAnchorConfig[num] = hookAnchorConfigEntries;
			}
		}

		internal Vector3 GetHookAnchorOffset(int hookIndex)
		{
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			hookIndex = Mathf.Clamp(hookIndex, 0, 4);
			HookAnchorConfigEntries hookAnchorConfigEntries = hookAnchorConfig[hookIndex];
			if (hookAnchorConfigEntries == null)
			{
				return DefaultHookAnchorOffsets[hookIndex];
			}
			return new Vector3(Mathf.Clamp(hookAnchorConfigEntries.OffsetX.Value, -1.5f, 1.5f), Mathf.Clamp(hookAnchorConfigEntries.OffsetY.Value, -1.5f, 1.5f), Mathf.Clamp(hookAnchorConfigEntries.OffsetZ.Value, -1.5f, 1.5f));
		}

		internal int CycleSelectedHookAnchor(int direction)
		{
			int selectedHookAnchorIndex = SelectedHookAnchorIndex;
			if (!AdvancedHookDebugEnabled)
			{
				return selectedHookAnchorIndex;
			}
			int num = (selectedHookAnchorIndex + ((direction >= 0) ? 1 : (-1)) + 5) % 5;
			if (selectedHookAnchor != null)
			{
				selectedHookAnchor.Value = num + 1;
			}
			return num;
		}

		internal bool ToggleHookAnchorDebugOrb()
		{
			if (!AdvancedHookDebugEnabled || showHookAnchorOrb == null)
			{
				return false;
			}
			showHookAnchorOrb.Value = !showHookAnchorOrb.Value;
			return showHookAnchorOrb.Value;
		}

		internal MeatRackAppearanceTemplate GetAppearanceTemplate()
		{
			if (string.IsNullOrEmpty(cachedVisualProfilePayload))
			{
				RebuildCachedVisualProfile();
			}
			return new MeatRackAppearanceTemplate(Mathf.Clamp((rackScale != null) ? rackScale.Value : 1f, 0.5f, 2f), Mathf.Clamp((rackHeightOffset != null) ? rackHeightOffset.Value : 0f, -2f, 2f), scaleMeatWithRack == null || scaleMeatWithRack.Value, (storageBehaviour != null) ? storageBehaviour.Value : MeatRackBehaviour.Auto, ParseLooksFullAt((looksFullAt != null) ? looksFullAt.Value : "50% Occupied Slots"), cachedVisualProfilePayload);
		}

		private void RebuildCachedVisualProfile()
		{
			cachedVisualProfilePayload = MeatRackVisualProfile.Capture(this).Serialize();
		}

		private static LooksFullAtMode ParseLooksFullAt(string value)
		{
			return value switch
			{
				"25% Occupied Slots" => LooksFullAtMode.OccupiedSlots25Percent, 
				"50% Occupied Slots" => LooksFullAtMode.OccupiedSlots50Percent, 
				"75% Occupied Slots" => LooksFullAtMode.OccupiedSlots75Percent, 
				"100% Occupied Slots" => LooksFullAtMode.OccupiedSlots100Percent, 
				"10 Meat" => LooksFullAtMode.MeatCount10, 
				"20 Meat" => LooksFullAtMode.MeatCount20, 
				"40 Meat" => LooksFullAtMode.MeatCount40, 
				"60 Meat" => LooksFullAtMode.MeatCount60, 
				"100 Meat" => LooksFullAtMode.MeatCount100, 
				"Always Full" => LooksFullAtMode.AlwaysFull, 
				_ => LooksFullAtMode.OccupiedSlots50Percent, 
			};
		}

		internal static string LooksFullAtDisplayName(LooksFullAtMode mode)
		{
			return mode switch
			{
				LooksFullAtMode.OccupiedSlots25Percent => "25% occupied slots", 
				LooksFullAtMode.OccupiedSlots50Percent => "50% occupied slots", 
				LooksFullAtMode.OccupiedSlots75Percent => "75% occupied slots", 
				LooksFullAtMode.OccupiedSlots100Percent => "100% occupied slots", 
				LooksFullAtMode.MeatCount10 => "10 meat", 
				LooksFullAtMode.MeatCount20 => "20 meat", 
				LooksFullAtMode.MeatCount40 => "40 meat", 
				LooksFullAtMode.MeatCount60 => "60 meat", 
				LooksFullAtMode.MeatCount100 => "100 meat", 
				LooksFullAtMode.AlwaysFull => "Always full", 
				_ => mode.ToString(), 
			};
		}

		private void OnPrefabsRegistered()
		{
			PrefabManager.OnPrefabsRegistered -= OnPrefabsRegistered;
			MeatCatalog.Initialize();
			RegisterMeatRack();
		}

		private void RegisterMeatRack()
		{
			//IL_01ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_02da: Unknown result type (might be due to invalid IL or missing references)
			//IL_02df: Unknown result type (might be due to invalid IL or missing references)
			//IL_0302: Unknown result type (might be due to invalid IL or missing references)
			//IL_031c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0328: Unknown result type (might be due to invalid IL or missing references)
			//IL_0339: Unknown result type (might be due to invalid IL or missing references)
			//IL_0345: Unknown result type (might be due to invalid IL or missing references)
			//IL_035f: Unknown result type (might be due to invalid IL or missing references)
			//IL_036b: Unknown result type (might be due to invalid IL or missing references)
			//IL_038f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0394: Unknown result type (might be due to invalid IL or missing references)
			//IL_039f: Unknown result type (might be due to invalid IL or missing references)
			//IL_03aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_03b7: Expected O, but got Unknown
			//IL_03c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_03cc: 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_03ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_03f4: Expected O, but got Unknown
			//IL_03f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0400: Expected O, but got Unknown
			GameObject val = FindPrefab("piece_chest_barrel");
			GameObject val2 = FindPrefab("piece_cookingstation_iron");
			if (!Object.op_Implicit((Object)(object)val))
			{
				((BaseUnityPlugin)this).Logger.LogError((object)"Could not create Meat Rack: functional source 'piece_chest_barrel' was not found.");
				return;
			}
			if (!Object.op_Implicit((Object)(object)val2))
			{
				((BaseUnityPlugin)this).Logger.LogError((object)"Could not create Meat Rack: visual source 'piece_cookingstation_iron' was not found.");
				return;
			}
			Piece component = val.GetComponent<Piece>();
			ZNetView component2 = val.GetComponent<ZNetView>();
			WearNTear component3 = val.GetComponent<WearNTear>();
			Container component4 = val.GetComponent<Container>();
			Collider[] componentsInChildren = val.GetComponentsInChildren<Collider>(true);
			Piece component5 = val2.GetComponent<Piece>();
			Renderer[] componentsInChildren2 = val2.GetComponentsInChildren<Renderer>(true);
			Collider[] componentsInChildren3 = val2.GetComponentsInChildren<Collider>(true);
			if (!Object.op_Implicit((Object)(object)component) || !Object.op_Implicit((Object)(object)component2) || !Object.op_Implicit((Object)(object)component3) || !Object.op_Implicit((Object)(object)component4) || componentsInChildren.Length == 0)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("Could not create Meat Rack: 'piece_chest_barrel' was not the expected complete container (Piece=" + Bool((Object)(object)component) + ", ZNetView=" + Bool((Object)(object)component2) + ", WearNTear=" + Bool((Object)(object)component3) + ", " + $"Container={Bool((Object)(object)component4)}, Colliders={componentsInChildren.Length})."));
				return;
			}
			if (!Object.op_Implicit((Object)(object)component5) || componentsInChildren2.Length == 0)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("Could not create Meat Rack: 'piece_cookingstation_iron' did not expose the expected visual piece " + $"(Piece={Bool((Object)(object)component5)}, Renderers={componentsInChildren2.Length})."));
				return;
			}
			GameObject val3 = PrefabManager.Instance.CreateClonedPrefab("DSMR_MeatRack", val);
			if (!Object.op_Implicit((Object)(object)val3))
			{
				((BaseUnityPlugin)this).Logger.LogError((object)"Could not clone the known-good DynamicStorage container root for the Meat Rack.");
				return;
			}
			Renderer[] componentsInChildren4 = val3.GetComponentsInChildren<Renderer>(true);
			int num = 0;
			Renderer[] array = componentsInChildren4;
			foreach (Renderer val4 in array)
			{
				if (Object.op_Implicit((Object)(object)val4))
				{
					val4.enabled = false;
					num++;
				}
			}
			int meshRendererCount;
			int skinnedRendererCount;
			Vector3 alignmentOffset;
			GameObject val5 = CreateRackVisual(val3, val2, out meshRendererCount, out skinnedRendererCount, out alignmentOffset);
			if (!Object.op_Implicit((Object)(object)val5))
			{
				Object.DestroyImmediate((Object)(object)val3);
				return;
			}
			RackColliderReconstructor.Result result;
			GameObject val6 = CreateRackCollision(val3, val2, alignmentOffset, out result);
			if (!Object.op_Implicit((Object)(object)val6))
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)"Iron Cooking Station blocking collision could not be reconstructed. The inherited barrel collider remains active as a fallback.");
			}
			Piece component6 = val3.GetComponent<Piece>();
			component6.m_name = "Meat Rack";
			component6.m_description = "Dynamic raw or cooked meat storage. The first meat class inserted locks the rack to raw or cooked until it is emptied; raw and cooked meat can never be mixed.";
			if (Object.op_Implicit((Object)(object)component5.m_icon))
			{
				component6.m_icon = component5.m_icon;
			}
			Container component7 = val3.GetComponent<Container>();
			component7.m_name = "Meat Rack";
			int width;
			int height;
			if (MeatRackCapacityPatches.ProtectionAvailable)
			{
				GetStorageGrid(StorageCapacity, out width, out height);
			}
			else
			{
				width = 10;
				height = 10;
			}
			component7.m_width = width;
			component7.m_height = height;
			if (!Object.op_Implicit((Object)(object)val3.GetComponent<MeatRackCapacity>()))
			{
				val3.AddComponent<MeatRackCapacity>();
			}
			if (!Object.op_Implicit((Object)(object)val3.GetComponent<MeatRackStorage>()))
			{
				val3.AddComponent<MeatRackStorage>();
			}
			if (!Object.op_Implicit((Object)(object)val3.GetComponent<MeatRackVisual>()))
			{
				val3.AddComponent<MeatRackVisual>();
			}
			registeredBaseScale = val3.transform.localScale;
			MeatRackAppearance meatRackAppearance = val3.GetComponent<MeatRackAppearance>();
			if (!Object.op_Implicit((Object)(object)meatRackAppearance))
			{
				meatRackAppearance = val3.AddComponent<MeatRackAppearance>();
			}
			meatRackAppearance.Setup(registeredBaseScale);
			MeatRackAppearanceTemplate appearanceTemplate = GetAppearanceTemplate();
			val3.transform.localScale = Multiply(registeredBaseScale, appearanceTemplate.RackScale);
			val5.transform.localPosition = Vector3.up * appearanceTemplate.HeightOffset;
			if (Object.op_Implicit((Object)(object)val6))
			{
				val6.transform.localPosition = Vector3.up * appearanceTemplate.HeightOffset;
			}
			WearNTear component8 = val3.GetComponent<WearNTear>();
			if (Object.op_Implicit((Object)(object)component8))
			{
				component8.m_autoCreateFragments = false;
			}
			PieceConfig val7 = new PieceConfig
			{
				PieceTable = PieceTables.Hammer,
				Category = PieceCategories.Misc,
				CraftingStation = "piece_workbench"
			};
			val7.AddRequirement(new RequirementConfig("Wood", 10, 0, true));
			val7.AddRequirement(new RequirementConfig("LeatherScraps", 2, 0, true));
			val7.AddRequirement(new RequirementConfig("Flint", 2, 0, true));
			CustomPiece val8 = new CustomPiece(val3, true, val7);
			if (!PieceManager.Instance.AddPiece(val8))
			{
				((BaseUnityPlugin)this).Logger.LogError((object)"Jotunn PieceManager rejected 'Meat Rack'.");
				return;
			}
			registeredPrefab = val3;
			MeatRackUnlocks.RegisterPiece(component6);
			((BaseUnityPlugin)this).Logger.LogInfo((object)("Registered Meat Rack 1.0.0: functionalRoot='piece_chest_barrel', visualSource='piece_cookingstation_iron', " + $"preservedContainerColliders={componentsInChildren.Length}, sourceRackColliders={componentsInChildren3.Length}, " + $"reconstructedRackBlockingColliders={result.ReconstructedColliderCount}, skippedRackTriggers={result.TriggerColliderCount}, " + $"hiddenContainerRenderers={num}, rackMeshRenderers={meshRendererCount}, rackSkinnedRenderers={skinnedRendererCount}, " + $"capacity={StorageCapacity} ({width}x{height}), enabledMeats={MeatCatalog.SupportedCount}, targetMeatsFound={MeatCatalog.FoundCount}/{MeatCatalog.CandidateCount}."));
		}

		private GameObject CreateRackVisual(GameObject prefab, GameObject rackSource, out int meshRendererCount, out int skinnedRendererCount, out Vector3 alignmentOffset)
		{
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Expected O, but got Unknown
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			meshRendererCount = 0;
			skinnedRendererCount = 0;
			alignmentOffset = Vector3.zero;
			GameObject val = new GameObject("DSMR_IronRackVisual");
			val.transform.SetParent(prefab.transform, false);
			val.transform.localPosition = Vector3.zero;
			val.transform.localRotation = Quaternion.identity;
			val.transform.localScale = Vector3.one;
			val.layer = prefab.layer;
			RackVisualReconstructor.Result result = RackVisualReconstructor.Build(rackSource, val.transform, val.layer);
			meshRendererCount = result.MeshRendererCount;
			skinnedRendererCount = result.SkinnedRendererCount;
			alignmentOffset = result.AlignmentOffset;
			if (result.TotalRendererCount <= 0)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)"Could not reconstruct any renderable mesh from the Iron Cooking Station visual source.");
				Object.DestroyImmediate((Object)(object)val);
				return null;
			}
			ManualLogSource logger = ((BaseUnityPlugin)this).Logger;
			string[] obj = new string[5]
			{
				"Iron Cooking Station reconstruction created ",
				result.TotalRendererCount.ToString(),
				" visual part(s); sanitized ",
				null,
				null
			};
			int sanitizedTransformCount = result.SanitizedTransformCount;
			obj[3] = sanitizedTransformCount.ToString();
			obj[4] = " obviously scene-space transform(s). Hook-anchor calibration is available through Advanced Calibration Tools.";
			logger.LogInfo((object)string.Concat(obj));
			return val;
		}

		private GameObject CreateRackCollision(GameObject prefab, GameObject rackSource, Vector3 visualAlignmentOffset, out RackColliderReconstructor.Result result)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Expected O, but got Unknown
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			result = new RackColliderReconstructor.Result(0, 0, 0, 0, 0);
			GameObject val = new GameObject("DSMR_IronRackCollision");
			val.transform.SetParent(prefab.transform, false);
			val.transform.localPosition = Vector3.zero;
			val.transform.localRotation = Quaternion.identity;
			val.transform.localScale = Vector3.one;
			val.layer = prefab.layer;
			result = RackColliderReconstructor.Build(rackSource, val.transform, visualAlignmentOffset);
			if (result.ReconstructedColliderCount <= 0)
			{
				Object.DestroyImmediate((Object)(object)val);
				return null;
			}
			ManualLogSource logger = ((BaseUnityPlugin)this).Logger;
			int reconstructedColliderCount = result.ReconstructedColliderCount;
			logger.LogInfo((object)("Iron Cooking Station collision reconstruction created " + reconstructedColliderCount + " blocking collider(s) aligned to the rebuilt rack visual. The inherited barrel collider remains enabled as an interaction fallback."));
			return val;
		}

		private static GameObject FindPrefab(string prefabName)
		{
			GameObject val = null;
			if (Object.op_Implicit((Object)(object)ZNetScene.instance))
			{
				val = ZNetScene.instance.GetPrefab(prefabName);
			}
			if (!Object.op_Implicit((Object)(object)val))
			{
				val = PrefabManager.Instance.GetPrefab(prefabName);
			}
			return val;
		}

		private void RefreshPlacementTemplateScale()
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			if (Object.op_Implicit((Object)(object)registeredPrefab))
			{
				registeredPrefab.transform.localScale = Multiply(registeredBaseScale, GetAppearanceTemplate().RackScale);
			}
		}

		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 static float NormalizeAngle(float value)
		{
			return Mathf.Repeat(value + 180f, 360f) - 180f;
		}

		private static Vector3 Multiply(Vector3 value, float multiplier)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			return new Vector3(value.x * multiplier, value.y * multiplier, value.z * multiplier);
		}

		private static string Bool(Object value)
		{
			if (!Object.op_Implicit(value))
			{
				return "no";
			}
			return "yes";
		}
	}
	internal static class MeatRackPlacementPatches
	{
		private static readonly FieldInfo PlacementGhostField = AccessTools.Field(typeof(Player), "m_placementGhost");

		internal static bool Install(Harmony harmony)
		{
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Expected O, but got Unknown
			//IL_0130: Unknown result type (might be due to invalid IL or missing references)
			//IL_013d: Expected O, but got Unknown
			if (harmony == null)
			{
				return false;
			}
			bool result = true;
			MethodInfo methodInfo = AccessTools.Method(typeof(Player), "UpdatePlacementGhost", new Type[1] { typeof(bool) }, (Type[])null) ?? AccessTools.Method(typeof(Player), "UpdatePlacementGhost", Type.EmptyTypes, (Type[])null);
			MethodInfo methodInfo2 = AccessTools.Method(typeof(MeatRackPlacementPatches), "UpdatePlacementGhostPostfix", (Type[])null, (Type[])null);
			if (methodInfo == null || methodInfo2 == null)
			{
				Logger.LogWarning((object)"Meat Rack placement-ghost hook was not found in this Valheim build.");
				result = false;
			}
			else
			{
				try
				{
					harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(methodInfo2), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				}
				catch (Exception ex)
				{
					Logger.LogWarning((object)("Meat Rack placement-ghost hook failed: " + ex.Message));
					result = false;
				}
			}
			MethodInfo methodInfo3 = AccessTools.Method(typeof(MeatRackPlacementPatches), "SetCreatorPostfix", (Type[])null, (Type[])null);
			bool flag = false;
			MethodInfo[] methods = typeof(Piece).GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			foreach (MethodInfo methodInfo4 in methods)
			{
				ParameterInfo[] parameters = methodInfo4.GetParameters();
				if (!(methodInfo4.Name != "SetCreator") && parameters.Length != 0 && !(parameters[0].ParameterType != typeof(long)))
				{
					try
					{
						harmony.Patch((MethodBase)methodInfo4, (HarmonyMethod)null, new HarmonyMethod(methodInfo3), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
						flag = true;
					}
					catch (Exception ex2)
					{
						Logger.LogWarning((object)("Meat Rack placement snapshot hook failed: " + ex2.Message));
					}
				}
			}
			if (!flag)
			{
				Logger.LogWarning((object)"No compatible Piece.SetCreator overload was found for Meat Rack appearance snapshots.");
				result = false;
			}
			return result;
		}

		private static void UpdatePlacementGhostPostfix(Player __instance)
		{
			if (!Object.op_Implicit((Object)(object)__instance) || (Object)(object)__instance != (Object)(object)Player.m_localPlayer || PlacementGhostField == null)
			{
				return;
			}
			GameObject val;
			try
			{
				object? value = PlacementGhostField.GetValue(__instance);
				val = (GameObject)((value is GameObject) ? value : null);
			}
			catch
			{
				return;
			}
			if (!Object.op_Implicit((Object)(object)val))
			{
				return;
			}
			MeatRackAppearance component = val.GetComponent<MeatRackAppearance>();
			if (Object.op_Implicit((Object)(object)component))
			{
				DynamicStorageMeatRackPlugin current = DynamicStorageMeatRackPlugin.Current;
				if (!((Object)(object)current == (Object)null))
				{
					MeatRackAppearanceTemplate appearanceTemplate = current.GetAppearanceTemplate();
					component.ApplyPlacementPreview(appearanceTemplate.RackScale, appearanceTemplate.HeightOffset);
				}
			}
		}

		private static void SetCreatorPostfix(Piece __instance)
		{
			try
			{
				if (Object.op_Implicit((Object)(object)__instance))
				{
					MeatRackAppearance component = ((Component)__instance).GetComponent<MeatRackAppearance>();
					if (Object.op_Implicit((Object)(object)component))
					{
						component.SavePlacementDefaults();
					}
					MeatRackCapacity component2 = ((Component)__instance).GetComponent<MeatRackCapacity>();
					if (Object.op_Implicit((Object)(object)component2))
					{
						component2.InitializeNewPlacement();
					}
				}
			}
			catch (Exception ex)
			{
				Logger.LogWarning((object)("Meat Rack placement appearance snapshot failed: " + ex.Message));
			}
		}
	}
	internal sealed class MeatDefinition
	{
		internal readonly string PrefabName;

		internal readonly string DisplayName;

		internal readonly MeatStorageClass StorageClass;

		internal readonly string CalibrationPrefabName;

		internal MeatDefinition(string prefabName, string displayName, MeatStorageClass storageClass, string calibrationPrefabName)
		{
			PrefabName = prefabName;
			DisplayName = displayName;
			StorageClass = storageClass;
			CalibrationPrefabName = calibrationPrefabName;
		}
	}
	internal static class MeatCatalog
	{
		private static readonly MeatDefinition[] CalibrationCandidates = new MeatDefinition[15]
		{
			Raw("RawMeat", "Boar Meat"),
			Raw("DeerMeat", "Deer Meat"),
			Raw("NeckTail", "Neck Tail"),
			Raw("BjornMeat", "Bear Meat"),
			Raw("SerpentMeat", "Serpent Meat"),
			Raw("WolfMeat", "Wolf Meat"),
			Raw("LoxMeat", "Lox Meat"),
			Raw("ChickenMeat", "Chicken Meat"),
			Raw("HareMeat", "Hare Meat"),
			Raw("BugMeat", "Seeker Meat"),
			Raw("AsksvinMeat", "Asksvin Tail"),
			Raw("VoltureMeat", "Volture Meat"),
			Raw("BoneMawSerpentMeat", "Bonemaw Meat"),
			Raw("MooseMeat", "Moose Meat"),
			Raw("SealBlubber", "Seal Blubber")
		};

		private static readonly MeatDefinition[] Candidates = new MeatDefinition[30]
		{
			Raw("RawMeat", "Boar Meat"),
			Raw("DeerMeat", "Deer Meat"),
			Raw("NeckTail", "Neck Tail"),
			Raw("BjornMeat", "Bear Meat"),
			Raw("SerpentMeat", "Serpent Meat"),
			Raw("WolfMeat", "Wolf Meat"),
			Raw("LoxMeat", "Lox Meat"),
			Raw("ChickenMeat", "Chicken Meat"),
			Raw("HareMeat", "Hare Meat"),
			Raw("BugMeat", "Seeker Meat"),
			Raw("AsksvinMeat", "Asksvin Tail"),
			Raw("VoltureMeat", "Volture Meat"),
			Raw("BoneMawSerpentMeat", "Bonemaw Meat"),
			Raw("MooseMeat", "Moose Meat"),
			Raw("SealBlubber", "Seal Blubber"),
			Cooked("CookedMeat", "Cooked Boar Meat", "RawMeat"),
			Cooked("CookedDeerMeat", "Cooked Deer Meat", "DeerMeat"),
			Cooked("NeckTailGrilled", "Grilled Neck Tail", "NeckTail"),
			Cooked("CookedBjornMeat", "Cooked Bear Meat", "BjornMeat"),
			Cooked("SerpentMeatCooked", "Cooked Serpent Meat", "SerpentMeat"),
			Cooked("CookedWolfMeat", "Cooked Wolf Meat", "WolfMeat"),
			Cooked("CookedLoxMeat", "Cooked Lox Meat", "LoxMeat"),
			Cooked("CookedChickenMeat", "Cooked Chicken Meat", "ChickenMeat"),
			Cooked("CookedHareMeat", "Cooked Hare Meat", "HareMeat"),
			Cooked("CookedBugMeat", "Cooked Seeker Meat", "BugMeat"),
			Cooked("CookedAsksvinMeat", "Cooked Asksvin Tail", "AsksvinMeat"),
			Cooked("CookedVoltureMeat", "Cooked Volture Meat", "VoltureMeat"),
			Cooked("CookedBoneMawSerpentMeat", "Cooked Bonemaw Meat", "BoneMawSerpentMeat"),
			Cooked("CookedMooseMeat", "Cooked Moose Meat", "MooseMeat"),
			Cooked("CookedSealBlubber", "Cooked Seal Blubber", "SealBlubber")
		};

		private static readonly Dictionary<string, MeatDefinition> Found = new Dictionary<string, MeatDefinition>(StringComparer.Ordinal);

		internal static int CandidateCount => Candidates.Length;

		internal static int FoundCount => Found.Count;

		internal static int SupportedCount => Found.Count;

		internal static IEnumerable<MeatDefinition> Definitions => CalibrationCandidates;

		internal static IEnumerable<MeatDefinition> AllDefinitions => Candidates;

		internal static void Initialize()
		{
			Found.Clear();
			MeatDefinition[] candidates = Candidates;
			foreach (MeatDefinition meatDefinition in candidates)
			{
				GameObject prefab = PrefabManager.Instance.GetPrefab(meatDefinition.PrefabName);
				if (!Object.op_Implicit((Object)(object)prefab) || !Object.op_Implicit((Object)(object)prefab.GetComponent<ItemDrop>()))
				{
					Logger.LogWarning((object)("Meat prefab not found or not an ItemDrop: " + meatDefinition.PrefabName));
				}
				else
				{
					Found[meatDefinition.PrefabName] = meatDefinition;
				}
			}
			Logger.LogInfo((object)$"Meat catalogue initialized: {Found.Count}/{Candidates.Length} target raw/cooked meat prefabs found and enabled.");
		}

		internal static bool IsSupported(string prefabName)
		{
			prefabName = NormalizePrefabName(prefabName);
			if (!string.IsNullOrEmpty(prefabName))
			{
				return Found.ContainsKey(prefabName);
			}
			return false;
		}

		internal static bool IsAnyTargetMeat(string prefabName)
		{
			prefabName = NormalizePrefabName(prefabName);
			if (string.IsNullOrEmpty(prefabName))
			{
				return false;
			}
			MeatDefinition[] candidates = Candidates;
			for (int i = 0; i < candidates.Length; i++)
			{
				if (string.Equals(candidates[i].PrefabName, prefabName, StringComparison.Ordinal))
				{
					return true;
				}
			}
			return false;
		}

		internal static MeatStorageClass StorageClassOf(string prefabName)
		{
			prefabName = NormalizePrefabName(prefabName);
			if (!string.IsNullOrEmpty(prefabName) && Found.TryGetValue(prefabName, out var value))
			{
				return value.StorageClass;
			}
			MeatDefinition[] candidates = Candidates;
			foreach (MeatDefinition meatDefinition in candidates)
			{
				if (string.Equals(meatDefinition.PrefabName, prefabName, StringComparison.Ordinal))
				{
					return meatDefinition.StorageClass;
				}
			}
			return MeatStorageClass.Unassigned;
		}

		internal static string CalibrationPrefabName(string prefabName)
		{
			prefabName = NormalizePrefabName(prefabName);
			if (!string.IsNullOrEmpty(prefabName) && Found.TryGetValue(prefabName, out var value))
			{
				return value.CalibrationPrefabName;
			}
			MeatDefinition[] candidates = Candidates;
			foreach (MeatDefinition meatDefinition in candidates)
			{
				if (string.Equals(meatDefinition.PrefabName, prefabName, StringComparison.Ordinal))
				{
					return meatDefinition.CalibrationPrefabName;
				}
			}
			return prefabName;
		}

		internal static bool IsAnySupportedMeatKnown(Player player)
		{
			if (!Object.op_Implicit((Object)(object)player))
			{
				return false;
			}
			foreach (MeatDefinition value in Found.Values)
			{
				GameObject prefab = PrefabManager.Instance.GetPrefab(value.PrefabName);
				ItemDrop val = (Object.op_Implicit((Object)(object)prefab) ? prefab.GetComponent<ItemDrop>() : null);
				string text = (((Object)(object)val != (Object)null && val.m_itemData != null && val.m_itemData.m_shared != null) ? val.m_itemData.m_shared.m_name : null);
				if (!string.IsNullOrEmpty(text) && player.IsMaterialKnown(text))
				{
					return true;
				}
			}
			return false;
		}

		internal static string DisplayName(string prefabName)
		{
			prefabName = NormalizePrefabName(prefabName);
			if (!string.IsNullOrEmpty(prefabName) && Found.TryGetValue(prefabName, out var value))
			{
				return value.DisplayName;
			}
			MeatDefinition[] candidates = Candidates;
			foreach (MeatDefinition meatDefinition in candidates)
			{
				if (string.Equals(meatDefinition.PrefabName, prefabName, StringComparison.Ordinal))
				{
					return meatDefinition.DisplayName;
				}
			}
			return prefabName;
		}

		internal static string StorageClassDisplayName(MeatStorageClass storageClass)
		{
			return storageClass switch
			{
				MeatStorageClass.Raw => "raw meat", 
				MeatStorageClass.Cooked => "cooked meat", 
				_ => "unassigned", 
			};
		}

		internal static GameObject GetPrefab(string prefabName)
		{
			prefabName = NormalizePrefabName(prefabName);
			if (!IsSupported(prefabName))
			{
				return null;
			}
			return PrefabManager.Instance.GetPrefab(prefabName);
		}

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

		internal static string NormalizePrefabName(string value)
		{
			if (string.IsNullOrEmpty(value))
			{
				return null;
			}
			if (value.EndsWith("(Clone)", StringComparison.Ordinal))
			{
				value = value.Substring(0, value.Length - "(Clone)".Length);
			}
			return value.Trim();
		}

		private static MeatDefinition Raw(string prefabName, string displayName)
		{
			return new MeatDefinition(prefabName, displayName, MeatStorageClass.Raw, prefabName);
		}

		private static MeatDefinition Cooked(string prefabName, string displayName, string calibrationPrefabName)
		{
			return new MeatDefinition(prefabName, displayName, MeatStorageClass.Cooked, calibrationPrefabName);
		}
	}
	internal readonly struct MeatRackAppearanceTemplate
	{
		internal readonly float RackScale;

		internal readonly float HeightOffset;

		internal readonly bool ScaleMeatWithRack;

		internal readonly MeatRackBehaviour Behaviour;

		internal readonly LooksFullAtMode LooksFullAt;

		internal readonly string VisualProfilePayload;

		internal MeatRackAppearanceTemplate(float rackScale, float heightOffset, bool scaleMeatWithRack, MeatRackBehaviour behaviour, LooksFullAtMode looksFullAt, string visualProfilePayload)
		{
			RackScale = rackScale;
			HeightOffset = heightOffset;
			ScaleMeatWithRack = scaleMeatWithRack;
			Behaviour = behaviour;
			LooksFullAt = looksFullAt;
			VisualProfilePayload = visualProfilePayload ?? string.Empty;
		}
	}
	public sealed class MeatRackAppearance : MonoBehaviour
	{
		private const string ZdoScaleKey = "DSMR.Appearance.Scale";

		private const string ZdoHeightOffsetKey = "DSMR.Appearance.HeightOffset";

		private const string ZdoScaleMeatKey = "DSMR.Appearance.ScaleMeat";

		private const string ZdoBehaviourKey = "DSMR.Storage.Behaviour";

		private const string ZdoLooksFullModeKey = "DSMR.Appearance.LooksFullMode";

		private const string ZdoVisualProfileKey = "DSMR.Appearance.MeatProfile";

		[SerializeField]
		private Vector3 baseScale = Vector3.one;

		private ZNetView nview;

		private float nextCheck;

		private float startedAt;

		private bool pendingPlacementSnapshot;

		private MeatRackAppearanceTemplate pendingTemplate;

		private float pendingDeadline;

		private float currentScaleMultiplier = 1f;

		private float currentHeightOffset;

		private bool currentScaleMeatWithRack = true;

		private MeatRackBehaviour currentBehaviour;

		private LooksFullAtMode currentLooksFullAt = LooksFullAtMode.OccupiedSlots50Percent;

		private string currentVisualProfilePayload = string.Empty;

		private MeatRackVisualProfile currentVisualProfile;

		internal float CurrentScaleMultiplier => currentScaleMultiplier;

		internal float CurrentHeightOffset => currentHeightOffset;

		internal bool CurrentScaleMeatWithRack => currentScaleMeatWithRack;

		internal MeatRackBehaviour CurrentBehaviour => currentBehaviour;

		internal LooksFullAtMode CurrentLooksFullAt => currentLooksFullAt;

		internal void Setup(Vector3 sourceBaseScale)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			baseScale = sourceBaseScale;
		}

		private void Start()
		{
			nview = ((Component)this).GetComponent<ZNetView>();
			startedAt = Time.time;
			nextCheck = 0f;
		}

		private void Update()
		{
			if (!(Time.unscaledTime < nextCheck))
			{
				nextCheck = Time.unscaledTime + 0.25f;
				TryLoadAndApply();
			}
		}

		private MeatRackAppearanceTemplate CurrentTemplate()
		{
			DynamicStorageMeatRackPlugin current = DynamicStorageMeatRackPlugin.Current;
			if (!((Object)(object)current != (Object)null))
			{
				return new MeatRackAppearanceTemplate(1f, 0f, scaleMeatWithRack: true, MeatRackBehaviour.Auto, LooksFullAtMode.OccupiedSlots50Percent, string.Empty);
			}
			return current.GetAppearanceTemplate();
		}

		private void TryLoadAndApply()
		{
			if (!Object.op_Implicit((Object)(object)nview))
			{
				nview = ((Component)this).GetComponent<ZNetView>();
			}
			if (!Object.op_Implicit((Object)(object)nview) || !nview.IsValid() || nview.GetZDO() == null)
			{
				return;
			}
			if (pendingPlacementSnapshot)
			{
				TryCommitPendingPlacementSnapshot();
			}
			MeatRackAppearanceTemplate meatRackAppearanceTemplate = CurrentTemplate();
			ZDO zDO = nview.GetZDO();
			float num = zDO.GetFloat("DSMR.Appearance.Scale", -1f);
			float num2 = zDO.GetFloat("DSMR.Appearance.HeightOffset", float.NaN);
			int num3 = zDO.GetInt("DSMR.Appearance.ScaleMeat", -1);
			int num4 = zDO.GetInt("DSMR.Storage.Behaviour", -1);
			int num5 = zDO.GetInt("DSMR.Appearance.LooksFullMode", -1);
			string text = zDO.GetString("DSMR.Appearance.MeatProfile", string.Empty);
			bool flag = HasProfilePayload(text);
			if ((num <= 0f || float.IsNaN(num2) || num3 < 0 || num4 < 0 || num4 > 2 || num5 < 0 || num5 > 9 || !flag) && nview.IsOwner())
			{
				if (Time.time - startedAt < 1f && !pendingPlacementSnapshot)
				{
					return;
				}
				if (num <= 0f)
				{
					num = meatRackAppearanceTemplate.RackScale;
					zDO.Set("DSMR.Appearance.Scale", num);
				}
				if (float.IsNaN(num2))
				{
					num2 = meatRackAppearanceTemplate.HeightOffset;
					zDO.Set("DSMR.Appearance.HeightOffset", num2);
				}
				if (num3 < 0)
				{
					num3 = (meatRackAppearanceTemplate.ScaleMeatWithRack ? 1 : 0);
					zDO.Set("DSMR.Appearance.ScaleMeat", num3);
				}
				if (num4 < 0 || num4 > 2)
				{
					num4 = (int)meatRackAppearanceTemplate.Behaviour;
					zDO.Set("DSMR.Storage.Behaviour", num4);
				}
				if (num5 < 0 || num5 > 9)
				{
					num5 = (int)meatRackAppearanceTemplate.LooksFullAt;
					zDO.Set("DSMR.Appearance.LooksFullMode", num5);
				}
				if (!flag)
				{
					text = meatRackAppearanceTemplate.VisualProfilePayload;
					zDO.Set("DSMR.Appearance.MeatProfile", text ?? string.Empty);
					flag = HasProfilePayload(text);
				}
			}
			if (num <= 0f)
			{
				num = meatRackAppearanceTemplate.RackScale;
			}
			if (float.IsNaN(num2))
			{
				num2 = meatRackAppearanceTemplate.HeightOffset;
			}
			if (num3 < 0)
			{
				num3 = (meatRackAppearanceTemplate.ScaleMeatWithRack ? 1 : 0);
			}
			if (num4 < 0 || num4 > 2)
			{
				num4 = (int)meatRackAppearanceTemplate.Behaviour;
			}
			if (num5 < 0 || num5 > 9)
			{
				num5 = (int)meatRackAppearanceTemplate.LooksFullAt;
			}
			if (!flag)
			{
				text = meatRackAppearanceTemplate.VisualProfilePayload;
			}
			ApplyAppearance(num, num2, num3 != 0, (MeatRackBehaviour)num4, (LooksFullAtMode)num5, text);
		}

		internal void SavePlacementDefaults()
		{
			pendingTemplate = CurrentTemplate();
			pendingPlacementSnapshot = true;
			pendingDeadline = Time.time + 15f;
			TryCommitPendingPlacementSnapshot();
		}

		private bool TryCommitPendingPlacementSnapshot()
		{
			if (!pendingPlacementSnapshot)
			{
				return false;
			}
			if (Time.time > pendingDeadline)
			{
				Logger.LogWarning((object)(((Object)((Component)this).gameObject).name + ": Meat Rack placement appearance snapshot timed out before ZDO ownership became ready."));
				pendingPlacementSnapshot = false;
				return false;
			}
			if (!Object.op_Implicit((Object)(object)nview))
			{
				nview = ((Component)this).GetComponent<ZNetView>();
			}
			if (!Object.op_Implicit((Object)(object)nview) || !nview.IsValid() || nview.GetZDO() == null || !nview.IsOwner())
			{
				return false;
			}
			WriteTemplate(nview.GetZDO(), pendingTemplate);
			pendingPlacementSnapshot = false;
			Logger.LogInfo((object)(((Object)((Component)this).gameObject).name + ": saved Meat Rack placement template (behaviour=" + pendingTemplate.Behaviour.ToString() + ")."));
			return true;
		}

		internal void ApplyPlacementPreview(float scaleMultiplier, float heightOffset)
		{
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			currentScaleMultiplier = Mathf.Clamp(scaleMultiplier, 0.5f, 2f);
			currentHeightOffset = Mathf.Clamp(heightOffset, -2f, 2f);
			((Component)this).transform.localScale = Multiply(baseScale, currentScaleMultiplier);
			ApplyRackVisualHeight(currentHeightOffset);
		}

		internal bool ApplyCurrentTemplateFromConfig(out string result)
		{
			if (!Object.op_Implicit((Object)(object)nview))
			{
				nview = ((Component)this).GetComponent<ZNetView>();
			}
			if (!Object.op_Implicit((Object)(object)nview) || !nview.IsValid() || nview.GetZDO() == null)
			{
				result = "Placed Meat Rack has no writable ZDO.";
				return false;
			}
			if (!nview.IsOwner())
			{
				result = "Cannot apply: this client is not the ZDO owner.";
				return false;
			}
			MeatRackAppearanceTemplate template = CurrentTemplate();
			WriteTemplate(nview.GetZDO(), template);
			ApplyAppearance(template.RackScale, template.HeightOffset, template.ScaleMeatWithRack, template.Behaviour, template.LooksFullAt, template.VisualProfilePayload);
			result = "Applied current Meat Rack template and visual calibration snapshot.";
			Logger.LogInfo((object)(((Object)((Component)this).gameObject).name + ": " + result));
			return true;
		}

		internal MeatVisualTuning GetMeatVisualTuning(string prefabName)
		{
			DynamicStorageMeatRackPlugin current = DynamicStorageMeatRackPlugin.Current;
			if ((Object)(object)current != (Object)null && current.AdvancedCalibrationEnabled)
			{
				return current.GetMeatVisualTuning(prefabName);
			}
			if (currentVisualProfile != null)
			{
				return currentVisualProfile.GetMeatTuning(prefabName);
			}
			if (!((Object)(object)current != (Object)null))
			{
				return new MeatVisualTuning(1f, 1f, 1f, 1f, 0f, 0f, 0f, 0f);
			}
			return current.GetMeatVisualTuning(prefabName);
		}

		internal Vector3 GetHookAnchorOffset(int hookIndex)
		{
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			DynamicStorageMeatRackPlugin current = DynamicStorageMeatRackPlugin.Current;
			if ((Object)(object)current != (Object)null && current.AdvancedCalibrationEnabled)
			{
				return current.GetHookAnchorOffset(hookIndex);
			}
			if (currentVisualProfile != null)
			{
				return currentVisualProfile.GetHookOffset(hookIndex);
			}
			if (!((Object)(object)current != (Object)null))
			{
				return Vector3.zero;
			}
			return current.GetHookAnchorOffset(hookIndex);
		}

		private static void WriteTemplate(ZDO zdo, MeatRackAppearanceTemplate template)
		{
			zdo.Set("DSMR.Appearance.Scale", Mathf.Clamp(template.RackScale, 0.5f, 2f));
			zdo.Set("DSMR.Appearance.HeightOffset", Mathf.Clamp(template.HeightOffset, -2f, 2f));
			zdo.Set("DSMR.Appearance.ScaleMeat", template.ScaleMeatWithRack ? 1 : 0);
			zdo.Set("DSMR.Storage.Behaviour", (int)template.Behaviour);
			zdo.Set("DSMR.Appearance.LooksFullMode", (int)template.LooksFullAt);
			zdo.Set("DSMR.Appearance.MeatProfile", template.VisualProfilePayload ?? string.Empty);
		}

		private void ApplyAppearance(float scaleMultiplier, float heightOffset, bool scaleMeatWithRack, MeatRackBehaviour behaviour, LooksFullAtMode looksFullAt, string visualProfilePayload)
		{
			//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ef: Unknown result type (might be due to invalid IL or missing references)
			bool flag = !string.Equals(currentVisualProfilePayload, visualProfilePayload ?? string.Empty, StringComparison.Ordinal);
			bool num = currentBehaviour != behaviour || currentScaleMeatWithRack != scaleMeatWithRack || currentLooksFullAt != looksFullAt || Mathf.Abs(currentScaleMultiplier - scaleMultiplier) > 0.0001f || Mathf.Abs(currentHeightOffset - heightOffset) > 0.0001f || flag;
			currentScaleMultiplier = Mathf.Clamp(scaleMultiplier, 0.5f, 2f);
			currentHeightOffset = Mathf.Clamp(heightOffset, -2f, 2f);
			currentScaleMeatWithRack = scaleMeatWithRack;
			currentBehaviour = behaviour;
			currentLooksFullAt = looksFullAt;
			currentVisualProfilePayload = visualProfilePayload ?? string.Empty;
			if (flag || currentVisualProfile == null)
			{
				currentVisualProfile = (MeatRackVisualProfile.TryDeserialize(currentVisualProfilePayload, out var profile) ? profile : null);
			}
			((Component)this).transform.localScale = Multiply(baseScale, currentScaleMultiplier);
			ApplyRackVisualHeight(currentHeightOffset);
			if (!num)
			{
				return;
			}
			MeatRackStorage component = ((Component)this).GetComponent<MeatRackStorage>();
			if (Object.op_Implicit((Object)(object)component))
			{
				component.ForceRefresh();
				return;
			}
			MeatRackVisual component2 = ((Component)this).GetComponent<MeatRackVisual>();
			if (Object.op_Implicit((Object)(object)component2))
			{
				component2.Refresh(null);
			}
		}

		private void ApplyRackVisualHeight(float heightOffset)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			Vector3 localPosition = Vector3.up * heightOffset;
			Transform val = FindDescendant(((Component)this).transform, "DSMR_IronRackVisual");
			if (Object.op_Implicit((Object)(object)val))
			{
				val.localPosition = localPosition;
			}
			Transform val2 = FindDescendant(((Component)this).transform, "DSMR_IronRackCollision");
			if (Object.op_Implicit((Object)(object)val2))
			{
				val2.localPosition = localPosition;
			}
		}

		private static bool HasProfilePayload(string payload)
		{
			if (!string.IsNullOrEmpty(payload))
			{
				return payload.StartsWith("1|", StringComparison.Ordinal);
			}
			return false;
		}

		private static Transform FindDescendant(Transform root, string name)
		{
			if (!Object.op_Implicit((Object)(object)root))
			{
				return null;
			}
			Transform[] componentsInChildren = ((Component)root).GetComponentsInChildren<Transform>(true);
			foreach (Transform val in componentsInChildren)
			{
				if (Object.op_Implicit((Object)(object)val) && ((Object)val).name == name)
				{
					return val;
				}
			}
			return null;
		}

		private static Vector3 Multiply(Vector3 value, float multiplier)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			return new Vector3(value.x * multiplier, value.y * multiplier, value.z * multiplier);
		}
	}
	internal static class MeatRackAppearanceEditorPatches
	{
		private static int lastApplyFrame = -1;

		private static int lastHookDebugFrame = -1;

		internal static bool Install(Harmony harmony)
		{
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Expected O, but got Unknown
			if (harmony == null)
			{
				return false;
			}
			MethodInfo methodInfo = AccessTools.Method(typeof(Container), "GetHoverText", Type.EmptyTypes, (Type[])null);
			MethodInfo methodInfo2 = AccessTools.Method(typeof(MeatRackAppearanceEditorPatches), "GetHoverTextPostfix", (Type[])null, (Type[])null);
			if (methodInfo == null || methodInfo2 == null)
			{
				Logger.LogWarning((object)"Container.GetHoverText() not found; Meat Rack appearance editor is unavailable.");
				return false;
			}
			try
			{
				harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(methodInfo2), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				return true;
			}
			catch (Exception ex)
			{
				Logger.LogWarning((object)("Could not install Meat Rack appearance editor hover: " + ex.Message));
				return false;
			}
		}

		private static void GetHoverTextPostfix(Container __instance, ref string __result)
		{
			try
			{
				DynamicStorageMeatRackPlugin current = DynamicStorageMeatRackPlugin.Current;
				if ((Object)(object)current == (Object)null || !Object.op_Implicit((Object)(object)__instance))
				{
					return;
				}
				MeatRackCapacity component = ((Component)__instance).GetComponent<MeatRackCapacity>();
				if (Object.op_Implicit((Object)(object)component))
				{
					__result += component.SafetyWarning;
					if (current.ShowStorageDebugTooltip)
					{
						__result += component.DebugStatus;
					}
				}
				if (!current.AppearanceEditorEnabled)
				{
					return;
				}
				MeatRackAppearance component2 = ((Component)__instance).GetComponent<MeatRackAppearance>();
				MeatRackStorage component3 = ((Component)__instance).GetComponent<MeatRackStorage>();
				if (!Object.op_Implicit((Object)(object)component2) || !Object.op_Implicit((Object)(object)component3))
				{
					return;
				}
				if (Input.GetKeyDown((KeyCode)114) && Time.frameCount != lastApplyFrame)
				{
					lastApplyFrame = Time.frameCount;
					string result;
					bool num = component2.ApplyCurrentTemplateFromConfig(out result);
					Player localPlayer = Player.m_localPlayer;
					if (Object.op_Implicit((Object)(object)localPlayer))
					{
						((Character)localPlayer).Message((MessageType)2, result, 0, (Sprite)null, false);
					}
					if (!num)
					{
						Logger.LogWarning((object)(((Object)((Component)__instance).gameObject).name + ": Meat Rack appearance editor apply rejected: " + result));
					}
				}
				MeatRackVisual component4 = ((Component)__instance).GetComponent<MeatRackVisual>();
				if (Object.op_Implicit((Object)(object)component4) && current.AdvancedHookDebugEnabled)
				{
					component4.SetHookDebugFocus();
				}
				if (Object.op_Implicit((Object)(object)component4) && current.AdvancedHookDebugEnabled && Time.frameCount != lastHookDebugFrame)
				{
					string text = null;
					if (Input.GetKeyDown((KeyCode)93))
					{
						text = component4.CycleHookAnchorDebug(1);
					}
					else if (Input.GetKeyDown((KeyCode)91))
					{
						text = component4.CycleHookAnchorDebug(-1);
					}
					else if (Input.GetKeyDown((KeyCode)305))
					{
						text = component4.ToggleHookAnchorDebug();
					}
					if (!string.IsNullOrEmpty(text))
					{
						lastHookDebugFrame = Time.frameCount;
						Player localPlayer2 = Player.m_localPlayer;
						if (Object.op_Implicit((Object)(object)localPlayer2))
						{
							((Character)localPlayer2).Message((MessageType)2, text, 0, (Sprite)null, false);
						}
					}
				}
				__result += BuildEditorHoverText(component2, component3, component4);
			}
			catch (Exception ex)
			{
				Logger.LogWarning((object)("Meat Rack appearance editor hover failed: " + ex.Message));
			}
		}

		private static string BuildEditorHoverText(MeatRackAppearance appearance, MeatRackStorage storage, MeatRackVisual visual)
		{
			string text = (string.IsNullOrEmpty(storage.AssignedPrefabName) ? "None" : MeatCatalog.DisplayName(storage.AssignedPrefabName));
			DynamicStorageMeatRackPlugin current = DynamicStorageMeatRackPlugin.Current;
			Container container = storage.Container;
			int num = (Object.op_Implicit((Object)(object)container) ? Mathf.Max(1, container.m_width * container.m_height) : (((Object)(object)current != (Object)null) ? current.StorageCapacity : 20));
			return "\n<color=#D66B5D><b>MEAT RACK CALIBRATION</b></color>\n<color=#F0C8BE>Behaviour:</color> <color=#FFFFFF>" + appearance.CurrentBehaviour.ToString() + "</color>\n<color=#F0C8BE>Storage class:</color> <color=#FFFFFF>" + MeatCatalog.StorageClassDisplayName(storage.StorageClass) + "</color>\n<color=#F0C8BE>Assigned (Single only):</color> <color=#FFFFFF>" + text + "</color>\n<color=#F0C8BE>Stored meat:</color> <color=#FFFFFF>" + storage.StoredCount + " across " + storage.DistinctMeatCount + " type(s)</color>\n<color=#F0C8BE>Rack scale:</color> <color=#FFFFFF>" + appearance.CurrentScaleMultiplier.ToString("0.###") + "</color>\n<color=#F0C8BE>Rack visual height:</color> <color=#FFFFFF>" + appearance.CurrentHeightOffset.ToString("0.###") + "</color>\n<color=#F0C8BE>Scale meat with rack:</color> <color=#FFFFFF>" + (appearance.CurrentScaleMeatWithRack ? "On" : "Off") + "</color>\n<color=#F0C8BE>Looks full at (single):</color> <color=#FFFFFF>" + DynamicStorageMeatRackPlugin.LooksFullAtDisplayName(appearance.CurrentLooksFullAt) + "</color>\n<color=#F0C8BE>Effective capacity:</color> <color=#FFFFFF>" + num + " slots</color>\n<color=#AAB4BC>Advanced meat/hook calibration is live while Calibration Tools are enabled; [R] snapshots it onto this rack.</color>" + (((Object)(object)current != (Object)null && current.AdvancedHookDebugEnabled) ? ("\n<color=#F0C8BE>Hook calibration:</color> <color=#FFFFFF>" + (Object.op_Implicit((Object)(object)visual) ? visual.HookAnchorDebugLabel : "Unavailable") + "</color>\n<color=#FFD166><b>[ / ]</b></color> <color=#AAB4BC>Cycle selected meat-hook anchor</color>\n<color=#FFD166><b>Right Ctrl</b></color> <color=#AAB4BC>Toggle bright anchor orb</color>") : string.Empty) + "\n<color=#FFD166><b>[R]</b></color> <color=#AAB4BC>Apply current rack template/behaviour to this placed rack</color>";
		}
	}
	public sealed class MeatRackCapacity : MonoBehaviour
	{
		private sealed class ItemSnapshot
		{
			internal ItemData Item;

			internal Vector2i Position;

			internal string PrefabName;

			internal int Stack;

			internal int Quality;

			internal float Durability;

			internal int Variant;

			internal long CrafterId;

			internal string CrafterName;
		}

		internal const int MaximumRecoveryCapacity = 80;

		internal const int UiSafeMaximumWidth = 8;

		internal const int MigrationEnvelopeWidth = 10;

		internal const int MigrationEnvelopeHeight = 10;

		private static readonly HashSet<MeatRackCapacity> Active = new HashSet<MeatRackCapacity>();

		private static readonly FieldInfo InventoryWidth = AccessTools.Field(typeof(Inventory), "m_width");

		private static readonly FieldInfo InventoryHeight = AccessTools.Field(typeof(Inventory), "m_height");

		private static readonly MethodInfo ContainerIsInUse = AccessTools.Method(typeof(Container), "IsInUse", Type.EmptyTypes, (Type[])null);

		private static readonly MethodInfo InventoryChangedMethod = ResolveInventoryChangedMethod();

		private Container container;

		private Inventory inventory;

		private ZNetView nview;

		private float nextRefresh;

		private bool newPlacementConfirmed;

		private bool interactionConfirmed;

		private bool observedNonEmptyInventory;

		private bool warnedMissingChanged;

		private int lastKnownInaccessibleStacks;

		private string lastSafetyAction = "None";

		internal string SafetyWarning
		{
			get
			{
				CacheReferences();
				int num = RefreshHiddenStackObservation();
				if (num <= 0)
				{
					return string.Empty;
				}
				int num2 = CountStacks(inventory);
				int num3 = Mathf.Max(0, num2 - 80);
				if (num3 > 0)
				{
					return "\n<color=#FF5555><b>STORAGE SAFETY WARNING</b></color>\n<color=#FF7777>" + num + " stored stack(s) are currently inaccessible in the container UI. The items are still present.</color>\n<color=#FFAAAA>Remove at least " + num3 + " stack(s) so every item can be restored to visible slots safely.</color>";
				}
				return "\n<color=#FF5555><b>STORAGE SAFETY WARNING</b></color>\n<color=#FF7777>" + num + " stored stack(s) are currently inaccessible in the container UI. The items are still present.</color>\n<color=#FFAAAA>Automatic recovery will move them into visible slots only after a fully validated layout is available and the rack is closed. If this warning persists, enable Advanced Debug -> Show Storage Debug Tooltip and check Last safety action.</color>";
			}
		}

		internal string DebugStatus
		{
			get
			{
				CacheReferences();
				DynamicStorageMeatRackPlugin current = DynamicStorageMeatRackPlugin.Current;
				int num = (((Object)(object)current != (Object)null) ? current.StorageCapacity : (-1));
				int width = ((inventory != null) ? ReadDimension(InventoryWidth, inventory, Object.op_Implicit((Object)(object)container) ? container.m_width : 0) : (Object.op_Implicit((Object)(object)container) ? container.m_width : 0));
				int height = ((inventory != null) ? ReadDimension(InventoryHeight, inventory, Object.op_Implicit((Object)(object)container) ? container.m_height : 0) : (Object.op_Implicit((Object)(object)container) ? container.m_height : 0));
				int num2 = CountStacks(inventory);
				GetOccupiedDiagnostics(inventory, width, height, out var bounds, out var outsideLiveGrid, out var inaccessibleUi);
				int num3 = ((inventory != null) ? ((object)inventory).GetHashCode() : 0);
				int num4 = (Object.op_Implicit((Object)(object)container) ? container.m_width : (-1));
				int num5 = (Object.op_Implicit((Object)(object)container) ? container.m_height : (-1));
				bool known;
				bool flag = IsContainerInUse(out known);
				string text = ((!Object.op_Implicit((Object)(object)nview) || !nview.IsValid()) ? "Unknown" : (nview.IsOwner() ? "Yes" : "No"));
				return "\n<color=#8FD3FF><b>MEAT RACK STORAGE DEBUG</b></color>\n<color=#B8E3FF>Desired:</color> <color=#FFFFFF>" + num + " slots</color>\n<color=#B8E3FF>Inventory grid:</color> <color=#FFFFFF>" + width + "x" + height + "</color>\n<color=#B8E3FF>Container grid:</color> <color=#FFFFFF>" + num4 + "x" + num5 + "</color>\n<color=#B8E3FF>Stacks:</color> <color=#FFFFFF>" + num2 + "</color>\n<color=#B8E3FF>Occupied bounds:</color> <color=#FFFFFF>" + bounds + "</color>\n<color=#B8E3FF>Outside live grid:</color> <color=#FFFFFF>" + outsideLiveGrid + "</color>\n<color=#FF8E8E>Inaccessible in current UI:</color> <color=#FFFFFF>" + inaccessibleUi + "</color>\n<color=#B8E3FF>Authoritative owner:</color> <color=#FFFFFF>" + text + "</color>\n<color=#B8E3FF>Container open:</color> <color=#FFFFFF>" + ((!known) ? "Unknown" : (flag ? "Yes" : "No")) + "</color>\n<color=#B8E3FF>Inventory ID:</color> <color=#FFFFFF>" + num3 + "</color>\n<color=#B8E3FF>Last safety action:</color> <color=#FFFFFF>" + lastSafetyAction + "</color>";
			}
		}

		private void OnEnable()
		{
			Active.Add(this);
			nextRefresh = 0f;
		}

		private void OnDisable()
		{
			Active.Remove(this);
		}

		private void OnDestroy()
		{
			Active.Remove(this);
		}

		private void Start()
		{
			CacheReferences();
			ReconcileCapacity();
		}

		private void Update()
		{
			if (!(Time.unscaledTime < nextRefresh))
			{
				nextRefresh = Time.unscaledTime + 0.35f;
				ReconcileCapacity();
			}
		}

		internal static void RefreshAll()
		{
			MeatRackCapacity[] array = new MeatRackCapacity[Active.Count];
			Active.CopyTo(array);
			MeatRackCapacity[] array2 = array;
			foreach (MeatRackCapacity meatRackCapacity in array2)
			{
				if (Object.op_Implicit((Object)(object)meatRackCapacity))
				{
					meatRackCapacity.nextRefresh = 0f;
					meatRackCapacity.ReconcileCapacity();
					meatRackCapacity.RefreshVisualFill();
				}
			}
		}

		internal void InitializeNewPlacement()
		{
			CacheReferences();
			newPlacementConfirmed = true;
			nextRefresh = 0f;
			ReconcileCapacity();
		}

		internal void NotifyContainerInteraction()
		{
			CacheReferences();
			interactionConfirmed = true;
			nextRefresh = 0f;
			ReconcileCapacity();
		}

		private void ReconcileCapacity()
		{
			DynamicStorageMeatRackPlugin current = DynamicStorageMeatRackPlugin.Current;
			if ((Object)(object)current == (Object)null)
			{
				return;
			}
			CacheReferences();
			if (Object.op_Implicit((Object)(object)container) && inventory != null)
			{
				if (CountStacks(inventory) > 0)
				{
					observedNonEmptyInventory = true;
				}
				ApplySafeCapacity(current.StorageCapacity);
			}
		}

		private void ApplySafeCapacity(int desiredCapacity)
		{
			int num = CountStacks(inventory);
			if (num < 0)
			{
				return;
			}
			if (!MeatRackCapacityPatches.ProtectionAvailable || InventoryWidth == null || InventoryHeight == null)
			{
				EnsureAtLeastMigrationEnvelope();
				lastSafetyAction = "Fail-closed: capacity load protection unavailable";
				return;
			}
			if (!observedNonEmptyInventory && !newPlacementConfirmed && !interactionConfirmed && num == 0)
			{
				EnsureAtLeastMigrationEnvelope();
				lastSafetyAction = "Waiting for positive inventory observation";
				return;
			}
			int num2 = SmallestSupportedCapacityForStacks(num);
			int num3 = DynamicStorageMeatRackPlugin.NormalizeStorageCapacity(desiredCapacity);
			int num4 = Mathf.Max(num3, num2);
			if (num4 > 80)
			{
				EnsureAtLeastMigrationEnvelope();
				lastSafetyAction = "Deferred: more than 80 stacks require the recovery envelope";
				return;
			}
			DynamicStorageMeatRackPlugin.GetSafeStorageGrid(num4, out var width, out var height);
			if (width > 8)
			{
				EnsureAtLeastMigrationEnvelope();
				lastSafetyAction = "Refused non-UI-safe target width " + width;
				Logger.LogError((object)(((Object)((Component)this).gameObject).name + ": Capacity safety refused target wider than 8 columns."));
				return;
			}
			int num5 = ReadDimension(InventoryWidth, inventory, container.m_width);
			int num6 = ReadDimension(InventoryHeight, inventory, container.m_height);
			if (num5 == width && num6 == height && container.m_width == width && container.m_height == height && CountInaccessibleStacks(inventory, width, height) == 0)
			{
				RefreshHiddenStackObservation();
				lastSafetyAction = ((num4 == num3) ? "Grid stable" : "Holding larger grid because stack count exceeds requested capacity");
				return;
			}
			bool known;
			bool flag = IsContainerInUse(out known);
			if (!known)
			{
				lastSafetyAction = "Deferred: could not prove container is closed";
				return;
			}
			if (flag)
			{
				lastSafetyAction = "Deferred while container is open";
				return;
			}
			List<ItemSnapshot> list = CaptureSnapshots();
			if (list == null)
			{
				lastSafetyAction = "Deferred: could not snapshot inventory";
				return;
			}
			if (!TryBuildRelocationPlan(list, width, height, out var plan))
			{
				lastSafetyAction = "Deferred: no validated destination for every stack";
				return;
			}
			int num7 = CountPlannedMoves(plan);
			if (num7 > 0 && !CanMutateInventory())
			{
				lastSafetyAction = "Deferred relocation: waiting for authoritative owner";
				return;
			}
			if (num7 > 0 && InventoryChangedMethod == null)
			{
				if (!warnedMissingChanged)
				{
					warnedMissingChanged = true;
					Logger.LogError((object)"Meat Rack cannot resolve Inventory.Changed; item-coordinate migration is disabled to preserve items.");
				}
				lastSafetyAction = "Deferred: persistence callback unavailable";
				return;
			}
			EnsureAtLeastMigrationEnvelope();
			if (num7 > 0)
			{
				ApplyPlan(plan);
				if (!ValidateSnapshots(list, width, height))
				{
					RestoreSnapshots(list);
					lastSafetyAction = "Rollback: validation failed before persistence";
					Logger.LogError((object)(((Object)((Component)this).gameObject).name + ": Capacity relocation validation failed; original positions restored."));
					return;
				}
				if (!NotifyInventoryChanged(inventory))
				{
					RestoreSnapshots(list);
					NotifyInventoryChanged(inventory);
					lastSafetyAction = "Rollback: could not persist relocated coordinates";
					Logger.LogError((object)(((Object)((Component)this).gameObject).name + ": Capacity relocation persistence failed; original positions restored."));
					return;
				}
				if (!ValidateSnapshots(list, width, height))
				{
					RestoreSnapshots(list);
					NotifyInventoryChanged(inventory);
					lastSafetyAction = "Rollback: post-persistence validation failed";
					Logger.LogError((object)(((Object)((Component)this).gameObject).name + ": Capacity relocation post-persistence validation failed; original positions restored."));
					return;
				}
			}
			container.m_width = width;
			container.m_height = height;
			WriteDimension(InventoryWidth, inventory, width);
			WriteDimension(InventoryHeight, inventory, height);
			RefreshHiddenStackObservation();
			lastSafetyAction = ((num7 > 0) ? $"Restored {num7} stack(s); committed {width}x{height}" : $"Committed UI-safe grid {width}x{height}");
			Logger.LogInfo((object)(((Object)((Component)this).gameObject).name + $": safe grid committed {num5}x{num6} -> {width}x{height}; " + $"stacks={num}, moved={num7}, requested={num3}, effective={num4}."));
		}

		private void EnsureAtLeastMigrationEnvelope()
		{
			if (Object.op_Implicit((Object)(object)container) && inventory != null)
			{
				int num = ReadDimension(InventoryWidth, inventory, container.m_width);
				int num2 = ReadDimension(InventoryHeight, inventory, container.m_height);
				int num3 = Mathf.Max(num, 10);
				int num4 = Mathf.Max(num2, 10);
				container.m_width = num3;
				container.m_height = num4;
				WriteDimension(InventoryWidth, inventory, num3);
				WriteDimension(InventoryHeight, inventory, num4);
			}
		}

		private bool CanMutateInventory()
		{
			if (!Object.op_Implicit((Object)(object)nview) || !nview.IsValid() || nview.GetZDO() == null)
			{
				return false;
			}
			return nview.IsOwner();
		}

		private bool IsContainerInUse(out bool known)
		{
			known = false;
			if (!Object.op_Implicit((Object)(object)container) || ContainerIsInUse == null)
			{
				return false;
			}
			try
			{
				object obj = ContainerIsInUse.Invoke(container, null);
				if (obj is bool)
				{
					known = true;
					return (bool)obj;
				}
			}
			catch
			{
			}
			return false;
		}

		private List<ItemSnapshot> CaptureSnapshots()
		{
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			if (inventory == null)
			{
				return null;
			}
			List<ItemData> allItems = inventory.GetAllItems();
			if (allItems == null)
			{
				return new List<ItemSnapshot>();
			}
			List<ItemSnapshot> list = new List<ItemSnapshot>(allItems.Count);
			foreach (ItemData item in allItems)
			{
				if (item != null)
				{
					list.Add(new ItemSnapshot
					{
						Item = item,
						Position = item.m_gridPos,
						PrefabName = (Object.op_Implicit((Object)(object)item.m_dropPrefab) ? ((Object)item.m_dropPrefab).name : string.Empty),
						Stack = item.m_stack,
						Quality = item.m_quality,
						Durability = item.m_durability,
						Variant = item.m_variant,
						CrafterId = item.m_crafterID,
						CrafterName = (item.m_crafterName ?? string.Empty)
					});
				}
			}
			return list;
		}

		private static bool TryBuildRelocationPlan(List<ItemSnapshot> snapshots, int width, int height, out Dictionary<ItemData, Vector2i> plan)
		{
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0127: Unknown result type (might be due to invalid IL or missing references)
			plan = new Dictionary<ItemData, Vector2i>();
			if (snapshots == null)
			{
				return false;
			}
			if (snapshots.Count > width * height)
			{
				return false;
			}
			bool[,] array = new bool[width, height];
			List<ItemSnapshot> list = new List<ItemSnapshot>();
			foreach (ItemSnapshot snapshot in snapshots)
			{
				if (snapshot == null || snapshot.Item == null)
				{
					return false;
				}
				int x = snapshot.Position.x;
				int y = snapshot.Position.y;
				if (x >= 0 && y >= 0 && x < width && y < height && !array[x, y])
				{
					array[x, y] = true;
					plan[snapshot.Item] = snapshot.Position;
				}
				else
				{
					list.Add(snapshot);
				}
			}
			int num = 0;
			int num2 = 0;
			foreach (ItemSnapshot item in list)
			{
				bool flag = false;
				for (int i = num2; i < height; i++)
				{
					if (flag)
					{
						break;
					}
					for (int j = ((i == num2) ? num : 0); j < width; j++)
					{
						if (!array[j, i])
						{
							array[j, i] = true;
							plan[item.Item] = new Vector2i(j, i);
							num = j + 1;
							num2 = i;
							if (num >= width)
							{
								num = 0;
								num2++;
							}
							flag = true;
							break;
						}
					}
				}
				if (!flag)
				{
					return false;
				}
			}
			return plan.Count == snapshots.Count;
		}

		private static int CountPlannedMoves(Dictionary<ItemData, Vector2i> plan)
		{
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			if (plan == null)
			{
				return 0;
			}
			int num = 0;
			foreach (KeyValuePair<ItemData, Vector2i> item in plan)
			{
				ItemData key = item.Key;
				if (key != null)
				{
					Vector2i value = item.Value;
					if (key.m_gridPos.x != value.x || key.m_gridPos.y != value.y)
					{
						num++;
					}
				}
			}
			return num;
		}

		private static void ApplyPlan(Dictionary<ItemData, Vector2i> plan)
		{
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			foreach (KeyValuePair<ItemData, Vector2i> item in plan)
			{
				if (item.Key != null)
				{
					item.Key.m_gridPos = item.Value;
				}
			}
		}

		private static void RestoreSnapshots(List<ItemSnapshot> snapshots)
		{
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			if (snapshots == null)
			{
				return;
			}
			foreach (ItemSnapshot snapshot in snapshots)
			{
				if (snapshot != null && snapshot.Item != null)
				{
					snapshot.Item.m_gridPos = snapshot.Position;
				}
			}
		}

		private bool ValidateSnapshots(List<ItemSnapshot> snapshots, int width, int height)
		{
			if (snapshots == null || inventory == null)
			{
				return false;
			}
			List<ItemData> allItems = inventory.GetAllItems();
			if (allItems == null)
			{
				return snapshots.Count == 0;
			}
			int num = 0;
			HashSet<ItemData> hashSet = new HashSet<ItemData>();
			HashSet<int> hashSet2 = new HashSet<int>();
			foreach (ItemData item3 in allItems)
			{
				if (item3 != null)
				{
					num++;
					hashSet.Add(item3);
					int x = item3.m_gridPos.x;
					int y = item3.m_gridPos.y;
					if (x < 0 || y < 0 || x >= width || y >= height)
					{
						return false;
					}
					int item = y * width + x;
					if (!hashSet2.Add(item))
					{
						return false;
					}
				}
			}
			if (num != snapshots.Count)
			{
				return false;
			}
			foreach (ItemSnapshot snapshot in snapshots)
			{
				ItemData item2 = snapshot.Item;
				if (item2 == null || !hashSet.Contains(item2))
				{
					return false;
				}
				if (!string.Equals(Object.op_Implicit((Object)(object)item2.m_dropPrefab) ? ((Object)item2.m_dropPrefab).name : string.Empty, snapshot.PrefabName, StringComparison.Ordinal) || item2.m_stack != snapshot.Stack || item2.m_quality != snapshot.Quality || Math.Abs(item2.m_durability - snapshot.Durability) > 0.0001f || item2.m_variant != snapshot.Variant || item2.m_crafterID != snapshot.CrafterId || !string.Equals(item2.m_crafterName ?? string.Empty, snapshot.CrafterName, StringComparison.Ordinal))
				{
					return false;
				}
			}
			return true;
		}

		private static int SmallestSupportedCapacityForStacks(int stacks)
		{
			if (stacks <= 10)
			{
				return 10;
			}
			if (stacks <= 20)
			{
				return 20;
			}
			if (stacks <= 40)
			{
				return 40;
			}
			if (stacks <= 60)
			{
				return 60;
			}
			if (stacks <= 80)
			{
				return 80;
			}
			return int.MaxValue;
		}

		private void CacheReferences()
		{
			if (!Object.op_Implicit((Object)(object)container))
			{
				container = ((Component)this).GetComponent<Container>();
			}
			if (!Object.op_Implicit((Object)(object)nview))
			{
				nview = ((Component)this).GetComponent<ZNetView>();
			}
			if (Object.op_Implicit((Object)(object)container))
			{
				Inventory val = container.GetInventory();
				if (val != null)
				{
					inventory = val;
				}
			}
		}

		private void RefreshVisualFill()
		{
			MeatRackStorage component = ((Component)this).GetComponent<MeatRackStorage>();
			MeatRackVisual component2 = ((Component)this).GetComponent<MeatRackVisual>();
			if (Object.op_Implicit((Object)(object)component2))
			{
				component2.Refresh(component);
			}
		}

		private int RefreshHiddenStackObservation()
		{
			int num = ((inventory != null) ? ReadDimension(InventoryWidth, inventory, Object.op_Implicit((Object)(object)container) ? container.m_width : 0) : 0);
			int liveHeight = ((inventory != null) ? ReadDimension(InventoryHeight, inventory, Object.op_Implicit((Object)(object)container) ? container.m_height : 0) : 0);
			int num2 = CountInaccess