Decompiled source of LockSmith v0.4.1

LockSmith.dll

Decompiled 14 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Text;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using DrakeModsLibs.API;
using DrakeModsLibs.Art;
using DrakeModsLibs.Sync;
using DrakeModsLibs.UI;
using HarmonyLib;
using Jotunn.Configs;
using Jotunn.Entities;
using Jotunn.Managers;
using Jotunn.Utils;
using LockSmith.Access;
using LockSmith.UI;
using Microsoft.CodeAnalysis;
using UnityEngine;
using UnityEngine.SceneManagement;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("LockSmith")]
[assembly: AssemblyDescription("Designate chests and doors with a Locksmith key. Public/private wards, Personal/Team chests, guests.")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("DrakeMods")]
[assembly: AssemblyProduct("LockSmith")]
[assembly: AssemblyCopyright("Copyright © 2026")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("5DF97E86-BF46-46C5-894F-6B2D80EA6645")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyVersion("0.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[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 LockSmith
{
	public class ConfigurationManagerAttributes
	{
		public bool? IsAdminOnly;
	}
	public static class ContentRegistration
	{
		public const string OfficialKeyId = "masterkey";

		public const string BundleArtPrefab = "MasterKey";

		private static string? _registeredKeyPrefab;

		public static string? RegisteredKeyPrefab => _registeredKeyPrefab;

		public static IReadOnlyList<string> RegisteredKeyPrefabs
		{
			get
			{
				if (!string.IsNullOrEmpty(_registeredKeyPrefab))
				{
					return new string[1] { _registeredKeyPrefab };
				}
				return Array.Empty<string>();
			}
		}

		public static bool IsKeyPrefab(string? name)
		{
			if (string.IsNullOrEmpty(name))
			{
				return false;
			}
			if (!string.IsNullOrEmpty(_registeredKeyPrefab))
			{
				string registeredKeyPrefab = _registeredKeyPrefab;
				if (name.Equals(registeredKeyPrefab, StringComparison.OrdinalIgnoreCase) || name.StartsWith(registeredKeyPrefab + "(", StringComparison.OrdinalIgnoreCase))
				{
					return true;
				}
			}
			if (!name.Equals("masterkey", StringComparison.OrdinalIgnoreCase) && !name.StartsWith("masterkey(", StringComparison.OrdinalIgnoreCase) && !name.Equals("LockSmithKey", StringComparison.OrdinalIgnoreCase) && !name.StartsWith("LockSmithKey(", StringComparison.OrdinalIgnoreCase) && !name.Equals("MasterKey", StringComparison.OrdinalIgnoreCase))
			{
				return name.StartsWith("MasterKey(", StringComparison.OrdinalIgnoreCase);
			}
			return true;
		}

		public static void CustomizeMasterKeyArtItem(ArtItemContext item)
		{
			if (item == null)
			{
				return;
			}
			if (!item.Id.Equals("masterkey", StringComparison.OrdinalIgnoreCase))
			{
				ManualLogSource? log = LockSmith.Log;
				if (log != null)
				{
					log.LogWarning((object)("[ArtForge] Ignoring unexpected art item '" + item.Id + "'."));
				}
				return;
			}
			item.DisplayName = (string.IsNullOrWhiteSpace(LockSmithConfig.KeyName) ? "Locksmith Key" : LockSmithConfig.KeyName.Trim());
			item.Description = "$item_locksmith_key_desc";
			item.CraftingStation = (string.IsNullOrWhiteSpace(LockSmithConfig.KeyCraftingStation) ? null : LockSmithConfig.KeyCraftingStation.Trim());
			List<(string, int)> list = ParseMaterials(LockSmithConfig.KeyMaterials);
			if (list.Count > 0)
			{
				item.RequirementItem = list[0].Item1;
				item.RequirementAmount = list[0].Item2;
			}
		}

		public static void FinalizeOfficialKeyFromKeysPack()
		{
			_registeredKeyPrefab = null;
			try
			{
				PrefabManager instance = PrefabManager.Instance;
				GameObject val = ((instance != null) ? instance.GetPrefab("masterkey") : null);
				if (!Object.op_Implicit((Object)(object)val))
				{
					ManualLogSource? log = LockSmith.Log;
					if (log != null)
					{
						log.LogError((object)"Official key 'masterkey' missing after ArtItemLoader. Need Assets/Items/keys/masterkey.json + keys.bundle with artPrefab MasterKey.");
					}
					return;
				}
				Sprite icon = LoadMasterKeyIcon();
				SanitizeOfficialKey(val, icon);
				FixMasterKeyHoldAndLook(val);
				EnsureRootZNetView(val);
				StampRenameHandOffOnPrefab(val);
				_registeredKeyPrefab = "masterkey";
				ManualLogSource? log2 = LockSmith.Log;
				if (log2 != null)
				{
					log2.LogInfo((object)("Official LockSmith key ready: '" + _registeredKeyPrefab + "' (keys.bundle MasterKey, handle in hand, bone skull)."));
				}
			}
			catch (Exception arg)
			{
				ManualLogSource? log3 = LockSmith.Log;
				if (log3 != null)
				{
					log3.LogError((object)$"Failed to finalize official key from keys pack: {arg}");
				}
			}
		}

		private static void StampRenameHandOffOnPrefab(GameObject prefab)
		{
			if (Object.op_Implicit((Object)(object)prefab))
			{
				ItemDrop component = prefab.GetComponent<ItemDrop>();
				if (Object.op_Implicit((Object)(object)component) && component.m_itemData != null)
				{
					ChestAccessService.EnsureRenameHandOff(component.m_itemData);
				}
			}
		}

		private static void FixMasterKeyHoldAndLook(GameObject prefab)
		{
			if (!Object.op_Implicit((Object)(object)prefab))
			{
				return;
			}
			GameObject val = FindArtVisual(prefab);
			if (Object.op_Implicit((Object)(object)val))
			{
				StripArtWorldPhysics(val);
				ReorientHeldKey(val, handleAtMax: true);
			}
			else
			{
				ManualLogSource? log = LockSmith.Log;
				if (log != null)
				{
					log.LogWarning((object)"masterkey art visual not found; grip flip skipped.");
				}
			}
			ApplyBoneToSkullOnly(prefab);
		}

		private static void StripArtWorldPhysics(GameObject visual)
		{
			if (!Object.op_Implicit((Object)(object)visual))
			{
				return;
			}
			int num = 0;
			Rigidbody[] componentsInChildren = visual.GetComponentsInChildren<Rigidbody>(true);
			foreach (Rigidbody val in componentsInChildren)
			{
				if (Object.op_Implicit((Object)(object)val))
				{
					Object.DestroyImmediate((Object)(object)val);
					num++;
				}
			}
			Collider[] componentsInChildren2 = visual.GetComponentsInChildren<Collider>(true);
			foreach (Collider val2 in componentsInChildren2)
			{
				if (Object.op_Implicit((Object)(object)val2))
				{
					Object.DestroyImmediate((Object)(object)val2);
					num++;
				}
			}
			ParticleSystem[] componentsInChildren3 = visual.GetComponentsInChildren<ParticleSystem>(true);
			foreach (ParticleSystem val3 in componentsInChildren3)
			{
				if (Object.op_Implicit((Object)(object)val3))
				{
					Object.DestroyImmediate((Object)(object)val3);
					num++;
				}
			}
			ParticleSystemRenderer[] componentsInChildren4 = visual.GetComponentsInChildren<ParticleSystemRenderer>(true);
			foreach (ParticleSystemRenderer val4 in componentsInChildren4)
			{
				if (Object.op_Implicit((Object)(object)val4))
				{
					Object.DestroyImmediate((Object)(object)val4);
					num++;
				}
			}
			MonoBehaviour[] componentsInChildren5 = visual.GetComponentsInChildren<MonoBehaviour>(true);
			foreach (MonoBehaviour val5 in componentsInChildren5)
			{
				if (Object.op_Implicit((Object)(object)val5))
				{
					bool flag;
					switch (((object)val5).GetType().Name)
					{
					case "Floating":
					case "ZSyncTransform":
					case "ItemDrop":
						flag = true;
						break;
					default:
						flag = false;
						break;
					}
					if (flag)
					{
						Object.DestroyImmediate((Object)(object)val5);
						num++;
					}
				}
			}
			if (num > 0)
			{
				ManualLogSource? log = LockSmith.Log;
				if (log != null)
				{
					log.LogInfo((object)$"masterkey art: stripped {num} drop/VFX component(s).");
				}
			}
		}

		private static GameObject? FindArtVisual(GameObject prefab)
		{
			Transform[] componentsInChildren = prefab.GetComponentsInChildren<Transform>(true);
			foreach (Transform val in componentsInChildren)
			{
				if (Object.op_Implicit((Object)(object)val) && !((Object)(object)val == (Object)(object)prefab.transform) && ((Object)val).name.Equals("art", StringComparison.OrdinalIgnoreCase))
				{
					return ((Component)val).gameObject;
				}
			}
			componentsInChildren = prefab.GetComponentsInChildren<Transform>(true);
			foreach (Transform val2 in componentsInChildren)
			{
				if (Object.op_Implicit((Object)(object)val2) && !((Object)(object)val2 == (Object)(object)prefab.transform) && (((Object)val2).name.Equals("MasterKey", StringComparison.OrdinalIgnoreCase) || ((Object)val2).name.Equals("MasterKey", StringComparison.OrdinalIgnoreCase)))
				{
					return ((Component)val2).gameObject;
				}
			}
			return null;
		}

		private static void ReorientHeldKey(GameObject visual, bool handleAtMax)
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: 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)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00de: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e3: 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)
			if (!Object.op_Implicit((Object)(object)visual))
			{
				return;
			}
			visual.transform.localRotation = Quaternion.identity;
			visual.transform.localPosition = Vector3.zero;
			if (TryGetArtLocalBounds(visual, out var bounds))
			{
				Vector3 size = ((Bounds)(ref bounds)).size;
				int num = 0;
				if (size.y > ((Vector3)(ref size))[num])
				{
					num = 1;
				}
				if (size.z > ((Vector3)(ref size))[num])
				{
					num = 2;
				}
				visual.transform.localRotation = Quaternion.Euler(0f, 180f, 0f);
				Vector3 center = ((Bounds)(ref bounds)).center;
				int num2 = num;
				Vector3 val;
				float num3;
				if (!handleAtMax)
				{
					val = ((Bounds)(ref bounds)).min;
					num3 = ((Vector3)(ref val))[num];
				}
				else
				{
					val = ((Bounds)(ref bounds)).max;
					num3 = ((Vector3)(ref val))[num];
				}
				((Vector3)(ref center))[num2] = num3;
				Vector3 val2 = Vector3.Scale(center, visual.transform.localScale);
				visual.transform.localPosition = -(visual.transform.localRotation * val2);
				ManualLogSource? log = LockSmith.Log;
				if (log != null)
				{
					log.LogInfo((object)(string.Format("masterkey grip: handle={0} axis={1} ", handleAtMax ? "max" : "min", num) + $"yaw180 faceOut bounds={size}."));
				}
			}
		}

		private static bool TryGetArtLocalBounds(GameObject visual, out Bounds bounds)
		{
			//IL_0001: 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)
			//IL_0042: 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_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: 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_00ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00de: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0100: Unknown result type (might be due to invalid IL or missing references)
			//IL_0105: Unknown result type (might be due to invalid IL or missing references)
			//IL_010e: 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)
			//IL_0126: Unknown result type (might be due to invalid IL or missing references)
			//IL_0130: Unknown result type (might be due to invalid IL or missing references)
			//IL_0135: Unknown result type (might be due to invalid IL or missing references)
			//IL_013e: Unknown result type (might be due to invalid IL or missing references)
			//IL_014a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0156: Unknown result type (might be due to invalid IL or missing references)
			//IL_0160: Unknown result type (might be due to invalid IL or missing references)
			//IL_0165: Unknown result type (might be due to invalid IL or missing references)
			//IL_016e: Unknown result type (might be due to invalid IL or missing references)
			//IL_017a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0186: Unknown result type (might be due to invalid IL or missing references)
			//IL_0190: Unknown result type (might be due to invalid IL or missing references)
			//IL_0195: Unknown result type (might be due to invalid IL or missing references)
			//IL_019e: Unknown result type (might be due to invalid IL or missing references)
			//IL_01aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01da: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0214: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_0200: Unknown result type (might be due to invalid IL or missing references)
			//IL_0205: Unknown result type (might be due to invalid IL or missing references)
			//IL_020a: Unknown result type (might be due to invalid IL or missing references)
			bounds = default(Bounds);
			bool flag = true;
			MeshFilter[] componentsInChildren = visual.GetComponentsInChildren<MeshFilter>(true);
			foreach (MeshFilter val in componentsInChildren)
			{
				if (!Object.op_Implicit((Object)(object)val) || !Object.op_Implicit((Object)(object)val.sharedMesh))
				{
					continue;
				}
				Bounds bounds2 = val.sharedMesh.bounds;
				Vector3[] array = (Vector3[])(object)new Vector3[8]
				{
					new Vector3(((Bounds)(ref bounds2)).min.x, ((Bounds)(ref bounds2)).min.y, ((Bounds)(ref bounds2)).min.z),
					new Vector3(((Bounds)(ref bounds2)).min.x, ((Bounds)(ref bounds2)).min.y, ((Bounds)(ref bounds2)).max.z),
					new Vector3(((Bounds)(ref bounds2)).min.x, ((Bounds)(ref bounds2)).max.y, ((Bounds)(ref bounds2)).min.z),
					new Vector3(((Bounds)(ref bounds2)).min.x, ((Bounds)(ref bounds2)).max.y, ((Bounds)(ref bounds2)).max.z),
					new Vector3(((Bounds)(ref bounds2)).max.x, ((Bounds)(ref bounds2)).min.y, ((Bounds)(ref bounds2)).min.z),
					new Vector3(((Bounds)(ref bounds2)).max.x, ((Bounds)(ref bounds2)).min.y, ((Bounds)(ref bounds2)).max.z),
					new Vector3(((Bounds)(ref bounds2)).max.x, ((Bounds)(ref bounds2)).max.y, ((Bounds)(ref bounds2)).min.z),
					new Vector3(((Bounds)(ref bounds2)).max.x, ((Bounds)(ref bounds2)).max.y, ((Bounds)(ref bounds2)).max.z)
				};
				foreach (Vector3 val2 in array)
				{
					Vector3 val3 = ((Component)val).transform.TransformPoint(val2);
					Vector3 val4 = visual.transform.InverseTransformPoint(val3);
					if (flag)
					{
						bounds = new Bounds(val4, Vector3.zero);
						flag = false;
					}
					else
					{
						((Bounds)(ref bounds)).Encapsulate(val4);
					}
				}
			}
			return !flag;
		}

		private static void ApplyBoneToSkullOnly(GameObject prefab)
		{
			if (!Object.op_Implicit((Object)(object)prefab))
			{
				return;
			}
			Material val = FindSkullMaterial();
			if (!Object.op_Implicit((Object)(object)val))
			{
				ManualLogSource? log = LockSmith.Log;
				if (log != null)
				{
					log.LogWarning((object)"No trophy bone material found; skull keeps pack mats.");
				}
				return;
			}
			int num = 0;
			Renderer[] componentsInChildren = prefab.GetComponentsInChildren<Renderer>(true);
			foreach (Renderer val2 in componentsInChildren)
			{
				if (!Object.op_Implicit((Object)(object)val2))
				{
					continue;
				}
				string name = ((object)val2).GetType().Name;
				if (name != "MeshRenderer" && name != "SkinnedMeshRenderer")
				{
					continue;
				}
				if (IsSkullRenderer(val2))
				{
					val2.enabled = true;
					val2.sharedMaterial = val;
					num++;
					continue;
				}
				Material[] sharedMaterials = val2.sharedMaterials;
				if (sharedMaterials == null || sharedMaterials.Length < 2)
				{
					continue;
				}
				bool flag = false;
				for (int j = 0; j < sharedMaterials.Length; j++)
				{
					Material val3 = sharedMaterials[j];
					string text = (Object.op_Implicit((Object)(object)val3) ? (((Object)val3).name ?? "") : "");
					if (text.IndexOf("mainkey", StringComparison.OrdinalIgnoreCase) < 0 && text.IndexOf("iron", StringComparison.OrdinalIgnoreCase) < 0 && text.IndexOf("blackmetal", StringComparison.OrdinalIgnoreCase) < 0 && text.IndexOf("black", StringComparison.OrdinalIgnoreCase) < 0 && (text.IndexOf("necklace", StringComparison.OrdinalIgnoreCase) >= 0 || text.IndexOf("silver", StringComparison.OrdinalIgnoreCase) >= 0 || text.IndexOf("skull", StringComparison.OrdinalIgnoreCase) >= 0 || text.IndexOf("bone", StringComparison.OrdinalIgnoreCase) >= 0 || text.IndexOf("gem", StringComparison.OrdinalIgnoreCase) >= 0))
					{
						sharedMaterials[j] = val;
						num++;
						flag = true;
					}
				}
				if (flag)
				{
					val2.enabled = true;
					val2.sharedMaterials = sharedMaterials;
				}
			}
			ManualLogSource? log2 = LockSmith.Log;
			if (log2 != null)
			{
				log2.LogInfo((object)$"masterkey bone-on-skull only: slots={num} mat='{((Object)val).name}'.");
			}
		}

		private static void FixKeysPackMasterKeyVisual(GameObject prefab)
		{
			if (!Object.op_Implicit((Object)(object)prefab))
			{
				return;
			}
			int num = 0;
			MeshFilter[] componentsInChildren = prefab.GetComponentsInChildren<MeshFilter>(true);
			foreach (MeshFilter val in componentsInChildren)
			{
				if (!Object.op_Implicit((Object)(object)val))
				{
					continue;
				}
				Mesh sharedMesh = val.sharedMesh;
				string text = (((Object)(object)sharedMesh != (Object)null) ? (((Object)sharedMesh).name ?? "") : "");
				if (!((Object)(object)sharedMesh == (Object)null) && sharedMesh.vertexCount > 0 && text.IndexOf("NurbsPath", StringComparison.OrdinalIgnoreCase) < 0)
				{
					continue;
				}
				Renderer[] componentsInChildren2 = ((Component)val).GetComponentsInChildren<Renderer>(true);
				foreach (Renderer val2 in componentsInChildren2)
				{
					if (Object.op_Implicit((Object)(object)val2))
					{
						val2.enabled = false;
					}
				}
				((Component)val).gameObject.SetActive(false);
				num++;
			}
			if (num > 0)
			{
				ManualLogSource? log = LockSmith.Log;
				if (log != null)
				{
					log.LogInfo((object)$"Disabled {num} broken spike mesh node(s) on '{((Object)prefab).name}'.");
				}
			}
			else
			{
				ManualLogSource? log2 = LockSmith.Log;
				if (log2 != null)
				{
					log2.LogInfo((object)"MasterKey shaft mesh looks embedded — leaving it enabled.");
				}
			}
			AttachKeySkullFromKeysBundle(prefab);
		}

		private static void AttachKeySkullFromKeysBundle(GameObject keyPrefab)
		{
			//IL_022c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0231: Unknown result type (might be due to invalid IL or missing references)
			//IL_023f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0254: Unknown result type (might be due to invalid IL or missing references)
			//IL_025e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0273: Unknown result type (might be due to invalid IL or missing references)
			//IL_027d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0283: Unknown result type (might be due to invalid IL or missing references)
			//IL_028d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0297: Unknown result type (might be due to invalid IL or missing references)
			if (!Object.op_Implicit((Object)(object)keyPrefab))
			{
				return;
			}
			Transform[] componentsInChildren = keyPrefab.GetComponentsInChildren<Transform>(true);
			foreach (Transform val in componentsInChildren)
			{
				if (!Object.op_Implicit((Object)(object)val) || !((Object)val).name.Equals("keyskull", StringComparison.OrdinalIgnoreCase) || !((Object)(object)val != (Object)(object)keyPrefab.transform))
				{
					continue;
				}
				MeshFilter component = ((Component)val).GetComponent<MeshFilter>();
				if (Object.op_Implicit((Object)(object)component) && Object.op_Implicit((Object)(object)component.sharedMesh) && component.sharedMesh.vertexCount > 0)
				{
					ManualLogSource? log = LockSmith.Log;
					if (log != null)
					{
						log.LogInfo((object)$"keys.bundle keyskull already under '{((Object)keyPrefab).name}' (mesh '{((Object)component.sharedMesh).name}', verts={component.sharedMesh.vertexCount}).");
					}
					return;
				}
			}
			AssetBundle val2 = TryLoadKeysBundle();
			if ((Object)(object)val2 == (Object)null)
			{
				ManualLogSource? log2 = LockSmith.Log;
				if (log2 != null)
				{
					log2.LogWarning((object)"keys.bundle not loadable; falling back to trophy skull mesh.");
				}
				AttachSkullDecoration(keyPrefab);
				return;
			}
			GameObject val3 = val2.LoadAsset<GameObject>("keyskull") ?? val2.LoadAsset<GameObject>("assets/drake/locksmit/keyskull.prefab");
			if (!Object.op_Implicit((Object)(object)val3))
			{
				GameObject[] array = val2.LoadAllAssets<GameObject>();
				foreach (GameObject val4 in array)
				{
					if ((Object)(object)val4 != (Object)null && ((Object)val4).name.Equals("keyskull", StringComparison.OrdinalIgnoreCase))
					{
						val3 = val4;
						break;
					}
				}
			}
			if (!Object.op_Implicit((Object)(object)val3))
			{
				ManualLogSource? log3 = LockSmith.Log;
				if (log3 != null)
				{
					log3.LogWarning((object)"keys.bundle has no 'keyskull' GameObject; using trophy skull.");
				}
				AttachSkullDecoration(keyPrefab);
				return;
			}
			Mesh val5 = null;
			Material[] array2 = null;
			MeshFilter componentInChildren = val3.GetComponentInChildren<MeshFilter>(true);
			if (Object.op_Implicit((Object)(object)componentInChildren) && Object.op_Implicit((Object)(object)componentInChildren.sharedMesh))
			{
				val5 = componentInChildren.sharedMesh;
				MeshRenderer component2 = ((Component)componentInChildren).GetComponent<MeshRenderer>();
				if (Object.op_Implicit((Object)(object)component2))
				{
					array2 = ((Renderer)component2).sharedMaterials;
				}
			}
			if (!Object.op_Implicit((Object)(object)val5) || val5.vertexCount <= 0)
			{
				ManualLogSource? log4 = LockSmith.Log;
				if (log4 != null)
				{
					log4.LogWarning((object)"keyskull has no usable mesh; using trophy skull.");
				}
				AttachSkullDecoration(keyPrefab);
				return;
			}
			Transform val6 = keyPrefab.transform.Find("art") ?? keyPrefab.transform.Find("attach") ?? keyPrefab.transform;
			GameObject val7 = new GameObject("keyskull");
			val7.transform.SetParent(val6, false);
			val7.transform.localPosition = new Vector3(0f, 0.08f, 0f);
			val7.transform.localRotation = Quaternion.Euler(-10f, 180f, 0f);
			val7.transform.localScale = Vector3.one * 0.35f;
			val7.AddComponent<MeshFilter>().sharedMesh = val5;
			MeshRenderer val8 = val7.AddComponent<MeshRenderer>();
			if (array2 != null && array2.Length != 0)
			{
				((Renderer)val8).sharedMaterials = array2;
			}
			ManualLogSource? log5 = LockSmith.Log;
			if (log5 != null)
			{
				log5.LogInfo((object)$"Attached keys.bundle keyskull mesh '{((Object)val5).name}' (verts={val5.vertexCount}) onto '{((Object)keyPrefab).name}'.");
			}
		}

		private static AssetBundle? TryLoadKeysBundle()
		{
			try
			{
				LockSmith instance = LockSmith.Instance;
				if ((Object)(object)instance == (Object)null)
				{
					return null;
				}
				string directoryName = Path.GetDirectoryName(((BaseUnityPlugin)instance).Info.Location);
				if (string.IsNullOrEmpty(directoryName))
				{
					return null;
				}
				string text = Path.Combine(directoryName, "Assets", "Items", "keys", "keys.bundle");
				if (!File.Exists(text))
				{
					return null;
				}
				string pluginPath = Paths.PluginPath;
				if (string.IsNullOrEmpty(pluginPath))
				{
					return null;
				}
				string text2 = pluginPath.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
				char directorySeparatorChar = Path.DirectorySeparatorChar;
				string text3 = text2 + directorySeparatorChar;
				if (!text.StartsWith(text3, StringComparison.OrdinalIgnoreCase))
				{
					return null;
				}
				return AssetUtils.LoadAssetBundle(text.Substring(text3.Length).Replace('\\', '/'));
			}
			catch (Exception ex)
			{
				ManualLogSource? log = LockSmith.Log;
				if (log != null)
				{
					log.LogWarning((object)("keys.bundle load failed: " + ex.Message));
				}
				return null;
			}
		}

		private static void AttachSkullDecoration(GameObject keyPrefab)
		{
			//IL_016c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0171: Unknown result type (might be due to invalid IL or missing references)
			//IL_0178: Unknown result type (might be due to invalid IL or missing references)
			//IL_018a: Unknown result type (might be due to invalid IL or missing references)
			//IL_019f: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01be: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e2: Unknown result type (might be due to invalid IL or missing references)
			if (!Object.op_Implicit((Object)(object)keyPrefab))
			{
				return;
			}
			List<GameObject> list = new List<GameObject>();
			Transform[] componentsInChildren = keyPrefab.GetComponentsInChildren<Transform>(true);
			foreach (Transform val in componentsInChildren)
			{
				if (Object.op_Implicit((Object)(object)val) && !((Object)(object)val == (Object)(object)keyPrefab.transform) && ((Object)val).name.Equals("keyskull", StringComparison.OrdinalIgnoreCase))
				{
					list.Add(((Component)val).gameObject);
				}
			}
			foreach (GameObject item in list)
			{
				if (Object.op_Implicit((Object)(object)item))
				{
					Object.DestroyImmediate((Object)(object)item);
				}
			}
			Renderer val2 = FindSkullMeshSource();
			if (!Object.op_Implicit((Object)(object)val2))
			{
				ManualLogSource? log = LockSmith.Log;
				if (log != null)
				{
					log.LogWarning((object)"No skull mesh source found; key will be CryptKey-only.");
				}
				return;
			}
			Mesh val3 = null;
			Material[] array = null;
			MeshFilter component = ((Component)val2).GetComponent<MeshFilter>();
			if (Object.op_Implicit((Object)(object)component) && Object.op_Implicit((Object)(object)component.sharedMesh))
			{
				val3 = component.sharedMesh;
				MeshRenderer component2 = ((Component)val2).GetComponent<MeshRenderer>();
				if (Object.op_Implicit((Object)(object)component2))
				{
					array = ((Renderer)component2).sharedMaterials;
				}
			}
			else
			{
				SkinnedMeshRenderer val4 = (SkinnedMeshRenderer)(object)((val2 is SkinnedMeshRenderer) ? val2 : null);
				if (val4 != null && Object.op_Implicit((Object)(object)val4.sharedMesh))
				{
					val3 = val4.sharedMesh;
					array = ((Renderer)val4).sharedMaterials;
				}
			}
			if (!Object.op_Implicit((Object)(object)val3))
			{
				ManualLogSource? log2 = LockSmith.Log;
				if (log2 != null)
				{
					log2.LogWarning((object)("Skull source '" + ((Object)val2).name + "' has no mesh; skipping decoration."));
				}
				return;
			}
			GameObject val5 = new GameObject("keyskull");
			val5.SetActive(true);
			val5.transform.SetParent(keyPrefab.transform, false);
			val5.transform.localPosition = new Vector3(0f, 0.08f, 0f);
			val5.transform.localRotation = Quaternion.Euler(-10f, 180f, 0f);
			val5.transform.localScale = Vector3.one * 0.18f;
			val5.AddComponent<MeshFilter>().sharedMesh = val3;
			MeshRenderer val6 = val5.AddComponent<MeshRenderer>();
			if (array != null && array.Length != 0)
			{
				((Renderer)val6).sharedMaterials = array;
			}
			else
			{
				Material val7 = FindSkullMaterial();
				if (Object.op_Implicit((Object)(object)val7))
				{
					((Renderer)val6).sharedMaterial = val7;
				}
			}
			ManualLogSource? log3 = LockSmith.Log;
			if (log3 != null)
			{
				log3.LogInfo((object)("Attached mesh-only skull from '" + ((Object)val2).name + "' (mesh '" + ((Object)val3).name + "') onto '" + ((Object)keyPrefab).name + "'."));
			}
		}

		private static void EnsureRootZNetView(GameObject prefab)
		{
			//IL_0122: 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)
			if (!Object.op_Implicit((Object)(object)prefab))
			{
				return;
			}
			prefab.SetActive(true);
			PrefabManager instance = PrefabManager.Instance;
			object obj = ((instance != null) ? instance.GetPrefab("LeatherScraps") : null);
			if (obj == null)
			{
				PrefabManager instance2 = PrefabManager.Instance;
				obj = ((instance2 != null) ? instance2.GetPrefab("CryptKey") : null);
			}
			GameObject val = (GameObject)obj;
			ZNetView val2 = null;
			if (Object.op_Implicit((Object)(object)val))
			{
				val2 = val.GetComponent<ZNetView>() ?? val.GetComponentInChildren<ZNetView>(true);
			}
			ZNetView[] componentsInChildren = prefab.GetComponentsInChildren<ZNetView>(true);
			ZNetView val3 = prefab.GetComponent<ZNetView>();
			ZNetView[] array = componentsInChildren;
			foreach (ZNetView val4 in array)
			{
				if (Object.op_Implicit((Object)(object)val4) && (!Object.op_Implicit((Object)(object)val3) || !((Object)(object)val4 == (Object)(object)val3)))
				{
					if (!Object.op_Implicit((Object)(object)val3) && (Object)(object)((Component)val4).gameObject == (Object)(object)prefab)
					{
						val3 = val4;
					}
					else
					{
						Object.DestroyImmediate((Object)(object)val4);
					}
				}
			}
			if (!Object.op_Implicit((Object)(object)val3))
			{
				val3 = prefab.AddComponent<ZNetView>();
				ManualLogSource? log = LockSmith.Log;
				if (log != null)
				{
					log.LogWarning((object)("Added missing root ZNetView on '" + ((Object)prefab).name + "'."));
				}
			}
			((Behaviour)val3).enabled = true;
			if (Object.op_Implicit((Object)(object)val2))
			{
				val3.m_persistent = val2.m_persistent;
				val3.m_distant = val2.m_distant;
				val3.m_type = val2.m_type;
				val3.m_syncInitialScale = val2.m_syncInitialScale;
			}
			else
			{
				val3.m_persistent = true;
				val3.m_distant = false;
			}
			int num = prefab.GetComponentsInChildren<ZNetView>(true).Length;
			ManualLogSource? log2 = LockSmith.Log;
			if (log2 != null)
			{
				log2.LogInfo((object)$"'{((Object)prefab).name}' ZNetView check: root={Object.op_Implicit((Object)(object)val3)}, total={num}.");
			}
		}

		private static Renderer? FindSkullMeshSource()
		{
			string[] array = new string[3] { "TrophySkeleton", "TrophyDraugr", "Skeleton" };
			foreach (string text in array)
			{
				PrefabManager instance = PrefabManager.Instance;
				GameObject val = ((instance != null) ? instance.GetPrefab(text) : null);
				if (!Object.op_Implicit((Object)(object)val))
				{
					continue;
				}
				Renderer val2 = null;
				int num = -1;
				Renderer[] componentsInChildren = val.GetComponentsInChildren<Renderer>(true);
				foreach (Renderer val3 in componentsInChildren)
				{
					if (!Object.op_Implicit((Object)(object)val3))
					{
						continue;
					}
					string name = ((object)val3).GetType().Name;
					if (name != "MeshRenderer" && name != "SkinnedMeshRenderer")
					{
						continue;
					}
					string obj = ((Object)val3).name ?? "";
					int num2 = 0;
					if (obj.IndexOf("skull", StringComparison.OrdinalIgnoreCase) >= 0)
					{
						num2 += 10;
					}
					if (obj.IndexOf("head", StringComparison.OrdinalIgnoreCase) >= 0)
					{
						num2 += 5;
					}
					if (val3 is MeshRenderer)
					{
						num2 += 2;
					}
					Material sharedMaterial = val3.sharedMaterial;
					if (Object.op_Implicit((Object)(object)sharedMaterial))
					{
						string obj2 = ((Object)sharedMaterial).name ?? "";
						if (obj2.IndexOf("skull", StringComparison.OrdinalIgnoreCase) >= 0)
						{
							num2 += 8;
						}
						if (obj2.IndexOf("bone", StringComparison.OrdinalIgnoreCase) >= 0)
						{
							num2 += 4;
						}
					}
					if (num2 > num)
					{
						num = num2;
						val2 = val3;
					}
				}
				if (Object.op_Implicit((Object)(object)val2))
				{
					return val2;
				}
			}
			return null;
		}

		private static void SanitizeOfficialKey(GameObject? prefab, Sprite? icon)
		{
			//IL_0050: 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_006e: Expected O, but got Unknown
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Expected O, but got Unknown
			if (!Object.op_Implicit((Object)(object)prefab))
			{
				return;
			}
			ItemDrop component = prefab.GetComponent<ItemDrop>();
			if (component?.m_itemData?.m_shared != null)
			{
				SharedData shared = component.m_itemData.m_shared;
				shared.m_name = "$item_locksmith_key";
				shared.m_description = "$item_locksmith_key_desc";
				shared.m_itemType = (ItemType)19;
				shared.m_maxStackSize = 1;
				shared.m_variants = 1;
				shared.m_attack = new Attack();
				shared.m_secondaryAttack = new Attack();
				shared.m_useDurability = false;
				if (Object.op_Implicit((Object)(object)icon))
				{
					shared.m_icons = (Sprite[])(object)new Sprite[1] { icon };
				}
				if ((Object)(object)component.m_itemData.m_dropPrefab == (Object)null)
				{
					component.m_itemData.m_dropPrefab = prefab;
				}
			}
		}

		private static void ApplyBlackIronNoGlow(GameObject? prefab)
		{
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			if (!Object.op_Implicit((Object)(object)prefab))
			{
				return;
			}
			int num = 0;
			Light[] componentsInChildren = prefab.GetComponentsInChildren<Light>(true);
			foreach (Light val in componentsInChildren)
			{
				if (Object.op_Implicit((Object)(object)val))
				{
					((Behaviour)val).enabled = false;
					val.intensity = 0f;
					num++;
				}
			}
			int num2 = 0;
			ParticleSystem[] componentsInChildren2 = prefab.GetComponentsInChildren<ParticleSystem>(true);
			foreach (ParticleSystem val2 in componentsInChildren2)
			{
				if (Object.op_Implicit((Object)(object)val2))
				{
					val2.Stop(true, (ParticleSystemStopBehavior)0);
					EmissionModule emission = val2.emission;
					((EmissionModule)(ref emission)).enabled = false;
					num2++;
				}
			}
			Renderer[] componentsInChildren3 = prefab.GetComponentsInChildren<Renderer>(true);
			foreach (Renderer val3 in componentsInChildren3)
			{
				if (Object.op_Implicit((Object)(object)val3))
				{
					bool flag;
					switch (((object)val3).GetType().Name)
					{
					case "ParticleSystemRenderer":
					case "TrailRenderer":
					case "LineRenderer":
						flag = true;
						break;
					default:
						flag = false;
						break;
					}
					if (flag)
					{
						val3.enabled = false;
						num2++;
					}
				}
			}
			Material val4 = FindBlackIronMaterial();
			Material val5 = FindSkullMaterial();
			int num3 = 0;
			int num4 = 0;
			componentsInChildren3 = prefab.GetComponentsInChildren<Renderer>(true);
			foreach (Renderer val6 in componentsInChildren3)
			{
				if (!Object.op_Implicit((Object)(object)val6))
				{
					continue;
				}
				string name = ((object)val6).GetType().Name;
				if (name != "MeshRenderer" && name != "SkinnedMeshRenderer")
				{
					continue;
				}
				val6.enabled = true;
				if (IsSkullRenderer(val6))
				{
					if (Object.op_Implicit((Object)(object)val5))
					{
						val6.sharedMaterial = val5;
						num4++;
					}
					else if (Object.op_Implicit((Object)(object)val4))
					{
						val6.sharedMaterial = val4;
						num3++;
					}
				}
				else if (Object.op_Implicit((Object)(object)val4))
				{
					val6.sharedMaterial = val4;
					num3++;
				}
				else
				{
					KillEmission(val6);
				}
			}
			if (!Object.op_Implicit((Object)(object)val4))
			{
				ManualLogSource? log = LockSmith.Log;
				if (log != null)
				{
					log.LogWarning((object)"Blackmetal material not found; shaft may keep bundle mats.");
				}
			}
			if (!Object.op_Implicit((Object)(object)val5))
			{
				ManualLogSource? log2 = LockSmith.Log;
				if (log2 != null)
				{
					log2.LogWarning((object)"Skull/bone material not found; skull may stay blackmetal.");
				}
			}
			ManualLogSource? log3 = LockSmith.Log;
			if (log3 != null)
			{
				log3.LogInfo((object)($"Key look: blackmetal={num3}, skullMat={num4} ('{((val5 != null) ? ((Object)val5).name : null)}'), " + $"lightsOff={num}, fxOff={num2}."));
			}
		}

		private static bool IsSkullRenderer(Renderer renderer)
		{
			Transform val = ((Component)renderer).transform;
			while (Object.op_Implicit((Object)(object)val))
			{
				string text = ((Object)val).name ?? "";
				if (text.IndexOf("skull", StringComparison.OrdinalIgnoreCase) >= 0 || text.IndexOf("keyskull", StringComparison.OrdinalIgnoreCase) >= 0 || text.Equals("skullKey2", StringComparison.OrdinalIgnoreCase))
				{
					return true;
				}
				val = val.parent;
			}
			return false;
		}

		private static void KillEmission(Renderer renderer)
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			Material[] materials = renderer.materials;
			foreach (Material val in materials)
			{
				if (Object.op_Implicit((Object)(object)val))
				{
					if (val.HasProperty("_EmissionColor"))
					{
						val.SetColor("_EmissionColor", Color.black);
					}
					if (val.HasProperty("_EmissionColorUI"))
					{
						val.SetColor("_EmissionColorUI", Color.black);
					}
					if (val.HasProperty("_EmissionIntensity"))
					{
						val.SetFloat("_EmissionIntensity", 0f);
					}
					if (val.IsKeywordEnabled("_EMISSION"))
					{
						val.DisableKeyword("_EMISSION");
					}
				}
			}
			renderer.materials = materials;
		}

		private static Material? FindBlackIronMaterial()
		{
			return FindMeshMaterialFromPrefabs(new string[6] { "AxeBlackMetal", "PickaxeBlackMetal", "SwordBlackMetal", "BlackMetal", "AtgeirBlackmetal", "KnifeBlackMetal" });
		}

		private static Material? FindSkullMaterial()
		{
			Material val = FindMeshMaterialFromPrefabs(new string[2] { "TrophySkeleton", "BoneFragments" }, new string[3] { "bone", "skull", "trophy" });
			if (Object.op_Implicit((Object)(object)val))
			{
				return val;
			}
			Material[] array = Resources.FindObjectsOfTypeAll<Material>();
			foreach (Material val2 in array)
			{
				if (Object.op_Implicit((Object)(object)val2))
				{
					string text = ((Object)val2).name ?? "";
					if ((text.IndexOf("bone", StringComparison.OrdinalIgnoreCase) >= 0 || text.IndexOf("skull", StringComparison.OrdinalIgnoreCase) >= 0) && !text.Equals("Skeleton", StringComparison.OrdinalIgnoreCase) && text.IndexOf("particle", StringComparison.OrdinalIgnoreCase) < 0 && text.IndexOf("vfx", StringComparison.OrdinalIgnoreCase) < 0)
					{
						return val2;
					}
				}
			}
			return null;
		}

		private static Material? FindMeshMaterialFromPrefabs(string[] prefabNames, string[]? preferNameContains = null)
		{
			Material val = null;
			foreach (string text in prefabNames)
			{
				PrefabManager instance = PrefabManager.Instance;
				GameObject val2 = ((instance != null) ? instance.GetPrefab(text) : null);
				if (!Object.op_Implicit((Object)(object)val2))
				{
					continue;
				}
				Renderer[] componentsInChildren = val2.GetComponentsInChildren<Renderer>(true);
				foreach (Renderer val3 in componentsInChildren)
				{
					if (!Object.op_Implicit((Object)(object)val3))
					{
						continue;
					}
					string name = ((object)val3).GetType().Name;
					if (name != "MeshRenderer" && name != "SkinnedMeshRenderer")
					{
						continue;
					}
					Material sharedMaterial = val3.sharedMaterial;
					if (!Object.op_Implicit((Object)(object)sharedMaterial))
					{
						continue;
					}
					string text2 = ((Object)sharedMaterial).name ?? "";
					if (text2.IndexOf("particle", StringComparison.OrdinalIgnoreCase) >= 0 || text2.IndexOf("vfx", StringComparison.OrdinalIgnoreCase) >= 0)
					{
						continue;
					}
					if (preferNameContains != null)
					{
						bool flag = false;
						foreach (string value in preferNameContains)
						{
							if (text2.IndexOf(value, StringComparison.OrdinalIgnoreCase) >= 0)
							{
								flag = true;
								break;
							}
						}
						if (flag)
						{
							return sharedMaterial;
						}
						if (val == null)
						{
							val = sharedMaterial;
						}
						continue;
					}
					return sharedMaterial;
				}
			}
			return val;
		}

		private static Sprite? LoadMasterKeyIcon()
		{
			try
			{
				LockSmith instance = LockSmith.Instance;
				if ((Object)(object)instance == (Object)null)
				{
					return null;
				}
				string directoryName = Path.GetDirectoryName(((BaseUnityPlugin)instance).Info.Location);
				if (string.IsNullOrEmpty(directoryName))
				{
					return null;
				}
				string text = Path.Combine(directoryName, "Assets", "masterkey_icon.png");
				if (!File.Exists(text))
				{
					return null;
				}
				string pluginPath = Paths.PluginPath;
				if (string.IsNullOrEmpty(pluginPath))
				{
					return null;
				}
				string text2 = pluginPath.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
				char directorySeparatorChar = Path.DirectorySeparatorChar;
				string text3 = text2 + directorySeparatorChar;
				if (!text.StartsWith(text3, StringComparison.OrdinalIgnoreCase))
				{
					return null;
				}
				string text4 = text.Substring(text3.Length).Replace('\\', '/');
				Sprite val = AssetUtils.LoadSpriteFromFile(text4);
				if (Object.op_Implicit((Object)(object)val))
				{
					ManualLogSource? log = LockSmith.Log;
					if (log != null)
					{
						log.LogInfo((object)("Loaded master key icon from " + text4));
					}
				}
				return Object.op_Implicit((Object)(object)val) ? val : null;
			}
			catch (Exception ex)
			{
				ManualLogSource? log2 = LockSmith.Log;
				if (log2 != null)
				{
					log2.LogWarning((object)("Failed to load master key icon: " + ex.Message));
				}
				return null;
			}
		}

		internal static List<(string PrefabName, int Amount)> ParseMaterials(string? raw)
		{
			List<(string, int)> list = new List<(string, int)>();
			if (string.IsNullOrWhiteSpace(raw))
			{
				return list;
			}
			string[] array = raw.Split(new char[2] { ',', ';' }, StringSplitOptions.RemoveEmptyEntries);
			for (int i = 0; i < array.Length; i++)
			{
				string text = array[i].Trim();
				int num = text.LastIndexOf(':');
				if (num <= 0 || num >= text.Length - 1)
				{
					ManualLogSource? log = LockSmith.Log;
					if (log != null)
					{
						log.LogWarning((object)("Ignoring invalid key material segment: \"" + text + "\""));
					}
					continue;
				}
				string item = text.Substring(0, num).Trim();
				if (!int.TryParse(text.Substring(num + 1).Trim(), NumberStyles.Integer, CultureInfo.InvariantCulture, out var result) || result <= 0)
				{
					ManualLogSource? log2 = LockSmith.Log;
					if (log2 != null)
					{
						log2.LogWarning((object)("Ignoring invalid key material amount: \"" + text + "\""));
					}
				}
				else
				{
					list.Add((item, result));
				}
			}
			return list;
		}
	}
	public static class KeyPassClipboardInput
	{
		public static void Tick(Player player)
		{
			if (!LockSmithConfig.EnableKeyPasses || !LockSmithConfig.EnableKeyMode || !Object.op_Implicit((Object)(object)player) || (Object)(object)player != (Object)(object)Player.m_localPlayer || KeyPassMenu.IsOpen || IsTextUiBlocking())
			{
				return;
			}
			ItemData equippedLocksmithKey = ChestAccessService.GetEquippedLocksmithKey((Humanoid?)(object)player);
			if (equippedLocksmithKey == null || (!Input.GetKey((KeyCode)306) && !Input.GetKey((KeyCode)305)))
			{
				return;
			}
			try
			{
				if (Input.GetKeyDown((KeyCode)99))
				{
					KeyPassService.TryPullFromLookTarget(equippedLocksmithKey);
				}
				else if (Input.GetKeyDown((KeyCode)118))
				{
					KeyPassService.TryPasteOntoLookTarget(equippedLocksmithKey);
				}
			}
			catch (Exception arg)
			{
				ManualLogSource? log = LockSmith.Log;
				if (log != null)
				{
					log.LogError((object)$"KeyPass clipboard input failed: {arg}");
				}
			}
		}

		private static bool IsTextUiBlocking()
		{
			try
			{
				if ((Object)(object)Console.instance != (Object)null && Console.IsVisible())
				{
					return true;
				}
			}
			catch
			{
			}
			try
			{
				if ((Object)(object)Chat.instance != (Object)null && Chat.instance.HasFocus())
				{
					return true;
				}
			}
			catch
			{
			}
			try
			{
				if (TextInput.IsVisible())
				{
					return true;
				}
			}
			catch
			{
			}
			try
			{
				if (InventoryGui.IsVisible() && !Input.GetKey((KeyCode)306))
				{
					Input.GetKey((KeyCode)305);
				}
			}
			catch
			{
			}
			return false;
		}
	}
	public static class KeyPassService
	{
		public const string PassGuestsKey = "locksmith_pass_guests";

		public const string PassKindKey = "locksmith_pass_kind";

		private const float NearbyRange = 5f;

		public static bool HasPassPayload(ItemData? item)
		{
			if (item?.m_customData == null)
			{
				return false;
			}
			if (item.m_customData.TryGetValue("locksmith_pass_guests", out var value))
			{
				return !string.IsNullOrWhiteSpace(value);
			}
			return false;
		}

		public static string? FormatMembershipSubtitle(ItemData? item)
		{
			List<PieceGuest> guests = GetGuests(item);
			if (guests.Count == 0)
			{
				return null;
			}
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.Append((guests.Count == 1) ? "Pass: " : "Team: ");
			AppendGuestNames(stringBuilder, guests);
			return stringBuilder.ToString();
		}

		public static string? FormatMembershipTooltipLine(ItemData? item)
		{
			List<PieceGuest> guests = GetGuests(item);
			if (guests.Count == 0)
			{
				return null;
			}
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.Append(LockSmithLocalization.T("locksmith_piece_team"));
			stringBuilder.Append(" : ");
			AppendGuestNames(stringBuilder, guests);
			return AccessHoverDisplay.Colorize(stringBuilder.ToString());
		}

		private static void AppendGuestNames(StringBuilder sb, List<PieceGuest> guests)
		{
			for (int i = 0; i < guests.Count; i++)
			{
				if (i > 0)
				{
					sb.Append(", ");
				}
				sb.Append(string.IsNullOrEmpty(guests[i].DisplayName) ? guests[i].PlayerId.ToString(CultureInfo.InvariantCulture) : guests[i].DisplayName);
			}
		}

		public static List<PieceGuest> GetGuests(ItemData? item)
		{
			List<PieceGuest> list = new List<PieceGuest>();
			if (item?.m_customData == null)
			{
				return list;
			}
			if (!item.m_customData.TryGetValue("locksmith_pass_guests", out var value) || string.IsNullOrWhiteSpace(value))
			{
				return list;
			}
			string[] array = value.Split(new char[1] { ';' }, StringSplitOptions.RemoveEmptyEntries);
			for (int i = 0; i < array.Length; i++)
			{
				string text = array[i].Trim();
				if (string.IsNullOrEmpty(text))
				{
					continue;
				}
				string displayName = string.Empty;
				int num = text.IndexOf('|');
				long result;
				if (num >= 0)
				{
					if (!long.TryParse(text.Substring(0, num).Trim(), NumberStyles.Integer, CultureInfo.InvariantCulture, out result))
					{
						continue;
					}
					displayName = text.Substring(num + 1).Trim();
				}
				else if (!long.TryParse(text, NumberStyles.Integer, CultureInfo.InvariantCulture, out result))
				{
					continue;
				}
				if (result != 0L)
				{
					list.Add(new PieceGuest(result, displayName));
				}
			}
			return list;
		}

		public static void SetGuests(ItemData item, IReadOnlyList<PieceGuest> guests)
		{
			if (item == null)
			{
				return;
			}
			if (item.m_customData == null)
			{
				item.m_customData = new Dictionary<string, string>();
			}
			if (guests == null || guests.Count == 0)
			{
				item.m_customData.Remove("locksmith_pass_guests");
				item.m_customData.Remove("locksmith_pass_kind");
				return;
			}
			StringBuilder stringBuilder = new StringBuilder();
			HashSet<long> hashSet = new HashSet<long>();
			foreach (PieceGuest guest in guests)
			{
				if (guest.PlayerId != 0L && hashSet.Add(guest.PlayerId))
				{
					if (stringBuilder.Length > 0)
					{
						stringBuilder.Append(';');
					}
					stringBuilder.Append(guest.PlayerId.ToString(CultureInfo.InvariantCulture));
					stringBuilder.Append('|');
					stringBuilder.Append((guest.DisplayName ?? string.Empty).Replace(";", ",").Replace("|", "/"));
				}
			}
			item.m_customData["locksmith_pass_guests"] = stringBuilder.ToString();
			item.m_customData["locksmith_pass_kind"] = ((hashSet.Count <= 1) ? "person" : "group");
		}

		public static void ClearPass(ItemData item)
		{
			if (item?.m_customData != null)
			{
				item.m_customData.Remove("locksmith_pass_guests");
				item.m_customData.Remove("locksmith_pass_kind");
			}
		}

		public static string GetLabelBracket(ItemData item)
		{
			string text = CustomizeLibsAPI.GetProperName(item);
			if (string.IsNullOrEmpty(text))
			{
				text = CustomizeLibsAPI.GetDisplayNameForUi(item, false) ?? "";
			}
			int num = text.LastIndexOf('[');
			int num2 = text.LastIndexOf(']');
			if (num >= 0 && num2 > num)
			{
				return text.Substring(num + 1, num2 - num - 1).Trim();
			}
			return string.Empty;
		}

		public static void SetLabelBracket(ItemData item, string? bracket)
		{
			if (item != null)
			{
				string text = LockSmithConfig.KeyName;
				if (string.IsNullOrWhiteSpace(text))
				{
					text = "Master Key";
				}
				string text2 = (bracket ?? string.Empty).Trim();
				if (string.IsNullOrEmpty(text2))
				{
					CustomizeLibsAPI.SetCustomName(item, text);
				}
				else
				{
					CustomizeLibsAPI.SetCustomName(item, text + " [" + text2 + "]");
				}
			}
		}

		public static bool HasLookTargetGuests()
		{
			if (TryGetLookTargetNview(out ZNetView nview))
			{
				return PieceGuestAccess.GetGuests(nview).Count > 0;
			}
			return false;
		}

		public static void TryPullFromLookTarget(ItemData item)
		{
			if (!TryGetLookTargetNview(out ZNetView nview))
			{
				AccessFeedback.ShowRaw((Humanoid?)(object)Player.m_localPlayer, "Look at a LockSmith piece first.");
				return;
			}
			List<PieceGuest> guests = PieceGuestAccess.GetGuests(nview);
			if (guests.Count == 0)
			{
				AccessFeedback.ShowRaw((Humanoid?)(object)Player.m_localPlayer, "No guests on that piece.");
				return;
			}
			MergeIntoKey(item, guests);
			string text = FormatMembershipSubtitle(item) ?? "names";
			AccessFeedback.ShowRaw((Humanoid?)(object)Player.m_localPlayer, "Copied " + text);
		}

		public static void TryPasteOntoLookTarget(ItemData item)
		{
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			Player localPlayer = Player.m_localPlayer;
			if (!Object.op_Implicit((Object)(object)localPlayer))
			{
				return;
			}
			if (!HasPassPayload(item))
			{
				AccessFeedback.ShowRaw((Humanoid?)(object)localPlayer, "Key has no names — Ctrl+C a piece first.");
				return;
			}
			if (!TryGetLookTargetNview(out ZNetView nview))
			{
				AccessFeedback.ShowRaw((Humanoid?)(object)localPlayer, "Look at a chest or door first.");
				return;
			}
			Vector3 position = ((Component)nview).transform.position;
			Container componentInChildren = ((Component)nview).GetComponentInChildren<Container>();
			bool isPrivateFamilyChest = (Object)(object)componentInChildren != (Object)null && GroupAccessState.IsPrivateFamilyChest(componentInChildren);
			if (!WardAccess.AllowsToolOnPiece(position, isPrivateFamilyChest))
			{
				AccessFeedback.Show((Humanoid?)(object)localPlayer, "locksmith_msg_need_active_ward");
				return;
			}
			long playerID = localPlayer.GetPlayerID();
			if (!PieceGuestAccess.CanManageGuests(nview, playerID))
			{
				AccessFeedback.ShowRaw((Humanoid?)(object)localPlayer, "No access to change that piece.");
				return;
			}
			List<PieceGuest> guests = GetGuests(item);
			if (guests.Count == 0)
			{
				AccessFeedback.ShowRaw((Humanoid?)(object)localPlayer, "Key has no names.");
				return;
			}
			PieceGuestAccess.RequestMergeGuests(nview, guests, playerID);
			PieceAccessState.MarkManaged(nview);
			string text = FormatMembershipSubtitle(item) ?? "names";
			AccessFeedback.ShowRaw((Humanoid?)(object)localPlayer, "Pasted " + text);
		}

		public static void TryGrabNearby(ItemData item)
		{
			//IL_0046: 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)
			Player localPlayer = Player.m_localPlayer;
			if (!Object.op_Implicit((Object)(object)localPlayer))
			{
				return;
			}
			Player val = null;
			float num = 5f;
			foreach (Player allPlayer in Player.GetAllPlayers())
			{
				if (Object.op_Implicit((Object)(object)allPlayer) && !((Object)(object)allPlayer == (Object)(object)localPlayer))
				{
					float num2 = Vector3.Distance(((Component)localPlayer).transform.position, ((Component)allPlayer).transform.position);
					if (!(num2 > num))
					{
						num = num2;
						val = allPlayer;
					}
				}
			}
			if (!Object.op_Implicit((Object)(object)val))
			{
				AccessFeedback.ShowRaw((Humanoid?)(object)localPlayer, "No player nearby.");
				return;
			}
			MergeIntoKey(item, new PieceGuest[1]
			{
				new PieceGuest(val.GetPlayerID(), val.GetPlayerName())
			});
			AccessFeedback.ShowRaw((Humanoid?)(object)localPlayer, "Added " + val.GetPlayerName() + " to key.");
		}

		public static bool TryCloneKey(ItemData source, out string detail)
		{
			detail = "";
			Player localPlayer = Player.m_localPlayer;
			if (!Object.op_Implicit((Object)(object)localPlayer) || source == null)
			{
				return false;
			}
			if (!TryConsumeCloneCost(localPlayer))
			{
				detail = "Need " + LockSmithConfig.KeyMaterials + " to clone.";
				AccessFeedback.ShowRaw((Humanoid?)(object)localPlayer, detail);
				return false;
			}
			string text = "masterkey";
			GameObject val = (((Object)(object)ObjectDB.instance != (Object)null) ? ObjectDB.instance.GetItemPrefab(text) : null);
			if (!Object.op_Implicit((Object)(object)val))
			{
				detail = "Key prefab missing.";
				AccessFeedback.ShowRaw((Humanoid?)(object)localPlayer, detail);
				return false;
			}
			Inventory inventory = ((Humanoid)localPlayer).GetInventory();
			if (inventory == null || !inventory.AddItem(val, 1))
			{
				detail = "Inventory full.";
				AccessFeedback.ShowRaw((Humanoid?)(object)localPlayer, detail);
				return false;
			}
			ItemData val2 = null;
			foreach (ItemData allItem in inventory.GetAllItems())
			{
				if (allItem != null && allItem != source && ChestAccessService.IsLocksmithKey(allItem))
				{
					val2 = allItem;
				}
			}
			if (val2 == null)
			{
				detail = "Clone failed.";
				return false;
			}
			if (source.m_customData != null)
			{
				if (val2.m_customData == null)
				{
					val2.m_customData = new Dictionary<string, string>();
				}
				foreach (KeyValuePair<string, string> customDatum in source.m_customData)
				{
					val2.m_customData[customDatum.Key] = customDatum.Value;
				}
			}
			ChestAccessService.EnsureRenameHandOff(val2);
			string properName = CustomizeLibsAPI.GetProperName(source);
			if (!string.IsNullOrEmpty(properName))
			{
				CustomizeLibsAPI.SetCustomName(val2, properName);
			}
			try
			{
				((Character)localPlayer).ShowPickupMessage(val2, 1);
			}
			catch (Exception)
			{
			}
			string text2 = CustomizeLibsAPI.GetDisplayNameForUi(val2, true);
			if (string.IsNullOrEmpty(text2))
			{
				text2 = LockSmithConfig.KeyName;
			}
			detail = "Cloned: " + text2;
			return true;
		}

		private static void MergeIntoKey(ItemData item, IReadOnlyList<PieceGuest> add)
		{
			List<PieceGuest> guests = GetGuests(item);
			HashSet<long> hashSet = new HashSet<long>();
			foreach (PieceGuest item2 in guests)
			{
				hashSet.Add(item2.PlayerId);
			}
			foreach (PieceGuest item3 in add)
			{
				if (item3.PlayerId != 0L && hashSet.Add(item3.PlayerId))
				{
					guests.Add(item3);
				}
			}
			SetGuests(item, guests);
			if (guests.Count == 1)
			{
				SetLabelBracket(item, string.IsNullOrEmpty(guests[0].DisplayName) ? "Pass" : guests[0].DisplayName);
			}
			else if (guests.Count > 1 && string.IsNullOrEmpty(GetLabelBracket(item)))
			{
				SetLabelBracket(item, "Team");
			}
		}

		private static bool TryGetLookTargetNview(out ZNetView nview)
		{
			nview = null;
			Player localPlayer = Player.m_localPlayer;
			if (!Object.op_Implicit((Object)(object)localPlayer))
			{
				return false;
			}
			GameObject hoverObject = ((Humanoid)localPlayer).GetHoverObject();
			if (!Object.op_Implicit((Object)(object)hoverObject))
			{
				return false;
			}
			nview = hoverObject.GetComponentInParent<ZNetView>();
			if (Object.op_Implicit((Object)(object)nview))
			{
				return nview.IsValid();
			}
			return false;
		}

		private static bool TryConsumeCloneCost(Player player)
		{
			string keyMaterials = LockSmithConfig.KeyMaterials;
			if (string.IsNullOrWhiteSpace(keyMaterials))
			{
				return true;
			}
			string[] array = keyMaterials.Split(new char[1] { ',' })[0].Trim().Split(new char[1] { ':' });
			if (array.Length < 1)
			{
				return true;
			}
			string text = array[0].Trim();
			int result = 1;
			if (array.Length > 1)
			{
				int.TryParse(array[1].Trim(), NumberStyles.Integer, CultureInfo.InvariantCulture, out result);
			}
			if (result < 1)
			{
				result = 1;
			}
			Inventory inventory = ((Humanoid)player).GetInventory();
			if (inventory == null)
			{
				return false;
			}
			GameObject val = (((Object)(object)ObjectDB.instance != (Object)null) ? ObjectDB.instance.GetItemPrefab(text) : null);
			if (!Object.op_Implicit((Object)(object)val))
			{
				return false;
			}
			string text2 = val.GetComponent<ItemDrop>()?.m_itemData?.m_shared?.m_name;
			if (string.IsNullOrEmpty(text2))
			{
				return false;
			}
			if (inventory.CountItems(text2, -1, true) < result)
			{
				return false;
			}
			inventory.RemoveItem(text2, result, -1, true);
			return true;
		}
	}
	[BepInPlugin("com.drakesworkshop.locksmith", "LockSmith", "0.4.1")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[NetworkCompatibility(/*Could not decode attribute arguments.*/)]
	public class LockSmith : BaseUnityPlugin
	{
		private readonly Harmony _harmony = new Harmony("com.drakesworkshop.locksmith");

		public const string CompanyName = "DrakeMods";

		public const string ModName = "LockSmith";

		public const string Version = "0.4.1";

		public const string GUID = "com.drakesworkshop.locksmith";

		public static LockSmith Instance { get; private set; }

		public static ManualLogSource? Log { get; private set; }

		private void Awake()
		{
			Instance = this;
			Log = ((BaseUnityPlugin)this).Logger;
			LockSmithConfig.Bind(((BaseUnityPlugin)this).Config, ((BaseUnityPlugin)this).Logger);
			KeyPassMenu.RegisterTab();
			string text = Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location) ?? "";
			RepairFlattenedArtLayout(text);
			ArtItemLoader.Register(((BaseUnityPlugin)this).Logger, text, (ConfigFile)null, (Action<ArtItemContext>)ContentRegistration.CustomizeMasterKeyArtItem);
			PrefabManager.OnVanillaPrefabsAvailable += OnVanillaPrefabs;
			_harmony.PatchAll();
			((BaseUnityPlugin)this).Logger.LogInfo((object)"LockSmith 0.4.1 Awake (official key = keys.bundle MasterKey).");
		}

		private void OnVanillaPrefabs()
		{
			PrefabManager.OnVanillaPrefabsAvailable -= OnVanillaPrefabs;
			try
			{
				LockSmithLocalization.Register();
				ContentRegistration.FinalizeOfficialKeyFromKeysPack();
				PublicPieceRegistration.TryRegisterAll("OnVanillaPrefabsAvailable");
			}
			catch (Exception arg)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)$"LockSmith content registration failed: {arg}");
			}
		}

		private static void RepairFlattenedArtLayout(string pluginDir)
		{
			if (string.IsNullOrEmpty(pluginDir))
			{
				return;
			}
			string text = Path.Combine(pluginDir, "Assets", "Items", "keys");
			if (File.Exists(Path.Combine(text, "masterkey.json")) && File.Exists(Path.Combine(text, "keys.bundle")))
			{
				return;
			}
			string text2 = Path.Combine(pluginDir, "masterkey.json");
			string text3 = Path.Combine(pluginDir, "keys.bundle");
			if (!File.Exists(text2) || !File.Exists(text3))
			{
				return;
			}
			try
			{
				Directory.CreateDirectory(text);
				File.Copy(text2, Path.Combine(text, "masterkey.json"), overwrite: true);
				File.Copy(text3, Path.Combine(text, "keys.bundle"), overwrite: true);
				string text4 = Path.Combine(pluginDir, "masterkey.png");
				if (File.Exists(text4))
				{
					File.Copy(text4, Path.Combine(text, "masterkey.png"), overwrite: true);
				}
				string text5 = Path.Combine(pluginDir, "masterkey_icon.png");
				if (File.Exists(text5))
				{
					string text6 = Path.Combine(pluginDir, "Assets");
					Directory.CreateDirectory(text6);
					File.Copy(text5, Path.Combine(text6, "masterkey_icon.png"), overwrite: true);
				}
				ManualLogSource? log = Log;
				if (log != null)
				{
					log.LogWarning((object)"Repaired flattened key art into Assets/Items/keys (Gale/manager zip layout).");
				}
			}
			catch (Exception ex)
			{
				ManualLogSource? log2 = Log;
				if (log2 != null)
				{
					log2.LogError((object)("Failed to repair flattened key art layout: " + ex.Message));
				}
			}
		}
	}
	public enum LockSmithModifier
	{
		Alt,
		Shift,
		Control
	}
	public static class LockSmithConfig
	{
		public const int ExpectedSyncedEntryCount = 20;

		private const string SectionAdmin = "01 Admin";

		private const string SectionFeatures = "02 Features";

		private const string SectionKey = "03 Key";

		private const string SectionPieceMode = "05 PieceMode";

		private const string SectionDisplay = "04 Display";

		private const string DisplayAdmin = "Admin";

		private const string DisplayFeatures = "Features";

		private const string DisplayKey = "Key";

		private const string DisplayDisplay = "Display";

		private static readonly DrakeConfigSync Sync = DrakeConfigSync.Create("LockSmith", "LockSmith", "0.4.1", (string)null);

		private static ConfigEntry<bool> _lockSyncedConfig = null;

		private static ConfigEntry<bool> _enableChests = null;

		private static ConfigEntry<bool> _enableDoors = null;

		private static ConfigEntry<bool> _enableGroupChests = null;

		private static ConfigEntry<bool> _enablePersonalPause = null;

		private static ConfigEntry<bool> _enablePieceGuests = null;

		private static ConfigEntry<bool> _enableOptInAccess = null;

		private static ConfigEntry<bool> _enableGuestPublicToggle = null;

		private static ConfigEntry<bool> _enableDesignate = null;

		private static ConfigEntry<bool> _enableKeyMode = null;

		private static ConfigEntry<bool> _enablePieceMode = null;

		private static ConfigEntry<bool> _enableKeyPasses = null;

		private static ConfigEntry<bool> _requireActiveWard = null;

		private static ConfigEntry<string> _publicPieceAllowList = null;

		private static ConfigEntry<string> _publicPieceDenyList = null;

		private static ConfigEntry<bool> _publicPieceNameSuffix = null;

		private static ConfigEntry<string> _keyName = null;

		private static ConfigEntry<string> _keyDescription = null;

		private static ConfigEntry<string> _keyCraftingStation = null;

		private static ConfigEntry<string> _keyMaterials = null;

		private static ConfigEntry<string> _teamLabelColor = null;

		private static ConfigEntry<LockSmithModifier> _clearModifier = null;

		private static ConfigEntry<LockSmithModifier> _setupModifier = null;

		public static bool LockSyncedConfig => _lockSyncedConfig.Value;

		public static bool EnableChests => _enableChests.Value;

		public static bool EnableDoors => _enableDoors.Value;

		public static bool EnableGroupChests => _enableGroupChests.Value;

		public static bool EnablePersonalPause => _enablePersonalPause.Value;

		public static bool EnablePieceGuests => _enablePieceGuests.Value;

		public static bool EnableOptInAccess => _enableOptInAccess.Value;

		public static bool EnableGuestPublicToggle => _enableGuestPublicToggle.Value;

		public static bool EnableDesignate => _enableDesignate.Value;

		public static bool EnableKeyMode => _enableKeyMode.Value;

		public static bool EnablePieceMode => _enablePieceMode.Value;

		public static bool EnableKeyPasses => _enableKeyPasses.Value;

		public static string PublicPieceAllowList => _publicPieceAllowList.Value;

		public static string PublicPieceDenyList => _publicPieceDenyList.Value;

		public static bool PublicPieceNameSuffix => _publicPieceNameSuffix.Value;

		public static bool RequireActiveWard => _requireActiveWard.Value;

		public static string KeyName => _keyName.Value;

		public static string KeyDescription => _keyDescription.Value;

		public static string KeyCraftingStation => _keyCraftingStation.Value;

		public static string KeyMaterials => _keyMaterials.Value;

		public static string TeamLabelColorHex => NormalizeHexColor(_teamLabelColor.Value);

		public static LockSmithModifier ClearModifier => _clearModifier.Value;

		public static LockSmithModifier SetupModifier => _setupModifier.Value;

		public static void Bind(ConfigFile config, ManualLogSource log)
		{
			//IL_0317: Unknown result type (might be due to invalid IL or missing references)
			//IL_0321: Expected O, but got Unknown
			//IL_033d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0347: Expected O, but got Unknown
			//IL_0363: Unknown result type (might be due to invalid IL or missing references)
			//IL_036d: Expected O, but got Unknown
			_lockSyncedConfig = Sync.BindSynced<bool>(config, "01 Admin", "Admin", "LockSyncedConfig", true, "When true, only the host/admin can change synced LockSmith settings.");
			_enableChests = Sync.BindSynced<bool>(config, "02 Features", "Features", "EnableChests", true, "Allow LockSmith public/private on player-built chests.");
			_enableDoors = Sync.BindSynced<bool>(config, "02 Features", "Features", "EnableDoors", true, "Allow LockSmith public/private on player-built doors and gates.");
			_enableGroupChests = Sync.BindSynced<bool>(config, "02 Features", "Features", "EnableGroupChests", true, "Team sharing on private-family chests (Join list / guests).");
			_enablePersonalPause = Sync.BindSynced<bool>(config, "02 Features", "Features", "EnablePersonalPause", false, "When true, key E toggles Personal↔Team as a pause switch (keeps names; friends locked out while Personal). When false (default), that toggle is hidden — chests stay team-shared and you only add people via Join.");
			_enablePieceGuests = Sync.BindSynced<bool>(config, "02 Features", "Features", "EnablePieceGuests", true, "Partial ward access: guests on a chest/door can open without being on the ward.");
			_enableOptInAccess = Sync.BindSynced<bool>(config, "02 Features", "Features", "EnableOptInAccess", true, "Ward-style opt-in: creator opens Join access; other players press E to join (solo-testable).");
			_enableGuestPublicToggle = Sync.BindSynced<bool>(config, "02 Features", "Features", "EnableGuestPublicToggle", true, "Permitted players (ward or guest) Alt+E public/private without the key. With EnableDesignate, only on designated pieces. Strangers never can. Not for private chests.");
			_enableDesignate = Sync.BindSynced<bool>(config, "02 Features", "Features", "EnableDesignate", true, "Key must Enable LockSmith on a piece first (locksmith_managed). After that, permitted Alt+E works without holding the key. Off = key E toggles immediately; no designate gate.");
			_enableKeyMode = Sync.BindSynced<bool>(config, "02 Features", "Features", "EnableKeyMode", true, "Craft and use the LockSmith key to designate pieces and change Team/Join settings.");
			_enablePieceMode = Sync.BindSynced<bool>(config, "02 Features", "Features", "EnablePieceMode", false, "When true, discovers ward-locked chests/doors (vanilla + mods) and registers Hammer Public tab clones that are always open (no key / no ZDO). Restart required after changing.");
			_enableKeyPasses = Sync.BindSynced<bool>(config, "02 Features", "Features", "EnableKeyPasses", true, "Key pass clipboard: inventory menu on the Locksmith key (Relabel / grab / pull / clear / clone). Ctrl+C/V world copy-paste follows.");
			_requireActiveWard = Sync.BindSynced<bool>(config, "02 Features", "Features", "RequireActiveWard", true, "When true (default), LockSmith cannot manage or toggle normal chests/doors unless they are inside an enabled ward — including already-managed pieces. Personal/Team private-family chests are exempt. Doors always need the ward up.");
			_publicPieceAllowList = Sync.BindSynced<string>(config, "05 PieceMode", "PieceMode", "PublicPieceAllowList", "", "Comma-separated donor prefab names to force-clone into the Public hammer tab (in addition to auto-discovery). Restart required. Still skips private-family and already-public donors.");
			_publicPieceDenyList = Sync.BindSynced<string>(config, "05 PieceMode", "PieceMode", "PublicPieceDenyList", "", "Comma-separated donor prefab names never cloned (escape hatch for broken mod pieces). Restart required.");
			_publicPieceNameSuffix = Sync.BindSynced<bool>(config, "05 PieceMode", "PieceMode", "PublicPieceNameSuffix", true, "When true (default), public clones show a localized (public) suffix on hammer and hover names. When false, keep the donor display name (still listed under the Public tab). Restart required.");
			_keyName = Sync.BindSynced<string>(config, "03 Key", "Key", "KeyName", "Locksmith Key", "Display name for the craftable key.");
			_keyDescription = Sync.BindSynced<string>(config, "03 Key", "Key", "KeyDescription", "Equip to designate a chest/door. After that, <color=#ffff00><b>Alt+E</b></color> toggles public/private if you have access. Key required for Team / Join setup. <color=#ffff00><b>Shift+Right-click</b></color> the key: Relabel, grab/pull names, clear, or clone.", "Tooltip description for the craftable key. Yellow-tag Alt+E / Shift+Right-click here. Ctrl+C/V stay in interact hints only — do not repeat them here.");
			_keyCraftingStation = Sync.BindSynced<string>(config, "03 Key", "Key", "KeyCraftingStation", "", "Crafting station prefab name. Empty = craft anywhere.");
			_keyMaterials = Sync.BindSynced<string>(config, "03 Key", "Key", "KeyMaterials", "Wood:1", "Recipe requirements as Prefab:Amount pairs, separated by commas.");
			_teamLabelColor = config.Bind<string>("04 Display", "TeamLabelColor", "#FF00FF", new ConfigDescription("Local color for [Team] / Guests labels (hex like #FF00FF). Not synced.", (AcceptableValueBase)null, Array.Empty<object>()));
			_clearModifier = config.Bind<LockSmithModifier>("04 Display", "ClearModifier", LockSmithModifier.Alt, new ConfigDescription("Local: with key, this modifier+E clears LockSmith. Default Alt. Do not match SetupModifier (or your Join / AltPlace bind).", (AcceptableValueBase)null, Array.Empty<object>()));
			_setupModifier = config.Bind<LockSmithModifier>("04 Display", "SetupModifier", LockSmithModifier.Shift, new ConfigDescription("Local: with key, this modifier+E opens/closes Join. Default Shift so it stays clear of Alt+E Clear. Guest Leave / no-key public toggle still use the game AltPlace bind.", (AcceptableValueBase)null, Array.Empty<object>()));
			Sync.AddLockingConfigEntry(_lockSyncedConfig);
			Sync.FinalizeBinding(log, 20, (Func<bool>)(() => LockSyncedConfig));
		}

		private static string NormalizeHexColor(string? raw)
		{
			if (string.IsNullOrWhiteSpace(raw))
			{
				return "#FF00FF";
			}
			string text = raw.Trim();
			if (text.StartsWith("#", StringComparison.Ordinal))
			{
				text = text.Substring(1);
			}
			if (text.Length != 6)
			{
				return "#FF00FF";
			}
			string text2 = text;
			foreach (char c in text2)
			{
				if ((c < '0' || c > '9') && (c < 'a' || c > 'f') && (c < 'A' || c > 'F'))
				{
					return "#FF00FF";
				}
			}
			return "#" + text.ToUpperInvariant();
		}
	}
	public static class LockSmithLocalization
	{
		public const string KeyNameToken = "item_locksmith_key";

		public const string KeyDescToken = "item_locksmith_key_desc";

		public const string PiecePublicToken = "locksmith_piece_public";

		public const string PiecePrivateToken = "locksmith_piece_private";

		public const string PieceUnmanagedToken = "locksmith_piece_unmanaged";

		public const string PiecePersonalToken = "locksmith_piece_personal";

		public const string PieceTeamToken = "locksmith_piece_team";

		public const string PieceGuestsTagToken = "locksmith_piece_guests_tag";

		public const string PieceOptInReadyToken = "locksmith_piece_optin_ready";

		public const string MsgNowPublicToken = "locksmith_msg_now_public";

		public const string MsgNowPrivateToken = "locksmith_msg_now_private";

		public const string MsgManagedToken = "locksmith_msg_managed";

		public const string MsgNowPersonalToken = "locksmith_msg_now_personal";

		public const string MsgNowTeamToken = "locksmith_msg_now_team";

		public const string MsgDeniedToken = "locksmith_msg_denied";

		public const string MsgDisabledToken = "locksmith_msg_disabled";

		public const string MsgWrongTargetToken = "locksmith_msg_wrong_target";

		public const string MsgGroupOwnerOnlyToken = "locksmith_msg_group_owner_only";

		public const string MsgGroupMemberToken = "locksmith_msg_group_member";

		public const string MsgGroupNeedTeamToken = "locksmith_msg_group_need_team";

		public const string MsgOptInOpenedToken = "locksmith_msg_optin_opened";

		public const string MsgOptInClosedToken = "locksmith_msg_optin_closed";

		public const string MsgOptedInToken = "locksmith_msg_opted_in";

		public const string MsgOptedOutToken = "locksmith_msg_opted_out";

		public const string MsgLeaveConfirmToken = "locksmith_msg_leave_confirm";

		public const string MsgAlreadyOptedInToken = "locksmith_msg_already_opted_in";

		public const string MsgNoTeamAccessToken = "locksmith_msg_no_team_access";

		public const string MsgPublicNoSetupToken = "locksmith_msg_public_no_setup";

		public const string MsgClearConfirmToken = "locksmith_msg_clear_confirm";

		public const string MsgClearedToken = "locksmith_msg_cleared";

		public const string MsgNothingToClearToken = "locksmith_msg_nothing_to_clear";

		public const string MsgNeedActiveWardToken = "locksmith_msg_need_active_ward";

		public const string HoverMakePublicToken = "locksmith_hover_make_public";

		public const string HoverMakePrivateToken = "locksmith_hover_make_private";

		public const string HoverDesignateToken = "locksmith_hover_designate";

		public const string HoverClearLockSmithToken = "locksmith_hover_clear";

		public const string HoverMakePersonalToken = "locksmith_hover_make_personal";

		public const string HoverMakeTeamToken = "locksmith_hover_make_team";

		public const string HoverOpenOptInToken = "locksmith_hover_open_optin";

		public const string HoverCloseOptInToken = "locksmith_hover_close_optin";

		public const string HoverJoinAccessToken = "locksmith_hover_join_access";

		public const string HoverLeaveAccessToken = "locksmith_hover_leave_access";

		public const string HoverGuestsHeaderToken = "locksmith_hover_guests_header";

		public const string InventoryTabTitleToken = "locksmith_inventory_tab_title";

		public const string InventoryHintPhraseToken = "locksmith_inventory_hint_phrase";

		public const string PublicNameSuffixToken = "locksmith_public_name_suffix";

		public const string MsgPublicPrefabToken = "locksmith_msg_public_prefab";

		public const string PieceCategoryPublicToken = "locksmith_category_public";

		public static void Register()
		{
			CustomLocalization localization = LocalizationManager.Instance.GetLocalization();
			string value = (string.IsNullOrWhiteSpace(LockSmithConfig.KeyName) ? "Locksmith Key" : LockSmithConfig.KeyName.Trim());
			string value2 = (string.IsNullOrWhiteSpace(LockSmithConfig.KeyDescription) ? "Equip to designate a chest/door. After that, <color=#ffff00><b>Alt+E</b></color> toggles public/private if you have access. Key required for Team / Join setup. <color=#ffff00><b>Shift+Right-click</b></color> the key: Relabel, grab/pull names, clear, or clone." : LockSmithConfig.KeyDescription.Trim());
			string text = "English";
			localization.AddTranslation(ref text, new Dictionary<string, string>
			{
				{ "item_locksmith_key", value },
				{ "item_locksmith_key_desc", value2 },
				{ "locksmith_inventory_tab_title", "Lock" },
				{ "locksmith_inventory_hint_phrase", "configure lock tool" },
				{ "locksmith_piece_public", "[Public]" },
				{ "locksmith_piece_private", "[Private]" },
				{ "locksmith_piece_unmanaged", "[Not LockSmith]" },
				{ "locksmith_piece_personal", "[Personal]" },
				{ "locksmith_piece_team", "Team" },
				{ "locksmith_piece_guests_tag", "Guests" },
				{ "locksmith_piece_optin_ready", "[Join open]" },
				{ "locksmith_msg_now_public", "Now public" },
				{ "locksmith_msg_now_private", "Now private" },
				{ "locksmith_msg_managed", "LockSmith enabled on this piece" },
				{ "locksmith_msg_now_personal", "Chest is personal (team paused — names kept)" },
				{ "locksmith_msg_now_team", "Chest is team (shared)" },
				{ "locksmith_msg_denied", "Only ward members can change access" },
				{ "locksmith_msg_disabled", "Access is disabled on this server" },
				{ "locksmith_msg_wrong_target", "Hold the Locksmith key and look at a chest or door" },
				{ "locksmith_msg_group_owner_only", "Only the chest creator can change team access" },
				{ "locksmith_msg_group_member", "[Team]" },
				{ "locksmith_msg_group_need_team", "Switch the chest to Team first" },
				{ "locksmith_msg_optin_opened", "Join is open — others press E to opt in" },
				{ "locksmith_msg_optin_closed", "Join is closed" },
				{ "locksmith_msg_opted_in", "You joined access" },
				{ "locksmith_msg_opted_out", "You left access" },
				{ "locksmith_msg_leave_confirm", "YOU SURE??? Leave access — you can't rejoin unless the owner opens Join. Press leave again within 5s to confirm." },
				{ "locksmith_msg_already_opted_in", "You already have access" },
				{ "locksmith_msg_no_team_access", "No team access" },
				{ "locksmith_msg_public_no_setup", "Public pieces stay open — lock it private to change Join settings" },
				{ "locksmith_msg_clear_confirm", "YOU SURE??? This clears guest names. {0}+E again to confirm. (Got a backup key? — someday ;))" },
				{ "locksmith_msg_cleared", "LockSmith removed — piece is vanilla again" },
				{ "locksmith_msg_nothing_to_clear", "Nothing LockSmith to clear on this piece" },
				{ "locksmith_msg_need_active_ward", "Needs an active ward" },
				{ "locksmith_hover_make_public", "Make public" },
				{ "locksmith_hover_make_private", "Make private" },
				{ "locksmith_hover_designate", "Enable LockSmith" },
				{ "locksmith_hover_clear", "Clear LockSmith" },
				{ "locksmith_hover_make_personal", "Make personal" },
				{ "locksmith_hover_make_team", "Make team" },
				{ "locksmith_hover_open_optin", "Open join" },
				{ "locksmith_hover_close_optin", "Close join" },
				{ "locksmith_hover_join_access", "Join access" },
				{ "locksmith_hover_leave_access", "Leave access" },
				{ "locksmith_hover_guests_header", "Guests:" },
				{ "locksmith_public_name_suffix", " (public)" },
				{ "locksmith_msg_public_prefab", "This piece is always public" },
				{ "locksmith_category_public", "Public" }
			});
		}

		public static string T(string token)
		{
			return Localization.instance.Localize("$" + token);
		}

		public static string FormatGuestCount(int count)
		{
			return "Guests: " + count;
		}

		public static string FormatGuestsCount(int count)
		{
			return "Guests [" + count + "]";
		}
	}
	public static class GameHookTargets
	{
		public const string ContainerType = "Container";

		public const string DoorType = "Door";

		public const string ContainerAwake = "Awake";

		public const string ContainerInteract = "Interact";

		public const string ContainerGetHoverText = "GetHoverText";

		public const string ContainerTakeAll = "TakeAll";

		public const string ContainerUseItem = "UseItem";

		public const string ContainerCheckAccess = "CheckAccess";

		public const string DoorAwake = "Awake";

		public const string DoorInteract = "Interact";

		public const string DoorGetHoverText = "GetHoverText";

		public const string HumanoidRightItemField = "m_rightItem";

		public const string HumanoidLeftItemField = "m_leftItem";

		public const string RpcSetChestPublic = "RPC_LockSmithSetChestPublic";

		public const string RpcSetDoorPublic = "RPC_LockSmithSetDoorPublic";

		public const string RpcSetGroupMode = "RPC_LockSmithSetGroupMode";

		public const string RpcSetOptInReady = "RPC_LockSmithSetOptInReady";

		public const string RpcOptInSelf = "RPC_LockSmithOptInSelf";

		public const string RpcOptOutSelf = "RPC_LockSmithOptOutSelf";

		public const string RpcMergeGuests = "RPC_LockSmithMergeGuests";

		public const string RpcClearLockSmith = "RPC_LockSmithClear";

		public const string ZdoPublicFlag = "locksmith_public";

		public const string ZdoManagedFlag = "locksmith_managed";

		public const string ZdoGroupMode = "locksmith_group_mode";

		public const string ZdoGroupMembers = "locksmith_group_members";

		public const string ZdoOptInReady = "locksmith_optin";
	}
}
namespace LockSmith.UI
{
	public static class AccessFeedback
	{
		private static MethodInfo? _showMessage;

		private static MethodInfo? _characterMessage;

		private static bool _resolved;

		public static void Show(Humanoid? user, string localizationToken)
		{
			if (!((Object)(object)user == (Object)null) && !string.IsNullOrEmpty(localizationToken))
			{
				ShowRaw(user, LockSmithLocalization.T(localizationToken));
			}
		}

		public static void ShowRaw(Humanoid? user, string message)
		{
			if (string.IsNullOrEmpty(message))
			{
				return;
			}
			try
			{
				EnsureResolved();
				if ((!(_showMessage != null) || !((Object)(object)MessageHud.instance != (Object)null) || !TryInvokeHud(_showMessage, MessageHud.instance, message)) && (Object)(object)user != (Object)null && _characterMessage != null)
				{
					TryInvokeCharacter(_characterMessage, user, message);
				}
			}
			catch (Exception ex)
			{
				ManualLogSource? log = LockSmith.Log;
				if (log != null)
				{
					log.LogDebug((object)("AccessFeedback failed: " + Unwrap(ex).Message));
				}
			}
		}

		private static bool TryInvokeHud(MethodInfo method, object hud, string message)
		{
			try
			{
				ParameterInfo[] parameters = method.GetParameters();
				object[] array = new object[parameters.Length];
				array[0] = (object)(MessageType)2;
				array[1] = message;
				for (int i = 2; i < parameters.Length; i++)
				{
					Type parameterType = parameters[i].ParameterType;
					if (parameterType == typeof(int))
					{
						array[i] = 0;
					}
					else if (parameterType == typeof(bool))
					{
						array[i] = false;
					}
					else
					{
						array[i] = null;
					}
				}
				method.Invoke(hud, array);
				return true;
			}
			catch (Exception ex)
			{
				ManualLogSource? log = LockSmith.Log;
				if (log != null)
				{
					log.LogDebug((object)("AccessFeedback MessageHud invoke failed: " + Unwrap(ex).Message));
				}
				return false;
			}
		}

		private static void TryInvokeCharacter(MethodInfo method, Humanoid user, string message)
		{
			try
			{
				ParameterInfo[] parameters = method.GetParameters();
				if (parameters.Length >= 5)
				{
					method.Invoke(user, new object[5]
					{
						(object)(MessageType)2,
						message,
						0,
						null,
						false
					});
				}
				else if (parameters.Length >= 4)
				{
					method.Invoke(user, new object[4]
					{
						(object)(MessageType)2,
						message,
						0,
						null
					});
				}
			}
			catch (Exception ex)
			{
				ManualLogSource? log = LockSmith.Log;
				if (log != null)
				{
					log.LogDebug((object)("AccessFeedback Character.Message invoke failed: " + Unwrap(ex).Message));
				}
			}
		}

		private static void EnsureResolved()
		{
			if (_resolved)
			{
				return;
			}
			_resolved = true;
			Type type = RuntimeType("MessageHud") ?? typeof(MessageHud);
			Type type2 = RuntimeType("Character") ?? typeof(Character);
			Type type3 = RuntimeType("MessageHud+MessageType") ?? RuntimeType("MessageHud.MessageType") ?? typeof(MessageType);
			Type type4 = RuntimeType("UnityEngine.Sprite") ?? typeof(Sprite);
			_showMessage = AccessTools.Method(type, "ShowMessage", (Type[])null, (Type[])null) ?? FindMethodByName(type, "ShowMessage");
			_characterMessage = AccessTools.Method(type2, "Message", new Type[5]
			{
				type3,
				typeof(string),
				typeof(int),
				type4,
				typeof(bool)
			}, (Type[])null) ?? AccessTools.Method(type2, "Message", new Type[4]
			{
				type3,
				typeof(string),
				typeof(int),
				type4
			}, (Type[])null) ?? FindMethodByName(type2, "Message");
			if (_characterMessage != null && !IsCallableOnRuntimeCharacter(_characterMessage))
			{
				ManualLogSource? log = LockSmith.Log;
				if (log != null)
				{
					log.LogDebug((object)"AccessFeedback: dropping Character.Message MethodInfo that is not callable at runtime.");
				}
				_characterMessage = null;
			}
			if (_showMessage == null && _characterMessage == null)
			{
				ManualLogSource? log2 = LockSmith.Log;
				if (log2 != null)
				{
					log2.LogWarning((object)"AccessFeedback: no MessageHud/Character.Message overload resolved.");
				}
			}
		}

		private static Type? RuntimeType(string fullName)
		{
			Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
			foreach (Assembly assembly in assemblies)
			{
				try
				{
					string name = assembly.GetName().Name;
					if (!(name != "assembly_valheim") || !(name != "UnityEngine.CoreModule"))
					{
						Type type = assembly.GetType(fullName, throwOnError: false);
						if (type != null)
						{
							return type;
						}
					}
				}
				catch
				{
				}
			}
			return AccessTools.TypeByName(fullName);
		}

		private static MethodInfo? FindMethodByName(Type type, string name)
		{
			MethodInfo methodInfo = null;
			MethodInfo[] methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			foreach (MethodInfo methodInfo2 in methods)
			{
				if (!(methodInfo2 == null) && !(methodInfo2.Name != name))
				{
					int num = methodInfo2.GetParameters().Length;
					if (methodInfo == null || num > methodInfo.GetParameters().Length)
					{
						methodInfo = methodInfo2;
					}
				}
			}
			return methodInfo;
		}

		private static bool IsCallableOnRuntimeCharacter(MethodInfo method)
		{
			try
			{
				Type type = RuntimeType("Character");
				if (type == null)
				{
					return true;
				}
				Type declaringType = method.DeclaringType;
				if (declaringType == null)
				{
					return false;
				}
				if (declaringType == type || type.IsSubclassOf(declaringType) || declaringType.IsAssignableFrom(type))
				{
					ParameterInfo[] parameters = method.GetParameters();
					Type[] array = new Type[parameters.Length];
					for (int i = 0; i < parameters.Length; i++)
					{
						array[i] = parameters[i].ParameterType;
					}
					return type.GetMethod(method.Name, array) != null || AccessTools.Method(type, method.Name, array, (Type[])null) != null;
				}
				return false;
			}
			catch
			{
				return false;
			}
		}

		private static Exception Unwrap(Exception ex)
		{
			while (ex is TargetInvocationException)
			{
				Exception innerException = ex.InnerException;
				if (innerException == null)
				{
					break;
				}
				ex = innerException;
			}
			return ex;
		}
	}
	public static class KeyPassMenu
	{
		public const string TabId = "locksmith.keypass";

		private static readonly DrakeWoodActionMenu ActionMenu = new DrakeWoodActionMenu("locksmith_key_action_menu");

		private static readonly DrakeConfirmPanel Confirm = new DrakeConfirmPanel("locksmith_key_confirm");

		private static readonly DrakeTextPromptPanel RelabelPrompt = new DrakeTextPromptPanel("locksmith_key_relabel");

		private static bool _tabRegistered;

		public static bool IsOpen
		{
			get
			{
				if (!ActionMenu.IsOpen && !Confirm.IsOpen && !RelabelPrompt.IsOpen)
				{
					if (DrakeTabHost.IsOpen)
					{
						return DrakeTabHost.ActiveTabId == "locksmith.keypass";
					}
					return false;
				}
				return true;
			}
		}

		public static void RegisterTab()
		{
			if (_tabRegistered)
			{
				return;
			}
			_tabRegistered = true;
			try
			{
				DrakeTabHost.Register("locksmith.keypass", "Lock", 200, (Func<ItemData, bool>)((ItemData item) => LockSmithConfig.EnableKeyPasses && ChestAccessService.IsLocksmithKey(item) && IsItemInLocalInventory(item)), (Action<DrakeTabPageContext>)delegate(DrakeTabPageContext ctx)
				{
					Open(ctx.Item);
				}, (Func<ItemData, bool>)((ItemData item) => LockSmithConfig.EnableKeyPasses && ChestAccessService.IsLocksmithKey(item)), (Action)HideForHost, (Func<string>)(() => LockSmithLocalization.T("locksmith_inventory_hint_phrase")), (Func<string>)(() => LockSmithLocalization.T("locksmith_inventory_tab_title")));
			}
			catch (Exception ex)
			{
				_tabRegistered = false;
				ManualLogSource? log = LockSmith.Log;
				if (log != null)
				{
					log.LogError((object)("DrakeTabHost.Register failed (libs API mismatch?): " + ex.Message));
				}
			}
		}

		public static void CloseAll()
		{
			RelabelPrompt.Close();
			Confirm.Close();
			ActionMenu.CloseSilent();
		}

		private static void HideForHost()
		{
			RelabelPrompt.Close();
			Confirm.Close();
			ActionMenu.CloseSilent();
		}

		public static bool TryOpen(ItemData? item)
		{
			RegisterTab();
			return DrakeTabHost.OpenForItem(item, "locksmith.keypass", (Action)null);
		}

		private static void Open(ItemData item)
		{
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Expected O, but got Unknown
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Expected O, but got Unknown
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a5: Expected O, but got Unknown
			//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d1: Expected O, but got Unknown
			ChestAccessService.EnsureRenameHandOff(item);
			string text = CustomizeLibsAPI.GetDisplayNameForUi(item, true);
			if (string.IsNullOrEmpty(text))
			{
				text = LockSmithConfig.KeyName;
			}
			string text2 = KeyPassService.FormatMembershipSubtitle(item);
			List<DrakeMenuAction> list = new List<DrakeMenuAction>
			{
				new DrakeMenuAction("Relabel…", (Action)delegate
				{
					ActionMenu.CloseSilent();
					OpenRelabel(item);
				}, true),
				new DrakeMenuAction("Grab nearby person", (Action)delegate
				{
					ActionMenu.CloseSilent();
					KeyPassService.TryGrabNearby(item);
					Open(item);
				}, true),
				new DrakeMenuAction("Clear names…", (Action)delegate
				{
					ActionMenu.CloseSilent();
					Confirm.Show("Clear names?", "Remove guest/team names from this key?\n\nTip: Ctrl+C a door first if you need a backup.", (Action)delegate
					{
						KeyPassService.ClearPass(item);
						AccessFeedback.ShowRaw((Humanoid?)(object)Player.m_localPlayer, "Key names cleared.");
						Open(item);
					}, (Action)delegate
					{
						Open(item);
					}, "Yes", "No");
				}, KeyPassService.HasPassPayload(item)),
				new DrakeMenuAction("Clone key (" + LockSmithConfig.KeyMaterials + ")", (Action)delegate
				{
					ActionMenu.CloseSilent();
					if (KeyPassService.TryCloneKey(item, out string detail))
					{
						AccessFeedback.ShowRaw((Humanoid?)(object)Player.m_localPlayer, detail);
					}
					DrakeTabHost.NotifyFeatureClosed("locksmith.keypass");
				}, true)
			};
			ActionMenu.Open(text, text2, (IReadOnlyList<DrakeMenuAction>)list, "Close", (Action)delegate
			{
				DrakeTabHost.NotifyFeatureClosed("locksmith.keypass");
			}, true);
		}

		private static void OpenRelabel(ItemData item)
		{
			string labelBracket = KeyPassService.GetLabelBracket(item);
			RelabelPrompt.Show("Relabel", labelBracket, (Action<string>)delegate(string text)
			{
				KeyPassService.SetLabelBracket(item, text);
				Open(item);
			}, (Action)delegate
			{
				Open(item);
			}, 48, "Ok", "Cancel");
		}

		private static bool IsItemInLocalInventory(ItemData item)
		{
			Player localPlayer = Player.m_localPlayer;
			if (!Object.op_Implicit((Object)(object)localPlayer))
			{
				return false;
			}
			Inventory inventory = ((Humanoid)localPlayer).GetInventory();
			if (inventory == null)
			{
				return false;
			}
			foreach (ItemData allItem in inventory.GetAllItems())
			{
				if (allItem == item)
				{
					return true;
				}
			}
			return false;
		}
	}
}
namespace LockSmith.Patches
{
	[HarmonyPatch(typeof(Container))]
	public static class ContainerAccessPatches
	{
		private static bool _loggedHoverFault;

		private static bool _loggedInteractFault;

		private static bool _loggedCheckAccessFault;

		[HarmonyPostfix]
		[HarmonyPatch("Awake")]
		private static void AwakePostfix(Container __instance)
		{
			try
			{
				ChestAccessService.RegisterRpc(__instance);
				GroupChestService.RegisterRpc(__instance);
			}
			catch (Exception arg)
			{
				ManualLogSource? log = LockSmith.Log;
				if (log != null)
				{
					log.LogError((object)$"Container.Awake LockSmith RPC register failed: {arg}");
				}
			}
		}

		[HarmonyPrefix]
		[HarmonyPatch("CheckAccess")]
		private static bool CheckAccessPrefix(Container __instance, long playerID, ref bool __result)
		{
			try
			{
				if (!GroupChestService.TryResolveCheckAccess(__instance, playerID, out var allowed))
				{
					return true;
				}
				__result = allowed;
				return false;
			}
			catch (Exception arg)
			{
				if (!_loggedCheckAccessFault)
				{
					_loggedCheckAccessFault = true;
					ManualLogSource? log = LockSmith.Log;
					if (log != null)
					{
						log.LogError((object)$"Container.CheckAccess LockSmith prefix failed: {arg}");
					}
				}
				return true;
			}
		}

		[HarmonyPrefix]
		[HarmonyPatch("Interact")]
		private static bool InteractPrefix(Container __instance, Humanoid character, bool hold, bool alt, ref bool __result, ref bool __state)
		{
			__state = false;
			try
			{
				PieceAccessState.SyncGuardStoneFromZdo(__instance);
				GroupAccessState.SyncPrivacyFromZdo(__instance);
				if (ChestAccessService.IsHoldingLocksmithKey(character))
				{
					try
					{
						if (!hold)
						{
							if (PieceGuestService.TryHandleGuestKeyLeave(__instance, character))
							{
								__result = true;
								return false;
							}
							if (PieceGuestService.ShouldGuestKeyFallThroughOpen(__instance, character))
							{
								if ((ChestAccessService.ShouldBypassWardCheck(__instance) || GroupChestService.ShouldBypassWard(__instance)) && __instance.m_checkGuardStone)
								{
									__instance.m_checkGuardStone = false;
									__state = true;
								}
								return true;
							}
							if (GroupAccessState.IsPrivateFamilyChest(__instance))
							{
								GroupChestService.TryHandleKeyInteract(__instance, character, hold, alt);
							}
							else
							{
								ChestAccessService.TryKeyInteract(__instance, character, alt);
							}
						}
						__result = true;
						return false;
					}
					catch (Exception arg)
					{
						ManualLogSource? log = LockSmith.Log;
						if (log != null)
						{
							log.LogError((object)$"LockSmith chest key interact failed: {arg}");
						}
						return true;
					}
				}
				if (PieceGuestService.TryHandleGuestPublicToggle(__instance, character, hold, alt))
				{
					__result = true;
					return false;
				}
				if (GroupChestService.TryHandleOptInInteract(__instance, character, hold, alt) || PieceGuestService.TryHandleOptInInteract(__instance, character, hold, alt))
				{
					__result = true;
					return false;
				}
				if (!ChestAccessService.ShouldBypassWardCheck(__instance) && !GroupChestService.ShouldBypassWard(__instance))
				{
					return true;
				}
				if (!__instance.m_checkGuardStone)
				{
					return true;
				}
				__instance.m_checkGuardStone = false;
				__state = true;
			}
			catch (Exception arg2)
			{
				if (!_loggedInteractFault)
				{
					_loggedInteractFault = true;
					ManualLogSource? log2 = LockSmith.Log;
					if (log2 != null)
					{
						log2.LogError((object)$"Container.Interact LockSmith prefix failed: {arg2}");
					}
				}
			}
			return true;
		}

		[HarmonyPostfix]
		[HarmonyPatch("Interact")]
		private static void InteractPostfix(Container __instance, bool __state)
		{
			if (!__state)
			{
				return;
			}
			try