Decompiled source of ItemShelvesPlatforms v1.0.2

ItemRackMod.dll

Decompiled 12 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Photon.Pun;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.SceneManagement;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = "")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.0.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace ItemRackMod
{
	internal static class DefaultItemNameResolver
	{
		private static readonly string[] PreferredMemberNames = new string[8] { "itemName", "displayName", "itemDisplayName", "display_name", "name", "Name", "item_name", "display_name_text" };

		private static readonly Dictionary<Type, MemberInfo[]> CachedMembers = new Dictionary<Type, MemberInfo[]>();

		private static readonly Dictionary<string, string> CanonicalItemAliases = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
		{
			{ "explosive mine", "ItemMineExplosive" },
			{ "sledge hammer", "ItemMelee" },
			{ "hammer", "ItemMelee" },
			{ "inflatable hammer", "ItemMeleeInflatableHammer" },
			{ "shockwave mine", "ItemMineStun" },
			{ "duct taped granade", "ItemGrenadeDuctTaped" },
			{ "trapzap", "ItemMineTrigger" },
			{ "human granade", "ItemGrenadeHuman" },
			{ "granade", "ItemGrenadeExplosive" },
			{ "stun granade", "ItemGrenadeStun" },
			{ "shockwave granade", "ItemGrenadeShockwave" },
			{ "recharge drone", "ItemDroneBattery" },
			{ "feather drone", "ItemDroneFeather" },
			{ "roll drone", "ItemDroneTorque" },
			{ "zero gravity drone", "ItemDroneZeroGravity" },
			{ "indestructible drone", "ItemDroneIndestructible" },
			{ "zero gravity orb", "ItemOrbZeroGravity" }
		};

		public static bool IsCanonicalPlatformName(string configuredName)
		{
			return !string.IsNullOrWhiteSpace(configuredName) && CanonicalItemAliases.ContainsKey(Normalize(configuredName));
		}

		public static bool MatchesConfiguredName(string configuredName, PhysGrabObject pgo)
		{
			if ((Object)(object)pgo == (Object)null || string.IsNullOrWhiteSpace(configuredName))
			{
				return false;
			}
			string text = Normalize(configuredName);
			if (string.IsNullOrEmpty(text))
			{
				return false;
			}
			if (CanonicalItemAliases.TryGetValue(text, out var value))
			{
				Component[] components = ((Component)pgo).GetComponents<Component>();
				foreach (Component val in components)
				{
					if ((Object)(object)val != (Object)null && string.Equals(((object)val).GetType().Name, value, StringComparison.OrdinalIgnoreCase))
					{
						return true;
					}
				}
				return false;
			}
			foreach (Component relevantComponent in GetRelevantComponents(pgo))
			{
				if (!((Object)(object)relevantComponent == (Object)null))
				{
					if (string.Equals(Normalize(((object)relevantComponent).GetType().Name), text, StringComparison.OrdinalIgnoreCase))
					{
						return true;
					}
					if (TryReadNamedString(relevantComponent, out var value2) && string.Equals(Normalize(value2), text, StringComparison.OrdinalIgnoreCase))
					{
						return true;
					}
				}
			}
			return (Object)(object)((Component)pgo).gameObject != (Object)null && string.Equals(Normalize(((Object)((Component)pgo).gameObject).name), text, StringComparison.OrdinalIgnoreCase);
		}

		public static bool TryGetItemName(PhysGrabObject pgo, out string name)
		{
			name = null;
			if ((Object)(object)pgo == (Object)null)
			{
				return false;
			}
			foreach (Component relevantComponent in GetRelevantComponents(pgo))
			{
				if (TryReadNamedString(relevantComponent, out name) && !string.IsNullOrWhiteSpace(name))
				{
					return true;
				}
			}
			name = (((Object)(object)((Component)pgo).gameObject != (Object)null) ? ((Object)((Component)pgo).gameObject).name : null);
			return !string.IsNullOrWhiteSpace(name);
		}

		public static IEnumerable<string> GetItemAliases(PhysGrabObject pgo)
		{
			if ((Object)(object)pgo == (Object)null)
			{
				yield break;
			}
			HashSet<string> yielded = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
			foreach (Component component in GetRelevantComponents(pgo))
			{
				if (!((Object)(object)component == (Object)null))
				{
					string typeName = ((object)component).GetType().Name;
					if (!string.IsNullOrWhiteSpace(typeName) && yielded.Add(typeName))
					{
						yield return typeName;
					}
					if (TryReadNamedString(component, out var value) && !string.IsNullOrWhiteSpace(value) && yielded.Add(value))
					{
						yield return value;
					}
					value = null;
				}
			}
			if ((Object)(object)((Component)pgo).gameObject != (Object)null && yielded.Add(((Object)((Component)pgo).gameObject).name))
			{
				yield return ((Object)((Component)pgo).gameObject).name;
			}
		}

		public static string Normalize(string value)
		{
			if (string.IsNullOrWhiteSpace(value))
			{
				return string.Empty;
			}
			string text = value.Trim();
			int num = text.IndexOf("(Clone)", StringComparison.OrdinalIgnoreCase);
			if (num >= 0)
			{
				text = text.Substring(0, num);
			}
			text = text.Replace("_", " ").Replace("-", " ");
			while (text.Contains("  "))
			{
				text = text.Replace("  ", " ");
			}
			return text.Trim().ToLowerInvariant();
		}

		private static IEnumerable<Component> GetRelevantComponents(PhysGrabObject pgo)
		{
			if ((Object)(object)pgo == (Object)null)
			{
				yield break;
			}
			Component[] componentsInChildren = ((Component)pgo).GetComponentsInChildren<Component>(true);
			foreach (Component component in componentsInChildren)
			{
				if (!((Object)(object)component == (Object)null))
				{
					string typeName = ((object)component).GetType().Name;
					if (typeName.Equals("Item", StringComparison.OrdinalIgnoreCase) || typeName.Equals("ItemAttributes", StringComparison.OrdinalIgnoreCase) || typeName.Equals("ItemMelee", StringComparison.OrdinalIgnoreCase) || typeName.StartsWith("ItemMelee", StringComparison.OrdinalIgnoreCase) || typeName.StartsWith("ItemDrone", StringComparison.OrdinalIgnoreCase) || typeName.StartsWith("ItemGrenade", StringComparison.OrdinalIgnoreCase) || typeName.StartsWith("ItemMine", StringComparison.OrdinalIgnoreCase) || typeName.StartsWith("ItemOrb", StringComparison.OrdinalIgnoreCase))
					{
						yield return component;
					}
				}
			}
			Transform current = ((Component)pgo).transform.parent;
			while ((Object)(object)current != (Object)null)
			{
				Component[] components = ((Component)current).GetComponents<Component>();
				foreach (Component component2 in components)
				{
					if (!((Object)(object)component2 == (Object)null))
					{
						string typeName2 = ((object)component2).GetType().Name;
						if (typeName2.Equals("Item", StringComparison.OrdinalIgnoreCase) || typeName2.Equals("ItemAttributes", StringComparison.OrdinalIgnoreCase) || typeName2.StartsWith("ItemDrone", StringComparison.OrdinalIgnoreCase) || typeName2.StartsWith("ItemGrenade", StringComparison.OrdinalIgnoreCase) || typeName2.StartsWith("ItemMine", StringComparison.OrdinalIgnoreCase) || typeName2.StartsWith("ItemOrb", StringComparison.OrdinalIgnoreCase))
						{
							yield return component2;
						}
					}
				}
				current = current.parent;
			}
		}

		private static bool TryReadNamedString(object target, out string value)
		{
			value = null;
			if (target == null)
			{
				return false;
			}
			Type type = target.GetType();
			MemberInfo[] members = GetMembers(type);
			string[] preferredMemberNames = PreferredMemberNames;
			foreach (string value2 in preferredMemberNames)
			{
				MemberInfo[] array = members;
				foreach (MemberInfo memberInfo in array)
				{
					if (memberInfo.Name.Equals(value2, StringComparison.OrdinalIgnoreCase) && TryGetString(target, memberInfo, out value) && !string.IsNullOrWhiteSpace(value))
					{
						return true;
					}
				}
			}
			MemberInfo[] array2 = members;
			foreach (MemberInfo memberInfo2 in array2)
			{
				if ((memberInfo2.Name.IndexOf("name", StringComparison.OrdinalIgnoreCase) >= 0 || memberInfo2.Name.IndexOf("display", StringComparison.OrdinalIgnoreCase) >= 0) && TryGetString(target, memberInfo2, out value) && !string.IsNullOrWhiteSpace(value))
				{
					return true;
				}
			}
			return false;
		}

		private static MemberInfo[] GetMembers(Type type)
		{
			if (CachedMembers.TryGetValue(type, out var value))
			{
				return value;
			}
			List<MemberInfo> list = new List<MemberInfo>();
			list.AddRange(type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic));
			list.AddRange(type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic));
			value = list.ToArray();
			CachedMembers[type] = value;
			return value;
		}

		private static bool TryGetString(object target, MemberInfo member, out string value)
		{
			value = null;
			try
			{
				if (member is FieldInfo fieldInfo && fieldInfo.FieldType == typeof(string))
				{
					value = fieldInfo.GetValue(target) as string;
					return true;
				}
				if (member is PropertyInfo propertyInfo && propertyInfo.PropertyType == typeof(string) && propertyInfo.CanRead && propertyInfo.GetIndexParameters().Length == 0)
				{
					value = propertyInfo.GetValue(target, null) as string;
					return true;
				}
			}
			catch
			{
			}
			return false;
		}
	}
	public class DefaultItemPlacement : MonoBehaviour
	{
		private ItemRackController _controller;

		private readonly List<DefaultAssignment> _assignments = new List<DefaultAssignment>();

		private readonly HashSet<int> _loggedMissingSlots = new HashSet<int>();

		private readonly HashSet<PhysGrabObject> _placedDefaultObjects = new HashSet<PhysGrabObject>();

		private readonly HashSet<int> _inventoryObjectIds = new HashSet<int>();

		private float _scanTimer;

		private bool _loggedInitialScan;

		private bool _placementComplete;

		private float _startupUntil;

		private const float FastScanInterval = 0.25f;

		private const float FastScanSeconds = 5f;

		private const float StartupPlacementSeconds = 5f;

		public void Init(ItemRackController controller)
		{
			_controller = controller;
			_assignments.Clear();
			_loggedMissingSlots.Clear();
			_placedDefaultObjects.Clear();
			_inventoryObjectIds.Clear();
			_scanTimer = 0f;
			_loggedInitialScan = false;
			_placementComplete = false;
			_startupUntil = Time.time + 5f;
			foreach (DefaultAssignment item in ModConfig.ParseSlotAssignments())
			{
				_assignments.Add(item);
			}
			Plugin.Log.LogInfo((object)$"Default item placement initialized with {_assignments.Count} preferred item slot(s).");
			ScanNow();
		}

		private void Update()
		{
			if (!_placementComplete && !(Time.time >= _startupUntil))
			{
				_scanTimer -= Time.deltaTime;
				if (!(_scanTimer > 0f))
				{
					_scanTimer = 0.25f;
					ScanNow();
				}
			}
		}

		private void ScanNow()
		{
			if ((Object)(object)_controller == (Object)null || _assignments.Count == 0 || !SemiFunc.IsMasterClientOrSingleplayer())
			{
				return;
			}
			CaptureInventoryItems();
			_placedDefaultObjects.RemoveWhere((PhysGrabObject pgo) => (Object)(object)pgo == (Object)null || pgo.dead);
			PhysGrabObject[] array = Object.FindObjectsOfType<PhysGrabObject>();
			if (array == null || array.Length == 0)
			{
				return;
			}
			List<PhysGrabObject> list = new List<PhysGrabObject>();
			PhysGrabObject[] array2 = array;
			foreach (PhysGrabObject val in array2)
			{
				if (!((Object)(object)val == (Object)null) && !val.dead && !val.grabbed && !_placedDefaultObjects.Contains(val) && !_inventoryObjectIds.Contains(((Object)val).GetInstanceID()) && !((Object)(object)((Component)val).GetComponent<PhysGrabCart>() != (Object)null))
				{
					list.Add(val);
				}
			}
			ProcessPlatformGroup(list, "MinePlatform");
			ProcessPlatformGroup(list, "DronePlatform");
			ProcessPlatformGroup(list, "GrenadePlatform");
			foreach (DefaultAssignment assignment in _assignments)
			{
				if (!IsPlatformAssignment(assignment.SlotKey))
				{
					TryPlaceAssignment(assignment, list);
				}
			}
			if (AreAllAssignmentsSatisfied())
			{
				_placementComplete = true;
				Plugin.Log.LogInfo((object)"Automatic rack/platform placement completed; scene-wide startup scans stopped.");
			}
			if (!_loggedInitialScan)
			{
				_loggedInitialScan = true;
				Plugin.Log.LogInfo((object)"Automatic rack/platform placement is active for the startup window. Platform priority: MinePlatform -> DronePlatform -> GrenadePlatform; exact item matching is enforced and fuzzy matching is disabled.");
			}
		}

		private void ProcessPlatformGroup(List<PhysGrabObject> candidates, string platformName)
		{
			foreach (DefaultAssignment assignment in _assignments)
			{
				if (IsAssignmentOnPlatform(assignment, platformName))
				{
					TryPlaceAssignment(assignment, candidates);
				}
			}
		}

		private static bool IsAssignmentOnPlatform(DefaultAssignment assignment, string platformName)
		{
			if (assignment == null || string.IsNullOrEmpty(assignment.SlotKey))
			{
				return false;
			}
			return assignment.SlotKey.StartsWith(platformName + ":", StringComparison.OrdinalIgnoreCase);
		}

		private void TryPlaceAssignment(DefaultAssignment assignment, List<PhysGrabObject> candidates)
		{
			if (!_controller.TryGetSlot(assignment.SlotKey, out var slot))
			{
				int hashCode = StringComparer.OrdinalIgnoreCase.GetHashCode(assignment.SlotKey);
				if (_loggedMissingSlots.Add(hashCode))
				{
					Plugin.Log.LogWarning((object)("Default item assignment points to missing slot '" + assignment.SlotKey + "'. Check the platform dimensions and SlotAssignments."));
				}
				return;
			}
			if ((Object)(object)slot.Occupant != (Object)null)
			{
				if (DefaultItemNameResolver.MatchesConfiguredName(assignment.ItemName, slot.Occupant))
				{
					_placedDefaultObjects.Add(slot.Occupant);
					return;
				}
				slot.DetachForAutomaticMove();
			}
			PhysGrabObject val = FindExactCandidate(assignment.ItemName, candidates);
			if (!((Object)(object)val == (Object)null))
			{
				ItemRackSlot itemRackSlot = _controller.FindSlotContaining(val);
				if ((Object)(object)itemRackSlot != (Object)null && (Object)(object)itemRackSlot != (Object)(object)slot)
				{
					itemRackSlot.DetachForAutomaticMove();
				}
				if (slot.TrySnapItem(val, allowReplace: false, applyConfiguredOrientation: true, bypassStartupFilter: true))
				{
					_placedDefaultObjects.Add(val);
					Plugin.Log.LogInfo((object)("Default item '" + assignment.ItemName + "' placed in " + assignment.SlotKey + "."));
				}
			}
		}

		private bool AreAllAssignmentsSatisfied()
		{
			if (_assignments.Count == 0)
			{
				return true;
			}
			foreach (DefaultAssignment assignment in _assignments)
			{
				if (assignment != null)
				{
					if (!_controller.TryGetSlot(assignment.SlotKey, out var slot))
					{
						return false;
					}
					if ((Object)(object)slot.Occupant == (Object)null || !DefaultItemNameResolver.MatchesConfiguredName(assignment.ItemName, slot.Occupant))
					{
						return false;
					}
				}
			}
			return true;
		}

		private void CaptureInventoryItems()
		{
			try
			{
				InventorySpot[] array = Resources.FindObjectsOfTypeAll<InventorySpot>();
				if (array == null)
				{
					return;
				}
				InventorySpot[] array2 = array;
				foreach (InventorySpot val in array2)
				{
					if ((Object)(object)val == (Object)null)
					{
						continue;
					}
					PhysGrabObject[] componentsInChildren = ((Component)val).GetComponentsInChildren<PhysGrabObject>(true);
					PhysGrabObject[] array3 = componentsInChildren;
					foreach (PhysGrabObject val2 in array3)
					{
						if ((Object)(object)val2 != (Object)null)
						{
							_inventoryObjectIds.Add(((Object)val2).GetInstanceID());
						}
					}
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogDebug((object)("Could not inspect inventory items: " + ex.Message));
			}
		}

		private static bool IsPlatformAssignment(string slotKey)
		{
			return slotKey.StartsWith("DronePlatform:", StringComparison.OrdinalIgnoreCase) || slotKey.StartsWith("GrenadePlatform:", StringComparison.OrdinalIgnoreCase) || slotKey.StartsWith("MinePlatform:", StringComparison.OrdinalIgnoreCase);
		}

		private PhysGrabObject FindExactCandidate(string configuredName, List<PhysGrabObject> candidates)
		{
			foreach (PhysGrabObject candidate in candidates)
			{
				if ((Object)(object)candidate == (Object)null || candidate.dead || candidate.grabbed || _placedDefaultObjects.Contains(candidate) || !DefaultItemNameResolver.MatchesConfiguredName(configuredName, candidate))
				{
					continue;
				}
				return candidate;
			}
			return null;
		}

		public void ResetPlacementBookkeeping()
		{
			_placedDefaultObjects.Clear();
			_inventoryObjectIds.Clear();
			_loggedMissingSlots.Clear();
			_scanTimer = 0f;
			_loggedInitialScan = false;
			_placementComplete = false;
			_startupUntil = Time.time + 5f;
		}
	}
	internal sealed class DefaultAssignment
	{
		public string SlotKey;

		public string ItemName;
	}
	public class ItemDisplayPlatform : MonoBehaviour
	{
		public int Rows { get; private set; }

		public int Columns { get; private set; }

		public void Setup(int rows, int columns)
		{
			Rows = Mathf.Max(1, rows);
			Columns = Mathf.Max(1, columns);
		}
	}
	internal static class ItemOrientation
	{
		private static readonly Dictionary<string, Vector3> _rotations = new Dictionary<string, Vector3>(StringComparer.OrdinalIgnoreCase);

		private static bool _loaded;

		public static void Apply(PhysGrabObject pgo, Transform slot)
		{
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)pgo == (Object)null) && !((Object)(object)slot == (Object)null))
			{
				EnsureLoaded();
				if (DefaultItemNameResolver.TryGetItemName(pgo, out var name) && _rotations.TryGetValue(DefaultItemNameResolver.Normalize(name), out var value))
				{
					((Component)pgo).transform.localRotation = Quaternion.Euler(value);
				}
			}
		}

		private static void EnsureLoaded()
		{
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			if (_loaded)
			{
				return;
			}
			_loaded = true;
			foreach (KeyValuePair<string, Vector3> item in ModConfig.ParseItemOrientations())
			{
				_rotations[DefaultItemNameResolver.Normalize(item.Key)] = item.Value;
			}
		}
	}
	public class ItemRackController : MonoBehaviour
	{
		private readonly List<ItemRackSlot> _slots = new List<ItemRackSlot>();

		private readonly Dictionary<string, ItemRackSlot> _slotByKey = new Dictionary<string, ItemRackSlot>(StringComparer.OrdinalIgnoreCase);

		private DefaultItemPlacement _defaultPlacement;

		private bool _initialized;

		public IReadOnlyList<ItemRackSlot> Slots => _slots;

		public void Init(Transform anchor)
		{
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_0132: Unknown result type (might be due to invalid IL or missing references)
			//IL_0176: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_023c: Unknown result type (might be due to invalid IL or missing references)
			if (!_initialized)
			{
				_initialized = true;
				Plugin.Log.LogInfo((object)"ItemRackController initialization started.");
				if (ModConfig.EnableLeftWall.Value)
				{
					BuildWall("Left", anchor, new Vector3(ModConfig.LeftOffsetX.Value, ModConfig.LeftOffsetY.Value, ModConfig.LeftOffsetZ.Value), ModConfig.LeftRotationY.Value);
				}
				if (ModConfig.EnableRightWall.Value)
				{
					BuildWall("Right", anchor, new Vector3(ModConfig.RightOffsetX.Value, ModConfig.RightOffsetY.Value, ModConfig.RightOffsetZ.Value), ModConfig.RightRotationY.Value);
				}
				if (ModConfig.EnableGrenadePlatform.Value)
				{
					BuildItemPlatform(anchor, "GrenadePlatform", 3, 4, new Vector3(ModConfig.GrenadePlatformOffsetX.Value, ModConfig.GrenadePlatformOffsetY.Value, ModConfig.GrenadePlatformOffsetZ.Value), ModConfig.GrenadePlatformRotationY.Value, ModConfig.GrenadePlatformSpacingX.Value, ModConfig.GrenadePlatformSpacingZ.Value, new Vector3(ModConfig.GrenadePlatformSizeX.Value, ModConfig.GrenadePlatformSizeY.Value, ModConfig.GrenadePlatformSizeZ.Value));
				}
				if (ModConfig.EnableDronePlatform.Value)
				{
					BuildItemPlatform(anchor, "DronePlatform", 2, 3, new Vector3(ModConfig.DronePlatformOffsetX.Value, ModConfig.DronePlatformOffsetY.Value, ModConfig.DronePlatformOffsetZ.Value), ModConfig.DronePlatformRotationY.Value, ModConfig.DronePlatformSpacingX.Value, ModConfig.DronePlatformSpacingZ.Value, new Vector3(ModConfig.DronePlatformSizeX.Value, ModConfig.DronePlatformSizeY.Value, ModConfig.DronePlatformSizeZ.Value));
				}
				if (ModConfig.EnableMinePlatform.Value)
				{
					BuildItemPlatform(anchor, "MinePlatform", 2, 6, new Vector3(ModConfig.MinePlatformOffsetX.Value, ModConfig.MinePlatformOffsetY.Value, ModConfig.MinePlatformOffsetZ.Value), ModConfig.MinePlatformRotationY.Value, ModConfig.MinePlatformSpacingX.Value, ModConfig.MinePlatformSpacingZ.Value, new Vector3(ModConfig.MinePlatformSizeX.Value, ModConfig.MinePlatformSizeY.Value, ModConfig.MinePlatformSizeZ.Value));
				}
				_defaultPlacement = ((Component)this).gameObject.GetComponent<DefaultItemPlacement>();
				if ((Object)(object)_defaultPlacement == (Object)null)
				{
					_defaultPlacement = ((Component)this).gameObject.AddComponent<DefaultItemPlacement>();
				}
				_defaultPlacement.Init(this);
				Plugin.Log.LogInfo((object)$"Built {_slots.Count} rack/platform slot(s) total inside the truck.");
				Plugin.Log.LogInfo((object)"Horizontal item platforms initialized. Platform cells are non-solid and use the same snap/hold system as rack shelves.");
			}
		}

		private void BuildWall(string wallName, Transform anchor, Vector3 localOffset, float extraRotationY)
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Expected O, but got Unknown
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject("ItemRackWall_" + wallName);
			val.transform.SetParent(anchor, false);
			val.transform.localPosition = localOffset;
			val.transform.localRotation = Quaternion.Euler(0f, extraRotationY, 0f);
			int num = Mathf.Max(1, ModConfig.Rows.Value);
			int num2 = Mathf.Max(1, ModConfig.Columns.Value);
			float value = ModConfig.SpacingHorizontal.Value;
			float value2 = ModConfig.SpacingVertical.Value;
			float num3 = (float)(num2 - 1) * value;
			float num4 = (float)(num - 1) * value2;
			Vector3 localPosition = default(Vector3);
			for (int i = 0; i < num; i++)
			{
				for (int j = 0; j < num2; j++)
				{
					int index = i * num2 + j + 1;
					((Vector3)(ref localPosition))..ctor((0f - num3) / 2f + (float)j * value, (0f - num4) / 2f + (float)i * value2, 0f);
					BuildSlot(val.transform, wallName, i + 1, j + 1, index, localPosition, platform: false, Vector3.zero);
				}
			}
			Plugin.Log.LogInfo((object)$"Built {wallName} wall: {num} row(s) x {num2} column(s).");
		}

		private void BuildSlot(Transform parent, string wallName, int row, int column, int index, Vector3 localPosition, bool platform, Vector3 cellSize)
		{
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Expected O, but got Unknown
			//IL_0037: 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_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_011a: Unknown result type (might be due to invalid IL or missing references)
			string text = ModConfig.MakeSlotKey(wallName, row, column);
			GameObject val = new GameObject($"ItemRackSlot_{wallName}_{index}");
			val.transform.SetParent(parent, false);
			val.transform.localPosition = localPosition;
			val.transform.localRotation = Quaternion.identity;
			SphereCollider val2 = val.AddComponent<SphereCollider>();
			((Collider)val2).isTrigger = true;
			val2.radius = (platform ? Mathf.Max(ModConfig.SlotRadius.Value, Mathf.Max(cellSize.x, Mathf.Max(cellSize.y, cellSize.z)) * 0.5f) : ModConfig.SlotRadius.Value);
			ItemRackSlot itemRackSlot = val.AddComponent<ItemRackSlot>();
			itemRackSlot.Setup(this, wallName, row, column, text);
			string configuredItemName = GetConfiguredItemName(text);
			if (!string.IsNullOrWhiteSpace(configuredItemName))
			{
				itemRackSlot.SetInitialItemBinding(configuredItemName);
			}
			_slots.Add(itemRackSlot);
			_slotByKey[text] = itemRackSlot;
			if (ModConfig.ShowVisualMarkers.Value)
			{
				if (platform)
				{
					CreatePlatformMarker(val.transform, cellSize);
				}
				else
				{
					CreateVisualMarker(val.transform);
				}
			}
		}

		private void BuildItemPlatform(Transform anchor, string platformName, int rows, int columns, Vector3 localOffset, float rotationY, float spacingX, float spacingZ, Vector3 cellSize)
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Expected O, but got Unknown
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c8: Expected O, but got Unknown
			//IL_00e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_011b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0122: Unknown result type (might be due to invalid IL or missing references)
			//IL_0129: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c0: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject("ItemRack_" + platformName);
			val.transform.SetParent(anchor, false);
			val.transform.localPosition = localOffset;
			val.transform.localRotation = Quaternion.Euler(0f, rotationY, 0f);
			float num = (float)(columns - 1) * spacingX;
			float num2 = (float)(rows - 1) * spacingZ;
			Vector3 localPosition = default(Vector3);
			for (int i = 0; i < rows; i++)
			{
				for (int j = 0; j < columns; j++)
				{
					((Vector3)(ref localPosition))..ctor((0f - num) / 2f + (float)j * spacingX, 0f, (0f - num2) / 2f + (float)i * spacingZ);
					string text = ModConfig.MakePlatformSlotKey(platformName, i + 1, j + 1);
					GameObject val2 = new GameObject($"{platformName}_Slot_{i + 1}_{j + 1}");
					val2.transform.SetParent(val.transform, false);
					val2.transform.localPosition = localPosition;
					val2.transform.localRotation = Quaternion.identity;
					SphereCollider val3 = val2.AddComponent<SphereCollider>();
					((Collider)val3).isTrigger = true;
					val3.radius = Mathf.Max(ModConfig.SlotRadius.Value, Mathf.Max(cellSize.x, Mathf.Max(cellSize.y, cellSize.z)) * 0.5f);
					ItemRackSlot itemRackSlot = val2.AddComponent<ItemRackSlot>();
					itemRackSlot.Setup(this, platformName, i + 1, j + 1, text);
					string configuredItemName = GetConfiguredItemName(text);
					if (!string.IsNullOrWhiteSpace(configuredItemName))
					{
						itemRackSlot.SetInitialItemBinding(configuredItemName);
					}
					_slots.Add(itemRackSlot);
					_slotByKey[text] = itemRackSlot;
					if (ModConfig.ShowVisualMarkers.Value)
					{
						CreatePlatformMarker(val2.transform, cellSize);
					}
				}
			}
			Plugin.Log.LogInfo((object)($"SUCCESS: Built {platformName} at local position {localOffset}, " + $"rotation Y={rotationY}, {rows} row(s) x {columns} column(s)."));
		}

		public bool TryGetSlot(string key, out ItemRackSlot slot)
		{
			return _slotByKey.TryGetValue(key, out slot);
		}

		public ItemRackSlot FindSlotContaining(PhysGrabObject pgo)
		{
			if ((Object)(object)pgo == (Object)null)
			{
				return null;
			}
			foreach (ItemRackSlot slot in _slots)
			{
				if ((Object)(object)slot != (Object)null && (Object)(object)slot.Occupant == (Object)(object)pgo)
				{
					return slot;
				}
			}
			return null;
		}

		private static string GetConfiguredItemName(string slotKey)
		{
			foreach (DefaultAssignment item in ModConfig.ParseSlotAssignments())
			{
				if (string.Equals(item.SlotKey, slotKey, StringComparison.OrdinalIgnoreCase))
				{
					return item.ItemName;
				}
			}
			return null;
		}

		private void CreateVisualMarker(Transform parent)
		{
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				GameObject val = GameObject.CreatePrimitive((PrimitiveType)3);
				((Object)val).name = "SlotMarker";
				Collider component = val.GetComponent<Collider>();
				if ((Object)(object)component != (Object)null)
				{
					Object.Destroy((Object)(object)component);
				}
				val.transform.SetParent(parent, false);
				val.transform.localPosition = Vector3.zero;
				float num = ModConfig.SlotRadius.Value * 1.6f;
				val.transform.localScale = new Vector3(num, num, 0.015f);
				ApplyMarkerMaterial(val);
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Could not create slot visual marker: " + ex.Message));
			}
		}

		private void CreatePlatformMarker(Transform parent, Vector3 cellSize)
		{
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				GameObject val = GameObject.CreatePrimitive((PrimitiveType)3);
				((Object)val).name = "PlatformSlotMarker";
				Collider component = val.GetComponent<Collider>();
				if ((Object)(object)component != (Object)null)
				{
					Object.Destroy((Object)(object)component);
				}
				val.transform.SetParent(parent, false);
				val.transform.localPosition = Vector3.zero;
				val.transform.localScale = cellSize;
				ApplyMarkerMaterial(val);
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Could not create platform visual marker: " + ex.Message));
			}
		}

		private static void ApplyMarkerMaterial(GameObject marker)
		{
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			Renderer component = marker.GetComponent<Renderer>();
			if (!((Object)(object)component == (Object)null))
			{
				component.shadowCastingMode = (ShadowCastingMode)0;
				component.receiveShadows = false;
				if ((Object)(object)component.material != (Object)null)
				{
					component.material.color = new Color(0.25f, 0.9f, 1f, 0.55f);
				}
			}
		}

		public void ResetForNewScene()
		{
			foreach (ItemRackSlot slot in _slots)
			{
				if ((Object)(object)slot != (Object)null)
				{
					slot.ResetForNewScene();
				}
			}
			if ((Object)(object)_defaultPlacement != (Object)null)
			{
				_defaultPlacement.ResetPlacementBookkeeping();
			}
			Plugin.Log.LogInfo((object)"ItemRackMod reset startup platform bindings for a new scene.");
		}

		public void OnSlotFreed(ItemRackSlot slot)
		{
		}
	}
	public class ItemRackSlot : MonoBehaviour
	{
		private ItemRackController _controller;

		private string _configuredInitialItemName;

		private float _initialBindingReleaseAt = -1f;

		private const float InitialBindingSeconds = 5f;

		private float _kinematicRefreshTimer;

		private const float KinematicRefreshInterval = 0.25f;

		private const float KinematicHoldSeconds = 0.75f;

		public string WallName { get; private set; }

		public int Row { get; private set; }

		public int Column { get; private set; }

		public string SlotKey { get; private set; }

		public PhysGrabObject Occupant { get; private set; }

		public string InitialItemName { get; private set; }

		public bool IsPlatformSlot { get; private set; }

		public bool InitialBindingActive { get; private set; }

		public void Setup(ItemRackController controller, string wallName, int row, int column, string slotKey)
		{
			_controller = controller;
			WallName = wallName;
			Row = row;
			Column = column;
			SlotKey = slotKey;
			IsPlatformSlot = IsPlatformName(wallName);
		}

		public void SetInitialItemBinding(string itemName)
		{
			if (!string.IsNullOrWhiteSpace(itemName))
			{
				_configuredInitialItemName = itemName.Trim();
				InitialItemName = _configuredInitialItemName;
				InitialBindingActive = true;
				_initialBindingReleaseAt = Time.time + 5f;
				Plugin.Log.LogInfo((object)$"Startup item filter armed for {SlotKey}: '{InitialItemName}' for {5f:0.#} seconds.");
			}
		}

		public void ResetForNewScene()
		{
			if ((Object)(object)Occupant != (Object)null && (Object)(object)((Component)Occupant).transform.parent == (Object)(object)((Component)this).transform)
			{
				((Component)Occupant).transform.SetParent((Transform)null, true);
			}
			Occupant = null;
			_kinematicRefreshTimer = 0f;
			if (!string.IsNullOrWhiteSpace(_configuredInitialItemName))
			{
				InitialItemName = _configuredInitialItemName;
				InitialBindingActive = true;
				_initialBindingReleaseAt = Time.time + 5f;
			}
		}

		public bool AcceptsItemForInitialBinding(PhysGrabObject pgo)
		{
			if (!InitialBindingActive)
			{
				return true;
			}
			return !string.IsNullOrWhiteSpace(InitialItemName) && DefaultItemNameResolver.MatchesConfiguredName(InitialItemName, pgo);
		}

		private static bool IsPlatformName(string name)
		{
			return string.Equals(name, "DronePlatform", StringComparison.OrdinalIgnoreCase) || string.Equals(name, "GrenadePlatform", StringComparison.OrdinalIgnoreCase) || string.Equals(name, "MinePlatform", StringComparison.OrdinalIgnoreCase);
		}

		private void Update()
		{
			if (InitialBindingActive && _initialBindingReleaseAt > 0f && Time.time >= _initialBindingReleaseAt)
			{
				InitialBindingActive = false;
				InitialItemName = null;
				_initialBindingReleaseAt = -1f;
				Plugin.Log.LogInfo((object)("Initial item filter expired for " + SlotKey + "; slot is now universal."));
			}
			if ((Object)(object)Occupant == (Object)null)
			{
				return;
			}
			if (Occupant.dead || Occupant.grabbed)
			{
				ReleaseOccupant();
			}
			else if (SemiFunc.IsMasterClientOrSingleplayer())
			{
				_kinematicRefreshTimer -= Time.deltaTime;
				if (!(_kinematicRefreshTimer > 0f))
				{
					_kinematicRefreshTimer = 0.25f;
					MaintainOccupant();
				}
			}
		}

		private void OnTriggerStay(Collider other)
		{
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)Occupant != (Object)null || !SemiFunc.IsMasterClientOrSingleplayer())
			{
				return;
			}
			PhysGrabObject val = FindPhysGrabObject(other);
			if ((Object)(object)val == (Object)null || val.dead || val.grabbed || (Object)(object)((Component)val).GetComponent<PhysGrabCart>() != (Object)null || (IsPlatformSlot && InitialBindingActive) || !AcceptsItemForInitialBinding(val))
			{
				return;
			}
			if ((Object)(object)val.rb != (Object)null)
			{
				float value = ModConfig.MaxSnapSpeed.Value;
				Vector3 velocity = val.rb.velocity;
				if (((Vector3)(ref velocity)).sqrMagnitude > value * value)
				{
					return;
				}
			}
			TrySnapItem(val);
		}

		private static PhysGrabObject FindPhysGrabObject(Collider other)
		{
			if ((Object)(object)other == (Object)null)
			{
				return null;
			}
			PhysGrabObject component = ((Component)other).GetComponent<PhysGrabObject>();
			if ((Object)(object)component != (Object)null)
			{
				return component;
			}
			component = ((Component)other).GetComponentInParent<PhysGrabObject>();
			if ((Object)(object)component != (Object)null)
			{
				return component;
			}
			if ((Object)(object)other.attachedRigidbody != (Object)null)
			{
				component = ((Component)other.attachedRigidbody).GetComponent<PhysGrabObject>();
				if ((Object)(object)component != (Object)null)
				{
					return component;
				}
				component = ((Component)other.attachedRigidbody).GetComponentInParent<PhysGrabObject>();
			}
			return component;
		}

		public bool TrySnapItem(PhysGrabObject pgo, bool allowReplace = false, bool applyConfiguredOrientation = true, bool bypassStartupFilter = false)
		{
			if ((Object)(object)pgo == (Object)null || pgo.dead || pgo.grabbed)
			{
				return false;
			}
			if ((Object)(object)((Component)pgo).GetComponent<PhysGrabCart>() != (Object)null)
			{
				return false;
			}
			if (!bypassStartupFilter && !AcceptsItemForInitialBinding(pgo))
			{
				return false;
			}
			if ((Object)(object)Occupant != (Object)null)
			{
				if (!allowReplace)
				{
					return false;
				}
				ReleaseOccupant();
				if ((Object)(object)Occupant != (Object)null)
				{
					return false;
				}
			}
			PhotonView component = ((Component)pgo).GetComponent<PhotonView>();
			if ((Object)(object)component != (Object)null && !component.IsMine)
			{
				component.RequestOwnership();
			}
			Occupant = pgo;
			_kinematicRefreshTimer = 0f;
			((Component)pgo).transform.SetParent(((Component)this).transform, false);
			MaintainOccupant(applyConfiguredOrientation);
			return (Object)(object)Occupant == (Object)(object)pgo;
		}

		public void DetachForAutomaticMove()
		{
			if (!((Object)(object)Occupant == (Object)null))
			{
				if ((Object)(object)((Component)Occupant).transform.parent == (Object)(object)((Component)this).transform)
				{
					((Component)Occupant).transform.SetParent((Transform)null, true);
				}
				Occupant = null;
				_kinematicRefreshTimer = 0f;
				_controller?.OnSlotFreed(this);
			}
		}

		private void MaintainOccupant(bool applyConfiguredOrientation = true)
		{
			//IL_003f: 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_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)Occupant == (Object)null)
			{
				return;
			}
			if (Occupant.dead || Occupant.grabbed)
			{
				ReleaseOccupant();
				return;
			}
			Quaternion val = Quaternion.identity;
			if (applyConfiguredOrientation)
			{
				val = GetDesiredLocalRotation();
			}
			Quaternion val2 = ((Component)this).transform.rotation * val;
			Occupant.OverrideKinematic(0.75f);
			((Component)Occupant).transform.localPosition = Vector3.zero;
			((Component)Occupant).transform.localRotation = val;
			Occupant.Teleport(((Component)this).transform.position, val2);
		}

		private Quaternion GetDesiredLocalRotation()
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: 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_0046: 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 ((Object)(object)Occupant == (Object)null)
			{
				return Quaternion.identity;
			}
			if (!DefaultItemNameResolver.TryGetItemName(Occupant, out var name))
			{
				return Quaternion.identity;
			}
			if (ModConfig.TryGetItemOrientation(name, out var euler))
			{
				return Quaternion.Euler(euler);
			}
			return Quaternion.identity;
		}

		private void ReleaseOccupant()
		{
			if ((Object)(object)Occupant != (Object)null && (Object)(object)((Component)Occupant).transform.parent == (Object)(object)((Component)this).transform)
			{
				((Component)Occupant).transform.SetParent((Transform)null, true);
			}
			Occupant = null;
			_kinematicRefreshTimer = 0f;
			_controller?.OnSlotFreed(this);
		}
	}
	internal static class ModConfig
	{
		public static ConfigEntry<int> Rows;

		public static ConfigEntry<int> Columns;

		public static ConfigEntry<float> SpacingHorizontal;

		public static ConfigEntry<float> SpacingVertical;

		public static ConfigEntry<bool> EnableLeftWall;

		public static ConfigEntry<bool> EnableRightWall;

		public static ConfigEntry<float> SlotRadius;

		public static ConfigEntry<float> MaxSnapSpeed;

		public static ConfigEntry<bool> ShowVisualMarkers;

		public static ConfigEntry<string> SlotAssignments;

		public static ConfigEntry<string> ItemOrientations;

		private static readonly Dictionary<string, Vector3> CachedItemOrientations = new Dictionary<string, Vector3>(StringComparer.OrdinalIgnoreCase);

		public static ConfigEntry<float> LeftOffsetX;

		public static ConfigEntry<float> LeftOffsetY;

		public static ConfigEntry<float> LeftOffsetZ;

		public static ConfigEntry<float> LeftRotationY;

		public static ConfigEntry<float> RightOffsetX;

		public static ConfigEntry<float> RightOffsetY;

		public static ConfigEntry<float> RightOffsetZ;

		public static ConfigEntry<float> RightRotationY;

		public static ConfigEntry<bool> EnableGrenadePlatform;

		public static ConfigEntry<float> GrenadePlatformOffsetX;

		public static ConfigEntry<float> GrenadePlatformOffsetY;

		public static ConfigEntry<float> GrenadePlatformOffsetZ;

		public static ConfigEntry<float> GrenadePlatformRotationY;

		public static ConfigEntry<float> GrenadePlatformSpacingX;

		public static ConfigEntry<float> GrenadePlatformSpacingZ;

		public static ConfigEntry<float> GrenadePlatformSizeX;

		public static ConfigEntry<float> GrenadePlatformSizeY;

		public static ConfigEntry<float> GrenadePlatformSizeZ;

		public static ConfigEntry<bool> EnableDronePlatform;

		public static ConfigEntry<float> DronePlatformOffsetX;

		public static ConfigEntry<float> DronePlatformOffsetY;

		public static ConfigEntry<float> DronePlatformOffsetZ;

		public static ConfigEntry<float> DronePlatformRotationY;

		public static ConfigEntry<float> DronePlatformSpacingX;

		public static ConfigEntry<float> DronePlatformSpacingZ;

		public static ConfigEntry<float> DronePlatformSizeX;

		public static ConfigEntry<float> DronePlatformSizeY;

		public static ConfigEntry<float> DronePlatformSizeZ;

		public static ConfigEntry<bool> EnableMinePlatform;

		public static ConfigEntry<float> MinePlatformOffsetX;

		public static ConfigEntry<float> MinePlatformOffsetY;

		public static ConfigEntry<float> MinePlatformOffsetZ;

		public static ConfigEntry<float> MinePlatformRotationY;

		public static ConfigEntry<float> MinePlatformSpacingX;

		public static ConfigEntry<float> MinePlatformSpacingZ;

		public static ConfigEntry<float> MinePlatformSizeX;

		public static ConfigEntry<float> MinePlatformSizeY;

		public static ConfigEntry<float> MinePlatformSizeZ;

		public static void Init(ConfigFile config)
		{
			Rows = config.Bind<int>("Rack", "Rows", 2, "Rows per wall (vertical, floor-to-ceiling).");
			Columns = config.Bind<int>("Rack", "Columns", 7, "Columns per wall (along the wall).");
			SpacingHorizontal = config.Bind<float>("Rack", "SpacingHorizontal", 0.8000002f, "Distance between columns, in meters.");
			SpacingVertical = config.Bind<float>("Rack", "SpacingVertical", 1.099999f, "Distance between rows, in meters.");
			EnableLeftWall = config.Bind<bool>("Rack", "EnableLeftWall", true, "Build a rack on the left wall.");
			EnableRightWall = config.Bind<bool>("Rack", "EnableRightWall", true, "Build a rack on the right wall.");
			SlotRadius = config.Bind<float>("Rack", "SlotRadius", 0.02000006f, "Radius of each slot's detection trigger, in meters.");
			MaxSnapSpeed = config.Bind<float>("Rack", "MaxSnapSpeed", 3.5f, "Items moving faster than this (m/s) will not snap into a slot.");
			ShowVisualMarkers = config.Bind<bool>("Rack", "ShowVisualMarkers", true, "Draw the same thin cyan marker at each empty rack/platform slot.");
			SlotAssignments = config.Bind<string>("Rack.ItemAssignments", "SlotAssignments", "Right:1:1=Inflatable Hammer,Right:1:2=Baseball Bat,Right:1:3=Zero Gravity Staff,Right:1:4=Roll Staff,Right:1:5=Void Staff,Right:1:6=Rubber Duck,Right:1:7=semibot walkie,Right:2:1=Sword,Right:2:2=Frying Pan,Right:2:3=ProdZap,Right:2:4=Sledge Hammer,Right:2:7=semibot walkie,Left:1:1=C.A.R.T. Cannon,Left:1:2=Defibro,Left:1:3=Extraction Tracker,Left:1:4=Tranq Gun,Left:1:5=Shotgun,Left:1:6=Gun,Left:1:7=Pulse Pistol,Left:2:1=C.A.R.T. Laser,Left:2:2=Leaf Blower,Left:2:3=Duck Bucket,Left:2:4=Valuable Tracker,Left:2:5=Photon Blaster,Left:2:6=Boltzap,Left:2:7=Phase Bridge,DronePlatform:1:1=zero gravity drone,DronePlatform:1:2=zero gravity orb,DronePlatform:1:3=recharge drone,DronePlatform:2:1=indestructible drone,DronePlatform:2:2=roll drone,DronePlatform:2:3=feather drone,GrenadePlatform:1:1=grenade,GrenadePlatform:1:2=human grenade,GrenadePlatform:1:3=stun grenade,GrenadePlatform:1:4=shockwave mine,GrenadePlatform:2:1=grenade,GrenadePlatform:2:2=human grenade,GrenadePlatform:2:3=stun grenade,GrenadePlatform:2:4=shockwave mine,GrenadePlatform:3:1=grenade,GrenadePlatform:3:2=human grenade,GrenadePlatform:3:3=stun grenade,GrenadePlatform:3:4=shockwave mine,MinePlatform:1:1=explosive mine,MinePlatform:1:2=explosive mine,MinePlatform:1:3=explosive mine,MinePlatform:1:4=trapzap,MinePlatform:1:5=trapzap,MinePlatform:1:6=trapzap,MinePlatform:2:1=shockwave grenade,MinePlatform:2:2=shockwave grenade,MinePlatform:2:3=shockwave grenade,MinePlatform:2:4=duct taped grenade,MinePlatform:2:5=duct taped grenade,MinePlatform:2:6=duct taped grenade", "Default locations for wall racks and the three horizontal item platforms. Platform assignments use stable game component aliases.");
			EnsurePlatformAssignments();
			ItemOrientations = config.Bind<string>("Rack.ItemAssignments", "ItemOrientations", "Boltzap=90,0,0;Photon Blaster=90,0,0;Valuable Tracker=90,0,0;Leaf Blower=90,0,0;Pulse Pistol=90,0,0;Gun=90,0,0;Shotgun=90,0,0;Tranq Gun=90,0,0;Extraction Tracker=90,0,0", "Extra local Euler rotation for automatically placed rack items.");
			RebuildItemOrientationCache();
			LeftOffsetX = config.Bind<float>("Rack.LeftWall", "OffsetX", 1.399298f, "Local X offset of the left rack from TruckSafetySpawnPoint.");
			LeftOffsetY = config.Bind<float>("Rack.LeftWall", "OffsetY", 0.8999991f, "Local Y offset of the left rack.");
			LeftOffsetZ = config.Bind<float>("Rack.LeftWall", "OffsetZ", -2f, "Local Z offset of the left rack.");
			LeftRotationY = config.Bind<float>("Rack.LeftWall", "RotationY", -3.650784E-06f, "Extra Y rotation for the left rack.");
			RightOffsetX = config.Bind<float>("Rack.RightWall", "OffsetX", -1.000082f, "Local X offset of the right rack.");
			RightOffsetY = config.Bind<float>("Rack.RightWall", "OffsetY", 0.8999996f, "Local Y offset of the right rack.");
			RightOffsetZ = config.Bind<float>("Rack.RightWall", "OffsetZ", 1.999999f, "Local Z offset of the right rack.");
			RightRotationY = config.Bind<float>("Rack.RightWall", "RotationY", -3.650784E-06f, "Extra Y rotation for the right rack.");
			EnableGrenadePlatform = config.Bind<bool>("Rack.GrenadePlatform", "Enable", true, "Build the grenade platform.");
			GrenadePlatformOffsetX = config.Bind<float>("Rack.GrenadePlatform", "OffsetX", 2.149997f, "Local X offset of the grenade platform.");
			GrenadePlatformOffsetY = config.Bind<float>("Rack.GrenadePlatform", "OffsetY", -0.6999996f, "Local Y offset of the grenade platform.");
			GrenadePlatformOffsetZ = config.Bind<float>("Rack.GrenadePlatform", "OffsetZ", 1.499974f, "Local Z offset of the grenade platform.");
			GrenadePlatformRotationY = config.Bind<float>("Rack.GrenadePlatform", "RotationY", 0f, "Extra Y rotation of the grenade platform.");
			GrenadePlatformSpacingX = config.Bind<float>("Rack.GrenadePlatform", "SpacingX", 0.4f, "Horizontal distance between grenade platform cells.");
			GrenadePlatformSpacingZ = config.Bind<float>("Rack.GrenadePlatform", "SpacingZ", 0.4000001f, "Depth distance between grenade platform cells.");
			GrenadePlatformSizeX = config.Bind<float>("Rack.GrenadePlatform", "CellSizeX", 0.04999996f, "Width of each grenade platform marker.");
			GrenadePlatformSizeY = config.Bind<float>("Rack.GrenadePlatform", "CellSizeY", 0.05f, "Height of each grenade platform marker.");
			GrenadePlatformSizeZ = config.Bind<float>("Rack.GrenadePlatform", "CellSizeZ", 0.04999998f, "Depth of each grenade platform marker.");
			EnableDronePlatform = config.Bind<bool>("Rack.DronePlatform", "Enable", true, "Build the drone platform.");
			DronePlatformOffsetX = config.Bind<float>("Rack.DronePlatform", "OffsetX", 2.000009f, "Local X offset of the drone platform.");
			DronePlatformOffsetY = config.Bind<float>("Rack.DronePlatform", "OffsetY", -0.7000024f, "Local Y offset of the drone platform.");
			DronePlatformOffsetZ = config.Bind<float>("Rack.DronePlatform", "OffsetZ", -1.400007f, "Local Z offset of the drone platform.");
			DronePlatformRotationY = config.Bind<float>("Rack.DronePlatform", "RotationY", 0f, "Extra Y rotation of the drone platform.");
			DronePlatformSpacingX = config.Bind<float>("Rack.DronePlatform", "SpacingX", 0.5000001f, "Horizontal distance between drone platform cells.");
			DronePlatformSpacingZ = config.Bind<float>("Rack.DronePlatform", "SpacingZ", 0.5000006f, "Depth distance between drone platform cells.");
			DronePlatformSizeX = config.Bind<float>("Rack.DronePlatform", "CellSizeX", 0.04999997f, "Width of each drone platform marker.");
			DronePlatformSizeY = config.Bind<float>("Rack.DronePlatform", "CellSizeY", 0.04999999f, "Height of each drone platform marker.");
			DronePlatformSizeZ = config.Bind<float>("Rack.DronePlatform", "CellSizeZ", 0.04999991f, "Depth of each drone platform marker.");
			EnableMinePlatform = config.Bind<bool>("Rack.MinePlatform", "Enable", true, "Build the mine/explosive-grenade platform.");
			MinePlatformOffsetX = config.Bind<float>("Rack.MinePlatform", "OffsetX", 5.000046f, "Local X offset of the mine platform.");
			MinePlatformOffsetY = config.Bind<float>("Rack.MinePlatform", "OffsetY", -1.589999f, "Local Y offset of the mine platform.");
			MinePlatformOffsetZ = config.Bind<float>("Rack.MinePlatform", "OffsetZ", -1.600002f, "Local Z offset of the mine platform.");
			MinePlatformRotationY = config.Bind<float>("Rack.MinePlatform", "RotationY", 0f, "Extra Y rotation of the mine platform.");
			MinePlatformSpacingX = config.Bind<float>("Rack.MinePlatform", "SpacingX", 0.6499999f, "Horizontal distance between mine platform cells.");
			MinePlatformSpacingZ = config.Bind<float>("Rack.MinePlatform", "SpacingZ", 0.6499999f, "Depth distance between mine platform cells.");
			MinePlatformSizeX = config.Bind<float>("Rack.MinePlatform", "CellSizeX", 0.04999996f, "Width of each mine platform marker.");
			MinePlatformSizeY = config.Bind<float>("Rack.MinePlatform", "CellSizeY", 0.05f, "Height of each mine platform marker.");
			MinePlatformSizeZ = config.Bind<float>("Rack.MinePlatform", "CellSizeZ", 0.04999996f, "Depth of each mine platform marker.");
			try
			{
				config.Save();
				Plugin.Log.LogInfo((object)"ItemRackMod configuration initialized and saved.");
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Could not save ItemRackMod configuration: " + ex.Message));
			}
		}

		private static void EnsurePlatformAssignments()
		{
			if (SlotAssignments == null)
			{
				return;
			}
			string text = SlotAssignments.Value ?? string.Empty;
			string[] collection = new string[30]
			{
				"DronePlatform:1:1=zero gravity drone", "DronePlatform:1:2=zero gravity orb", "DronePlatform:1:3=recharge drone", "DronePlatform:2:1=indestructible drone", "DronePlatform:2:2=roll drone", "DronePlatform:2:3=feather drone", "GrenadePlatform:1:1=granade", "GrenadePlatform:1:2=human granade", "GrenadePlatform:1:3=stun granade", "GrenadePlatform:1:4=shockwave mine",
				"GrenadePlatform:2:1=granade", "GrenadePlatform:2:2=human granade", "GrenadePlatform:2:3=stun granade", "GrenadePlatform:2:4=shockwave mine", "GrenadePlatform:3:1=granade", "GrenadePlatform:3:2=human granade", "GrenadePlatform:3:3=stun granade", "GrenadePlatform:3:4=shockwave mine", "MinePlatform:1:1=explosive mine", "MinePlatform:1:2=explosive mine",
				"MinePlatform:1:3=explosive mine", "MinePlatform:1:4=shockwave granade", "MinePlatform:1:5=shockwave granade", "MinePlatform:1:6=shockwave granade", "MinePlatform:2:1=trapzap", "MinePlatform:2:2=trapzap", "MinePlatform:2:3=trapzap", "MinePlatform:2:4=duct taped granade", "MinePlatform:2:5=duct taped granade", "MinePlatform:2:6=duct taped granade"
			};
			List<string> list = new List<string>();
			string[] array = text.Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries);
			foreach (string text2 in array)
			{
				string text3 = text2.Trim();
				int num = text3.IndexOf('=');
				if (num > 0)
				{
					string text4 = text3.Substring(0, num).Trim();
					if (!text4.StartsWith("DronePlatform:", StringComparison.OrdinalIgnoreCase) && !text4.StartsWith("GrenadePlatform:", StringComparison.OrdinalIgnoreCase) && !text4.StartsWith("MinePlatform:", StringComparison.OrdinalIgnoreCase))
					{
						list.Add(text3);
					}
				}
			}
			list.AddRange(collection);
			SlotAssignments.Value = string.Join(",", list.ToArray());
			Plugin.Log.LogInfo((object)"Platform item assignments migrated to the exact item names supplied by the user.");
		}

		public static string MakeSlotKey(string wall, int row, int column)
		{
			return $"{wall}:{row}:{column}";
		}

		public static string MakePlatformSlotKey(string platformName, int row, int column)
		{
			return $"{platformName}:{row}:{column}";
		}

		public static List<DefaultAssignment> ParseSlotAssignments()
		{
			List<DefaultAssignment> list = new List<DefaultAssignment>();
			string text = SlotAssignments?.Value ?? string.Empty;
			if (string.IsNullOrWhiteSpace(text))
			{
				return list;
			}
			string[] array = text.Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries);
			string[] array2 = array;
			foreach (string text2 in array2)
			{
				string text3 = text2.Trim();
				int num = text3.IndexOf('=');
				if (num <= 0 || num >= text3.Length - 1)
				{
					continue;
				}
				string text4 = text3.Substring(0, num).Trim();
				string text5 = text3.Substring(num + 1).Trim();
				if (!string.IsNullOrWhiteSpace(text4) && !string.IsNullOrWhiteSpace(text5))
				{
					string[] array3 = text4.Split(new char[1] { ':' });
					if (array3.Length == 3 && int.TryParse(array3[1], out var result) && int.TryParse(array3[2], out var result2) && result >= 1 && result2 >= 1 && !string.IsNullOrWhiteSpace(array3[0]))
					{
						list.Add(new DefaultAssignment
						{
							SlotKey = MakeSlotKey(array3[0].Trim(), result, result2),
							ItemName = text5
						});
					}
				}
			}
			return list;
		}

		public static bool TryGetItemOrientation(string itemName, out Vector3 euler)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			euler = Vector3.zero;
			if (string.IsNullOrWhiteSpace(itemName))
			{
				return false;
			}
			return CachedItemOrientations.TryGetValue(DefaultItemNameResolver.Normalize(itemName), out euler);
		}

		private static void RebuildItemOrientationCache()
		{
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			CachedItemOrientations.Clear();
			foreach (KeyValuePair<string, Vector3> item in ParseItemOrientations())
			{
				string text = DefaultItemNameResolver.Normalize(item.Key);
				if (!string.IsNullOrEmpty(text))
				{
					CachedItemOrientations[text] = item.Value;
				}
			}
		}

		public static List<KeyValuePair<string, Vector3>> ParseItemOrientations()
		{
			//IL_0152: Unknown result type (might be due to invalid IL or missing references)
			List<KeyValuePair<string, Vector3>> list = new List<KeyValuePair<string, Vector3>>();
			string text = ItemOrientations?.Value ?? string.Empty;
			if (string.IsNullOrWhiteSpace(text))
			{
				return list;
			}
			string[] array = text.Split(new char[1] { ';' }, StringSplitOptions.RemoveEmptyEntries);
			string[] array2 = array;
			foreach (string text2 in array2)
			{
				string text3 = text2.Trim();
				int num = text3.IndexOf('=');
				if (num > 0 && num < text3.Length - 1)
				{
					string key = text3.Substring(0, num).Trim();
					string[] array3 = text3.Substring(num + 1).Trim().Split(new char[1] { ',' });
					if (array3.Length == 3 && float.TryParse(array3[0].Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out var result) && float.TryParse(array3[1].Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out var result2) && float.TryParse(array3[2].Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out var result3))
					{
						list.Add(new KeyValuePair<string, Vector3>(key, new Vector3(result, result2, result3)));
					}
				}
			}
			return list;
		}
	}
	[HarmonyPatch(typeof(TruckSafetySpawnPoint), "Awake")]
	internal static class TruckSafetySpawnPoint_Awake_Patch
	{
		private static void Postfix(TruckSafetySpawnPoint __instance)
		{
			if (!((Object)(object)__instance == (Object)null))
			{
				ItemRackController component = ((Component)__instance).GetComponent<ItemRackController>();
				if (!((Object)(object)component != (Object)null))
				{
					component = ((Component)__instance).gameObject.AddComponent<ItemRackController>();
					component.Init(((Component)__instance).transform);
					Plugin.Log.LogInfo((object)"Item racks attached inside the truck (anchored to TruckSafetySpawnPoint).");
				}
			}
		}
	}
	[BepInPlugin("Item.Shelves.Platforms", "Item Shelves & Platforms", "1.4.3")]
	public class Plugin : BaseUnityPlugin
	{
		public const string Guid = "Item.Shelves.Platforms";

		public const string Name = "Item Shelves & Platforms";

		public const string Version = "1.4.3";

		internal static ManualLogSource Log;

		private Harmony _harmony;

		private void Awake()
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			ModConfig.Init(((BaseUnityPlugin)this).Config);
			_harmony = new Harmony("Item.Shelves.Platforms");
			_harmony.PatchAll();
			SceneManager.sceneLoaded += OnSceneLoaded;
			Log.LogInfo((object)"Item Shelves & Platforms v1.4.3 loaded, patches applied.");
		}

		private void OnSceneLoaded(Scene scene, LoadSceneMode mode)
		{
			ItemRackController[] array = Object.FindObjectsOfType<ItemRackController>();
			if (array == null)
			{
				return;
			}
			ItemRackController[] array2 = array;
			foreach (ItemRackController itemRackController in array2)
			{
				if ((Object)(object)itemRackController != (Object)null)
				{
					itemRackController.ResetForNewScene();
				}
			}
		}

		private void OnDestroy()
		{
			SceneManager.sceneLoaded -= OnSceneLoaded;
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}
	}
}