Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of LockSmith v0.5.0
LockSmith.dll
Decompiled a month ago
The result has been truncated due to the large size, download it to view full contents!
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.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using DrakeModsLibs.API; using DrakeModsLibs.Compat; 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.Compat; using LockSmith.Compat.ArcaneWard; using LockSmith.Compat.DevCommands; using LockSmith.Compat.ProtectiveWards; using LockSmith.Compat.Vanilla; using LockSmith.Compat.WardIsLove; 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; private static ObjectDB? _recipeDb; private static bool? _recipeEnabled; 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 void SyncKeyRecipe() { ObjectDB instance = ObjectDB.instance; if ((Object)(object)instance == (Object)null) { return; } bool useKey = LockSmithConfig.UseKey; if (instance == _recipeDb && _recipeEnabled == useKey) { return; } _recipeDb = instance; _recipeEnabled = useKey; foreach (Recipe recipe in instance.m_recipes) { if ((Object)(object)recipe != (Object)null && (Object)(object)recipe.m_item != (Object)null && ((Object)((Component)recipe.m_item).gameObject).name.Equals("masterkey", StringComparison.OrdinalIgnoreCase)) { recipe.m_enabled = useKey; } } } 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 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.EnableManagedAccess || !Object.op_Implicit((Object)(object)player) || (Object)(object)player != (Object)(object)Player.m_localPlayer || KeyPassMenu.IsOpen || PieceAccessMenu.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; private static readonly List<PieceGuest> SessionNames = new List<PieceGuest>(); public static bool HasSessionNames => SessionNames.Count > 0; public static int SessionNameCount => SessionNames.Count; 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."); } else { TryPullFromPiece(item, nview); } } public static void TryPullFromPiece(ItemData item, ZNetView nview) { 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) { if (!TryGetLookTargetNview(out ZNetView nview)) { AccessFeedback.ShowRaw((Humanoid?)(object)Player.m_localPlayer, "Look at a chest or door first."); } else { TryPasteOntoPiece(item, nview); } } public static void TryPasteOntoPiece(ItemData item, ZNetView nview) { Player localPlayer = Player.m_localPlayer; if (Object.op_Implicit((Object)(object)localPlayer)) { if (!HasPassPayload(item)) { AccessFeedback.ShowRaw((Humanoid?)(object)localPlayer, "Key has no names — copy a piece first."); } else { PasteGuests(nview, GetGuests(item), FormatMembershipSubtitle(item) ?? "names"); } } } public static void CopyToSession(ZNetView nview) { List<PieceGuest> guests = PieceGuestAccess.GetGuests(nview); if (guests.Count == 0) { AccessFeedback.ShowRaw((Humanoid?)(object)Player.m_localPlayer, "No guests on that piece."); return; } SessionNames.Clear(); SessionNames.AddRange(guests); AccessFeedback.ShowRaw((Humanoid?)(object)Player.m_localPlayer, "Copied " + guests.Count + " guest(s)"); } public static void PasteFromSession(ZNetView nview) { if (SessionNames.Count == 0) { AccessFeedback.ShowRaw((Humanoid?)(object)Player.m_localPlayer, "Nothing copied yet."); } else { PasteGuests(nview, new List<PieceGuest>(SessionNames), SessionNames.Count + " guest(s)"); } } private static void PasteGuests(ZNetView nview, List<PieceGuest> names, string label) { //IL_002c: Unknown result type (might be due to invalid IL or missing references) Player localPlayer = Player.m_localPlayer; if (!Object.op_Implicit((Object)(object)localPlayer) || PieceMenuLock.BlockIfHeldByOther(nview, (Humanoid?)(object)localPlayer)) { 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; } if (names.Count == 0) { AccessFeedback.ShowRaw((Humanoid?)(object)localPlayer, "Nothing to paste."); return; } PieceGuestAccess.RequestMergeGuests(nview, names, playerID, delegate { AccessFeedback.ShowRaw((Humanoid?)(object)Player.m_localPlayer, "Pasted " + label); }); } public static Player? FindNearestPlayer(Player from, Func<Player, bool> accept) { //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) Player result = null; float num = 5f; foreach (Player allPlayer in Player.GetAllPlayers()) { if (Object.op_Implicit((Object)(object)allPlayer) && !((Object)(object)allPlayer == (Object)(object)from) && accept(allPlayer)) { float num2 = Vector3.Distance(((Component)from).transform.position, ((Component)allPlayer).transform.position); if (!(num2 > num)) { num = num2; result = allPlayer; } } } return result; } public static void TryGrabNearby(ItemData item) { Player localPlayer = Player.m_localPlayer; if (Object.op_Implicit((Object)(object)localPlayer)) { Player val = FindNearestPlayer(localPlayer, (Player _) => true); 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 (LockSmithConfig.EnableKeyExtras) { 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.5.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [NetworkCompatibility(/*Could not decode attribute arguments.*/)] public class 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.5.0"; 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(); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Locksmith key is temporarily removed — simple mode (AltPlace+E)."); PrefabManager.OnVanillaPrefabsAvailable += OnVanillaPrefabs; _harmony.PatchAll(); CompatibilityManager.Initialize(_harmony); ((BaseUnityPlugin)this).Logger.LogInfo((object)"LockSmith 0.5.0 Awake."); } private void Update() { try { PieceRpc.Tick(); } catch (Exception arg) { ((BaseUnityPlugin)this).Logger.LogError((object)$"PieceRpc tick failed: {arg}"); PieceRpc.Clear(); } try { PieceMenuLock.Tick(); PieceAccessMenu.Tick(); ContentRegistration.SyncKeyRecipe(); } catch (Exception arg2) { ((BaseUnityPlugin)this).Logger.LogError((object)$"LockSmith menu/recipe tick failed: {arg2}"); PieceAccessMenu.Close(); } } private void OnVanillaPrefabs() { PrefabManager.OnVanillaPrefabsAvailable -= OnVanillaPrefabs; try { LockSmithLocalization.Register(); if (LockSmithConfig.UseKey) { ContentRegistration.FinalizeOfficialKeyFromKeysPack(); } PublicPieceRegistration.TryRegisterAll("OnVanillaPrefabsAvailable"); } catch (Exception arg) { ((BaseUnityPlugin)this).Logger.LogError((object)$"LockSmith content registration failed: {arg}"); } } } public static class LockSmithConfig { public const int ExpectedSyncedEntryCount = 25; 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 (advanced)"; private const string DisplayDisplay = "Display"; private static readonly DrakeConfigSync Sync = DrakeConfigSync.Create("LockSmith", "LockSmith", "0.5.0", (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> _enableManagedAccess = null; private static ConfigEntry<bool> _enablePieceMode = null; private static ConfigEntry<bool> _enableKeyPasses = null; private static ConfigEntry<bool> _enableKeyExtras = null; private static ConfigEntry<bool> _requireKeyForSetup = null; private static ConfigEntry<bool> _useKey = null; private static ConfigEntry<bool> _enableAddNearby = 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> _publicPieceHoverText = 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; 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 EnableManagedAccess => _enableManagedAccess.Value; public static bool EnablePieceMode => _enablePieceMode.Value; public static bool EnableKeyPasses => _enableKeyPasses.Value; public static bool EnableKeyExtras => _enableKeyExtras.Value; public static bool RequireKeyForSetup { get { if (UseKey) { return _requireKeyForSetup.Value; } return false; } } public static bool UseKey => false; public static bool EnableAddNearby => _enableAddNearby.Value; public static string PublicPieceAllowList => _publicPieceAllowList.Value; public static string PublicPieceDenyList => _publicPieceDenyList.Value; public static bool PublicPieceNameSuffix => _publicPieceNameSuffix.Value; public static string PublicPieceHoverText { get { string text = (_publicPieceHoverText.Value ?? string.Empty).Trim(); if (text.Length != 0) { return text; } return "[Public]"; } } 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 void Bind(ConfigFile config, ManualLogSource log) { //IL_03e9: Unknown result type (might be due to invalid IL or missing references) //IL_03f3: 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, private-chest owners get Make personal / Make team in the Lock menu: a pause switch that keeps the names but locks friends out while Personal. When false (default), chests stay team-shared."); _enablePieceGuests = Sync.BindSynced<bool>(config, "02 Features", "Features", "EnablePieceGuests", true, "Guests: players on a door's or chest's guest list can open it without being on the ward."); _enableOptInAccess = Sync.BindSynced<bool>(config, "02 Features", "Features", "EnableOptInAccess", true, "Join (ward-style opt-in): an owner uses Open Join in the Lock menu, then other players press AltPlace+E → Join access."); _enableGuestPublicToggle = Sync.BindSynced<bool>(config, "02 Features", "Features", "EnableGuestPublicToggle", true, "Ward members and guests get Make public / Make private in the Lock menu. Strangers never do. Not for private chests."); _enableDesignate = Sync.BindSynced<bool>(config, "02 Features", "Features", "EnableDesignate", true, "A ward member must press Enable LockSmith in the Lock menu before a piece gets the other options. Off = every eligible door and chest is LockSmith-ready straight away."); _enableManagedAccess = Sync.BindSynced<bool>(config, "02 Features", "Features", "EnableManagedAccess", true, "Master switch for managed (dynamic) access on placed doors and chests: the Lock menu, public/private, Join and guests, Team chests. Off = LockSmith leaves placed pieces alone (use EnablePieceMode for always-public Hammer pieces). Was EnableKeyMode; the old value carries over."); MigrateRenamedBool(config, "02 Features", "EnableKeyMode", _enableManagedAccess); _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, "Copy guests / Paste guests buttons in the Lock menu. Without the key, copied names last for the play session; with the key in hand they are saved on the key and Ctrl+C / Ctrl+V work."); _enableKeyExtras = Sync.BindSynced<bool>(config, "03 Key", "Key (advanced)", "EnableKeyExtras", false, "Advanced: extra tools in the key's inventory menu (Shift+Right-click): Relabel, Grab nearby person, Clear names, Clone key. Needs UseKey. Names on a key never grant access by themselves."); _useKey = Sync.BindSynced<bool>(config, "03 Key", "Key (advanced)", "UseKey", true, "(No effect for now: the key is temporarily removed and LockSmith runs in simple mode.) Advanced mode. The key is a physical item that gives finer control: holding it and pressing E opens the Lock menu, its hover shows guest names, copied guests are saved on the key (Ctrl+C / Ctrl+V), and RequireKeyForSetup / EnableKeyExtras apply. Off = simple mode: the key is never added to the game, hovers stay minimal, and everything goes through AltPlace+E. Restart required; keys already in inventories disappear while it is off."); _enableAddNearby = Sync.BindSynced<bool>(config, "02 Features", "Features", "EnableAddNearby", true, "Lock menu button that adds the closest player (within 5m) straight onto the guest list, so owners don't need Join for someone standing right there. Works with or without the key."); _requireKeyForSetup = Sync.BindSynced<bool>(config, "03 Key", "Key (advanced)", "RequireKeyForSetup", false, "Advanced: owners must hold the key for Enable LockSmith / Join / Personal-Team / Paste / Remove. Make public/private and Leave still work without it. Needs UseKey. Off (default) = everything works from AltPlace+E."); _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."); _publicPieceHoverText = Sync.BindSynced<string>(config, "05 PieceMode", "PieceMode", "PublicPieceHoverText", "[Public]", "Hover line on placed Public hammer doors and chests. Plain text, no color. Default [Public]. Empty uses [Public]."); _keyName = Sync.BindSynced<string>(config, "03 Key", "Key (advanced)", "KeyName", "Locksmith Key", "Display name for the craftable key."); _keyDescription = Sync.BindSynced<string>(config, "03 Key", "Key (advanced)", "KeyDescription", "Optional. Hold it and press <color=#ffff00><b>E</b></color> on a chest or door to open the Lock menu (Shift+E works without it). Copied guest names are saved on the key.", "Tooltip description for the craftable key."); _keyCraftingStation = Sync.BindSynced<string>(config, "03 Key", "Key (advanced)", "KeyCraftingStation", "", "Crafting station prefab name. Empty = craft anywhere."); _keyMaterials = Sync.BindSynced<string>(config, "03 Key", "Key (advanced)", "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>())); Sync.AddLockingConfigEntry(_lockSyncedConfig); Sync.FinalizeBinding(log, 25, (Func<bool>)(() => LockSyncedConfig)); } private static void MigrateRenamedBool(ConfigFile config, string section, string oldKey, ConfigEntry<bool> entry) { //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Expected O, but got Unknown try { Dictionary<ConfigDefinition, string> dictionary = AccessTools.Property(typeof(ConfigFile), "OrphanedEntries")?.GetValue(config) as Dictionary<ConfigDefinition, string>; ConfigDefinition key = new ConfigDefinition(section, oldKey); if (dictionary != null && dictionary.TryGetValue(key, out var value)) { if (bool.TryParse(value, out var result)) { entry.Value = result; } dictionary.Remove(key); config.Save(); } } catch (Exception ex) { ManualLogSource? log = LockSmith.Log; if (log != null) { log.LogDebug((object)("Config migrate " + oldKey + " skipped: " + ex.Message)); } } } 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 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 MsgAlreadyOptedInToken = "locksmith_msg_already_opted_in"; public const string MsgNoTeamAccessToken = "locksmith_msg_no_team_access"; public const string MsgClearedToken = "locksmith_msg_cleared"; public const string MsgNeedActiveWardToken = "locksmith_msg_need_active_ward"; public const string MsgSyncFailedToken = "locksmith_msg_sync_failed"; public const string MsgJoinFailedToken = "locksmith_msg_join_failed"; 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 HoverLockMenuToken = "locksmith_hover_lock_menu"; 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 PublicPieceDescToken = "locksmith_public_piece_desc"; public const string MsgPublicPrefabToken = "locksmith_msg_public_prefab"; public const string PieceCategoryPublicToken = "locksmith_category_public"; public const string DefaultKeyDescription = "Optional. Hold it and press <color=#ffff00><b>E</b></color> on a chest or door to open the Lock menu (Shift+E works without it). Copied guest names are saved on the key."; private const string LegacyKeyDescription = "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."; 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) || LockSmithConfig.KeyDescription.Trim() == "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.") ? "Optional. Hold it and press <color=#ffff00><b>E</b></color> on a chest or door to open the Lock menu (Shift+E works without it). Copied guest names are saved on the key." : 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 on" }, { "locksmith_msg_now_personal", "Now personal" }, { "locksmith_msg_now_team", "Now team (shared)" }, { "locksmith_msg_denied", "Ward members only" }, { "locksmith_msg_disabled", "Turned off on this server" }, { "locksmith_msg_wrong_target", "Look at a chest or door" }, { "locksmith_msg_group_owner_only", "Only the owner can change this" }, { "locksmith_msg_group_member", "[Team]" }, { "locksmith_msg_optin_opened", "Join open — others press E" }, { "locksmith_msg_optin_closed", "Join closed" }, { "locksmith_msg_opted_in", "You have access" }, { "locksmith_msg_opted_out", "You left" }, { "locksmith_msg_already_opted_in", "You already have access" }, { "locksmith_msg_no_team_access", "No team access" }, { "locksmith_msg_cleared", "LockSmith removed" }, { "locksmith_msg_need_active_ward", "Needs an active ward" }, { "locksmith_msg_sync_failed", "Didn't go through — try again" }, { "locksmith_msg_join_failed", "Couldn't join — try again" }, { "locksmith_hover_make_public", "Make public" }, { "locksmith_hover_make_private", "Make private" }, { "locksmith_hover_designate", "Enable LockSmith" }, { "locksmith_hover_clear", "Remove 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_lock_menu", "Lock menu" }, { "locksmith_hover_guests_header", "Guests:" }, { "locksmith_public_name_suffix", " (public)" }, { "locksmith_public_piece_desc", "<color=#FFCC33>Always open to anyone. This cannot be locked.</color>" }, { "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 RpcMenuRequest = "RPC_LockSmithMenuRequest"; public const string RpcMenuResponse = "RPC_LockSmithMenuResponse"; public const string RpcMenuRelease = "RPC_LockSmithMenuRelease"; public const string ZdoMenuHolder = "locksmith_menu_holder"; public const string ZdoMenuHolderName = "locksmith_menu_holder_name"; public const string ZdoMenuUntil = "locksmith_menu_until"; 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", (DrakePanelOptions)null); private static readonly DrakeConfirmPanel Confirm = new DrakeConfirmPanel("locksmith_key_confirm", (DrakePanelOptions)null); 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.UseKey && LockSmithConfig.EnableKeyExtras && ChestAccessService.IsLocksmithKey(item) && IsItemInLocalInventory(item)), (Action<DrakeTabPageContext>)delegate(DrakeTabPageContext ctx) { Open(ctx.Item); }, (Func<ItemData, bool>)((ItemData item) => LockSmithConfig.UseKey && LockSmithConfig.EnableKeyExtras && 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 fals