Decompiled source of DoorControl v0.9.0

BepInEx/plugins/DoorControl/DoorControl.dll

Decompiled a week ago
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using Bounce.Unmanaged;
using HarmonyLib;
using RPCPlugin.RPC;
using Talespire;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: AssemblyVersion("0.0.0.0")]
namespace TaleSpireDoorControl;

[BepInDependency(/*Could not decode attribute arguments.*/)]
[BepInPlugin("com.talespire.doorcontrol", "Door Control", "0.9.0")]
[BepInDependency(/*Could not decode attribute arguments.*/)]
public sealed class DoorControlPlugin : BaseUnityPlugin
{
	[HarmonyPatch(typeof(PlaceableHandle), "Interact")]
	internal static class PlaceableHandleInteractPatch
	{
		private static void Prefix(ref PlaceableHandle __instance)
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			InteractionCount++;
			CandidateDoorKey = null;
			CandidateContentId = null;
			DecoratedCurrentMenu = false;
			if (!__instance.HasValue)
			{
				return;
			}
			try
			{
				CandidateContentId = ((object)__instance.ContentId/*cast due to .constrained prefix*/).ToString();
				CandidateDoorKey = CandidateContentId;
				EnsureDoorDataLoadedForCurrentBoard();
				RequestBoardSyncIfNeeded();
				if (DebugMode != null && DebugMode.Value)
				{
					Log.LogInfo((object)("PlaceableHandle.Interact armed radial context (#" + InteractionCount + "). ContentId=" + CandidateContentId + " | GM=" + LocalClient.IsInGmMode + "."));
				}
			}
			catch (Exception ex)
			{
				CandidateDoorKey = null;
				CandidateContentId = null;
				Log.LogError((object)("Failed arming placeable radial context: " + ex));
			}
		}
	}

	[HarmonyPatch]
	internal static class MapMenuAddItemPatch
	{
		private static IEnumerable<MethodBase> TargetMethods()
		{
			MethodInfo[] methods = typeof(MapMenu).GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			foreach (MethodInfo method in methods)
			{
				if (!(method.Name != "AddItem") && typeof(MapMenuItem).IsAssignableFrom(method.ReturnType))
				{
					yield return method;
				}
			}
		}

		private static void Prefix(MethodBase __originalMethod, object[] __args, ref string __state)
		{
			__state = null;
			if (InjectingDoorControl || string.IsNullOrEmpty(CandidateDoorKey))
			{
				return;
			}
			try
			{
				string addItemTitle = GetAddItemTitle(__originalMethod, __args);
				if (!string.Equals(addItemTitle, "Open", StringComparison.OrdinalIgnoreCase) && !string.Equals(addItemTitle, "Close", StringComparison.OrdinalIgnoreCase))
				{
					return;
				}
				__state = addItemTitle;
				NativeDoorMenuCount++;
				EnsureDoorDataLoadedForCurrentBoard();
				DoorRecord orCreateDoorRecord = GetOrCreateDoorRecord(CandidateDoorKey);
				bool isLocked = orCreateDoorRecord.IsLocked;
				bool flag = !orCreateDoorRecord.IsDestroyed && (isLocked || orCreateDoorRecord.Obstruction != DoorObstructionState.Normal);
				if (DebugMode != null && DebugMode.Value)
				{
					Log.LogInfo((object)string.Concat("Native door radial item (#", NativeDoorMenuCount, "): ", addItemTitle, " | DoorId=", CandidateDoorKey, " | Locked=", isLocked, " | Obstruction=", orCreateDoorRecord.Obstruction, " | GM=", LocalClient.IsInGmMode, "."));
				}
				if (!flag || !string.Equals(addItemTitle, "Open", StringComparison.OrdinalIgnoreCase))
				{
					return;
				}
				string blockedDoorId = CandidateDoorKey;
				Action<MapMenuItem, object> replacement = delegate
				{
					DoorRecord orCreateDoorRecord2 = GetOrCreateDoorRecord(blockedDoorId);
					string text = ((orCreateDoorRecord2.Obstruction == DoorObstructionState.Barricaded) ? "Barricaded" : ((orCreateDoorRecord2.Obstruction == DoorObstructionState.Jammed) ? "Jammed" : "Locked"));
					if ((Object)(object)Instance != (Object)null)
					{
						Instance.ShowToast("The door is " + text.ToLowerInvariant() + ".");
					}
					Log.LogInfo((object)("Blocked native Open. DoorId=" + blockedDoorId + " | State=" + text + "."));
				};
				ReplaceAddItemAction(__originalMethod, __args, replacement);
				if (RenameLockedOpenAction != null && RenameLockedOpenAction.Value)
				{
					string replacementTitle = ((orCreateDoorRecord.Obstruction == DoorObstructionState.Barricaded) ? "Barricaded" : ((orCreateDoorRecord.Obstruction == DoorObstructionState.Jammed) ? "Jammed" : "Locked"));
					ReplaceAddItemTitle(__originalMethod, __args, replacementTitle);
				}
			}
			catch (Exception ex)
			{
				Log.LogError((object)("MapMenu.AddItem prefix failed: " + ex));
			}
		}

		private static void Postfix(MapMenu __instance, string __state)
		{
			//IL_043a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0441: Expected O, but got Unknown
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Expected O, but got Unknown
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Expected O, but got Unknown
			//IL_048c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0493: Expected O, but got Unknown
			//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_0100: Expected O, but got Unknown
			//IL_030f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0316: Expected O, but got Unknown
			//IL_019a: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a1: Expected O, but got Unknown
			//IL_014b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0152: Expected O, but got Unknown
			//IL_0399: Unknown result type (might be due to invalid IL or missing references)
			//IL_03a0: Expected O, but got Unknown
			//IL_035a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0361: Expected O, but got Unknown
			//IL_03f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_03fb: Expected O, but got Unknown
			//IL_023a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0241: Expected O, but got Unknown
			//IL_01f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fb: Expected O, but got Unknown
			//IL_02c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_02cb: Expected O, but got Unknown
			//IL_0285: Unknown result type (might be due to invalid IL or missing references)
			//IL_028c: Expected O, but got Unknown
			if (InjectingDoorControl || string.IsNullOrEmpty(__state) || DecoratedCurrentMenu || string.IsNullOrEmpty(CandidateDoorKey))
			{
				return;
			}
			DoorContext doorContext = BuildDoorContext(CandidateDoorKey, CandidateContentId, __state);
			try
			{
				InjectingDoorControl = true;
				if (string.Equals(__state, "Open", StringComparison.OrdinalIgnoreCase))
				{
					if (doorContext.IsDestroyed)
					{
						ItemArgs val = new ItemArgs();
						val.Action = DestroyedStatusFromMenu;
						val.Title = "Destroyed";
						val.Obj = doorContext;
						val.CloseMenuOnActivate = true;
						__instance.AddItem(val);
						if (LocalClient.IsInGmMode)
						{
							ItemArgs val2 = new ItemArgs();
							val2.Action = RestoreDoorFromMenu;
							val2.Title = "Restore Door";
							val2.Obj = doorContext;
							val2.CloseMenuOnActivate = true;
							__instance.AddItem(val2);
						}
					}
					else if (doorContext.Obstruction == DoorObstructionState.Jammed)
					{
						if (LocalClient.IsInGmMode || DoorRulesBridge.HasCapability("clear-jam"))
						{
							ItemArgs val3 = new ItemArgs();
							val3.Action = ClearJamFromMenu;
							val3.Title = "Clear Jam";
							val3.Obj = doorContext;
							val3.CloseMenuOnActivate = true;
							__instance.AddItem(val3);
						}
					}
					else if (doorContext.Obstruction == DoorObstructionState.Barricaded)
					{
						if (LocalClient.IsInGmMode)
						{
							ItemArgs val4 = new ItemArgs();
							val4.Action = ClearBarricadeFromMenu;
							val4.Title = "Clear Barricade";
							val4.Obj = doorContext;
							val4.CloseMenuOnActivate = true;
							__instance.AddItem(val4);
						}
					}
					else if (doorContext.IsLocked)
					{
						ItemArgs val5 = new ItemArgs();
						val5.Action = PickLockFromMenu;
						val5.Title = "Pick Lock";
						val5.Obj = doorContext;
						val5.CloseMenuOnActivate = true;
						__instance.AddItem(val5);
						if (doorContext.HasKeyCode && (DoorRulesBridge.HasCapability("key") || DoorControlApi.HasKeyProvider))
						{
							ItemArgs val6 = new ItemArgs();
							val6.Action = TryKeyFromMenu;
							val6.Title = "Try Key";
							val6.Obj = doorContext;
							val6.CloseMenuOnActivate = true;
							__instance.AddItem(val6);
						}
						if (LocalClient.IsInGmMode)
						{
							ItemArgs val7 = new ItemArgs();
							val7.Action = UnlockDoorFromMenu;
							val7.Title = "Unlock";
							val7.Obj = doorContext;
							val7.CloseMenuOnActivate = true;
							__instance.AddItem(val7);
						}
						if (DoorRulesBridge.HasCapability("jam"))
						{
							ItemArgs val8 = new ItemArgs();
							val8.Action = JamDoorFromMenu;
							val8.Title = "Jam";
							val8.Obj = doorContext;
							val8.CloseMenuOnActivate = true;
							__instance.AddItem(val8);
						}
						ItemArgs val9 = new ItemArgs();
						val9.Action = BarricadeDoorFromMenu;
						val9.Title = "Barricade";
						val9.Obj = doorContext;
						val9.CloseMenuOnActivate = true;
						__instance.AddItem(val9);
						AddRegisteredLockedDoorActions(__instance, doorContext);
					}
					else
					{
						ItemArgs val10 = new ItemArgs();
						val10.Action = LockDoorFromMenu;
						val10.Title = "Lock";
						val10.Obj = doorContext;
						val10.CloseMenuOnActivate = true;
						__instance.AddItem(val10);
						if (DoorRulesBridge.HasCapability("jam"))
						{
							ItemArgs val11 = new ItemArgs();
							val11.Action = JamDoorFromMenu;
							val11.Title = "Jam";
							val11.Obj = doorContext;
							val11.CloseMenuOnActivate = true;
							__instance.AddItem(val11);
						}
						ItemArgs val12 = new ItemArgs();
						val12.Action = BarricadeDoorFromMenu;
						val12.Title = "Barricade";
						val12.Obj = doorContext;
						val12.CloseMenuOnActivate = true;
						__instance.AddItem(val12);
					}
					if (!doorContext.IsDestroyed && doorContext.HealthConfigured && DoorRulesBridge.HasCapability("door-damage"))
					{
						ItemArgs val13 = new ItemArgs();
						val13.Action = AttackDoorFromMenu;
						val13.Title = "Attack Door";
						val13.Obj = doorContext;
						val13.CloseMenuOnActivate = true;
						__instance.AddItem(val13);
					}
				}
				if (LocalClient.IsInGmMode)
				{
					ItemArgs val14 = new ItemArgs();
					val14.Action = DoorPropertiesFromMenu;
					val14.Title = "Door Properties";
					val14.Obj = doorContext;
					val14.CloseMenuOnActivate = true;
					__instance.AddItem(val14);
				}
				if (DebugMode != null && DebugMode.Value)
				{
					ItemArgs val15 = new ItemArgs();
					val15.Action = InspectDoor;
					val15.Title = "Inspect Door";
					val15.Obj = doorContext;
					val15.CloseMenuOnActivate = true;
					__instance.AddItem(val15);
				}
				DecoratedCurrentMenu = true;
			}
			catch (Exception ex)
			{
				Log.LogError((object)("Door radial injection failed: " + ex));
			}
			finally
			{
				InjectingDoorControl = false;
				CandidateDoorKey = null;
				CandidateContentId = null;
			}
		}
	}

	public const string Guid = "com.talespire.doorcontrol";

	public const string Name = "Door Control";

	public const string Version = "0.9.0";

	internal const string RpcPrefix = "/doorcontrol/v2|";

	internal static DoorControlPlugin Instance;

	internal static ManualLogSource Log;

	internal static ConfigEntry<bool> DebugMode;

	internal static ConfigEntry<bool> RenameLockedOpenAction;

	internal static ConfigEntry<string> DifficultyTerm;

	internal static ConfigEntry<int> LastLockDifficulty;

	internal static ConfigEntry<bool> ManualCheckFallback;

	internal static ConfigEntry<int> LastDoorSDP;

	internal static ConfigEntry<int> LastDoorSP;

	internal static readonly Dictionary<string, DoorRecord> DoorRecords = new Dictionary<string, DoorRecord>(StringComparer.Ordinal);

	internal static string LoadedBoardId = null;

	internal static string CandidateDoorKey = null;

	internal static string CandidateContentId = null;

	internal static bool DecoratedCurrentMenu = false;

	internal static bool InjectingDoorControl = false;

	internal static int InteractionCount = 0;

	internal static int NativeDoorMenuCount = 0;

	internal static string SyncRequestedBoardId = null;

	private Harmony _harmony;

	private Rect _lockWindowRect = new Rect(0f, 0f, 390f, 245f);

	private Rect _checkWindowRect = new Rect(0f, 0f, 360f, 190f);

	private Rect _doorPropsWindowRect = new Rect(0f, 0f, 410f, 315f);

	private LockDialogState _lockDialog;

	private ManualCheckDialogState _checkDialog;

	private DoorPropertiesDialogState _doorPropsDialog;

	private string _toastText;

	private float _toastUntil;

	private void Awake()
	{
		//IL_018d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0197: Expected O, but got Unknown
		Instance = this;
		Log = ((BaseUnityPlugin)this).Logger;
		DebugMode = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "DebugMode", false, "Show Inspect Door and verbose diagnostics.");
		RenameLockedOpenAction = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "RenameLockedOpenAction", true, "Rename the native Open action to Locked while a door is locked.");
		DifficultyTerm = ((BaseUnityPlugin)this).Config.Bind<string>("Rules", "DifficultyTerm", "DV", "Label used for lock difficulty, e.g. DV, DC, TN, Difficulty, or Target.");
		LastLockDifficulty = ((BaseUnityPlugin)this).Config.Bind<int>("Rules", "LastLockDifficulty", 15, "Default/prefilled difficulty when configuring a new lock.");
		ManualCheckFallback = ((BaseUnityPlugin)this).Config.Bind<bool>("Rules", "ManualCheckFallback", true, "If no external check provider handles Pick Lock, allow the user to enter a check total manually.");
		LastDoorSDP = ((BaseUnityPlugin)this).Config.Bind<int>("Structure", "LastDoorSDP", 20, "Prefilled maximum SDP when configuring structural door health.");
		LastDoorSP = ((BaseUnityPlugin)this).Config.Bind<int>("Structure", "LastDoorSP", 0, "Prefilled maximum SP when configuring structural door health.");
		TryMigrateLegacyStateDirectory();
		Log.LogInfo((object)"Door Control 0.9.0 loaded after RadialUI and RPCPlugin.");
		Log.LogInfo((object)"Generic locks, obstruction states, structural SP/SDP, GM-authoritative checks, legacy-conflict protection, and rules-client integration enabled.");
		try
		{
			RPCManager.AddHandler("/doorcontrol/v2|", (Func<string, string, SourceRole, string>)HandleRpcMessage);
			Log.LogInfo((object)"Registered Door Control RPC handler: /doorcontrol/v2|");
		}
		catch (Exception ex)
		{
			Log.LogError((object)("Failed registering Door Control RPC handler: " + ex));
		}
		try
		{
			DoorRulesBridge.Start();
			Log.LogInfo((object)"Rules integration bridge listening on 127.0.0.1:31749.");
		}
		catch (Exception ex2)
		{
			Log.LogError((object)("Failed starting rules integration bridge: " + ex2));
		}
		try
		{
			_harmony = new Harmony("com.talespire.doorcontrol");
			try
			{
				Harmony.UnpatchID("com.cyberpunkpink.talespire.doorcontrol");
				Log.LogInfo((object)"Cleared legacy Cyberpunk PINK Door Control Harmony patches if present.");
			}
			catch (Exception ex3)
			{
				Log.LogWarning((object)("Could not clear legacy Door Control patches: " + ex3.Message));
			}
			_harmony.PatchAll();
			Log.LogInfo((object)"Patched PlaceableHandle.Interact and MapMenu.AddItem overloads.");
		}
		catch (Exception ex4)
		{
			Log.LogError((object)("Failed to install Door Control patches: " + ex4));
		}
	}

	private void OnDestroy()
	{
		try
		{
			DoorRulesBridge.Stop();
		}
		catch
		{
		}
		try
		{
			RPCManager.RemoveHandler("/doorcontrol/v2|");
		}
		catch
		{
		}
		try
		{
			if (_harmony != null)
			{
				_harmony.UnpatchSelf();
			}
		}
		catch
		{
		}
		Instance = null;
	}

	private void Update()
	{
		DoorRulesBridge.PumpMainThread();
	}

	private void OnGUI()
	{
		//IL_007c: 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_020c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0218: Unknown result type (might be due to invalid IL or missing references)
		//IL_022d: Expected O, but got Unknown
		//IL_0228: Unknown result type (might be due to invalid IL or missing references)
		//IL_022d: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b1: Expected O, but got Unknown
		//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
		//IL_026e: 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_018b: Expected O, but got Unknown
		//IL_0186: Unknown result type (might be due to invalid IL or missing references)
		//IL_018b: Unknown result type (might be due to invalid IL or missing references)
		if (_lockDialog != null)
		{
			if (((Rect)(ref _lockWindowRect)).x <= 0f && ((Rect)(ref _lockWindowRect)).y <= 0f)
			{
				((Rect)(ref _lockWindowRect)).x = ((float)Screen.width - ((Rect)(ref _lockWindowRect)).width) * 0.5f;
				((Rect)(ref _lockWindowRect)).y = ((float)Screen.height - ((Rect)(ref _lockWindowRect)).height) * 0.5f;
			}
			_lockWindowRect = GUILayout.Window(654310, _lockWindowRect, new WindowFunction(DrawLockDialog), _lockDialog.IsRemoteRequest ? "Lock Door Request" : "Configure Lock", (GUILayoutOption[])(object)new GUILayoutOption[0]);
		}
		if (_checkDialog != null)
		{
			if (((Rect)(ref _checkWindowRect)).x <= 0f && ((Rect)(ref _checkWindowRect)).y <= 0f)
			{
				((Rect)(ref _checkWindowRect)).x = ((float)Screen.width - ((Rect)(ref _checkWindowRect)).width) * 0.5f;
				((Rect)(ref _checkWindowRect)).y = ((float)Screen.height - ((Rect)(ref _checkWindowRect)).height) * 0.5f;
			}
			string text = ((_checkDialog.Request == null || string.IsNullOrEmpty(_checkDialog.Request.DisplayName)) ? "Door Check" : _checkDialog.Request.DisplayName);
			_checkWindowRect = GUILayout.Window(654311, _checkWindowRect, new WindowFunction(DrawManualCheckDialog), text, (GUILayoutOption[])(object)new GUILayoutOption[0]);
		}
		if (_doorPropsDialog != null)
		{
			if (((Rect)(ref _doorPropsWindowRect)).x <= 0f && ((Rect)(ref _doorPropsWindowRect)).y <= 0f)
			{
				((Rect)(ref _doorPropsWindowRect)).x = ((float)Screen.width - ((Rect)(ref _doorPropsWindowRect)).width) * 0.5f;
				((Rect)(ref _doorPropsWindowRect)).y = ((float)Screen.height - ((Rect)(ref _doorPropsWindowRect)).height) * 0.5f;
			}
			_doorPropsWindowRect = GUILayout.Window(654312, _doorPropsWindowRect, new WindowFunction(DrawDoorPropertiesDialog), "Door Properties", (GUILayoutOption[])(object)new GUILayoutOption[0]);
		}
		if (!string.IsNullOrEmpty(_toastText) && Time.realtimeSinceStartup < _toastUntil)
		{
			Rect val = default(Rect);
			((Rect)(ref val))..ctor(20f, (float)Screen.height - 75f, 520f, 48f);
			GUI.Box(val, _toastText);
		}
	}

	private void DrawLockDialog(int id)
	{
		LockDialogState lockDialog = _lockDialog;
		if (lockDialog == null)
		{
			return;
		}
		if (lockDialog.IsRemoteRequest)
		{
			GUILayout.Label("A player requested that this door be locked.", (GUILayoutOption[])(object)new GUILayoutOption[0]);
			if (!string.IsNullOrEmpty(lockDialog.Requester))
			{
				GUILayout.Label("Requester: " + lockDialog.Requester, (GUILayoutOption[])(object)new GUILayoutOption[0]);
			}
		}
		else
		{
			GUILayout.Label("Configure this door's lock.", (GUILayoutOption[])(object)new GUILayoutOption[0]);
		}
		GUILayout.Space(6f);
		GUILayout.Label("Lock " + GetDifficultyTerm() + ":", (GUILayoutOption[])(object)new GUILayoutOption[0]);
		lockDialog.DifficultyText = GUILayout.TextField((lockDialog.DifficultyText == null) ? "" : lockDialog.DifficultyText, 4, (GUILayoutOption[])(object)new GUILayoutOption[0]);
		GUILayout.Label("Key Code (optional):", (GUILayoutOption[])(object)new GUILayoutOption[0]);
		lockDialog.KeyCode = GUILayout.TextField((lockDialog.KeyCode == null) ? "" : lockDialog.KeyCode, 96, (GUILayoutOption[])(object)new GUILayoutOption[0]);
		GUILayout.Label("The key code is stored on the door for ruleset integrations such as key cards or door keys.", (GUILayoutOption[])(object)new GUILayoutOption[0]);
		GUILayout.FlexibleSpace();
		GUILayout.BeginHorizontal((GUILayoutOption[])(object)new GUILayoutOption[0]);
		if (GUILayout.Button(lockDialog.IsRemoteRequest ? "Approve" : "Lock", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(30f) }))
		{
			if (!int.TryParse(lockDialog.DifficultyText, out var result) || result < 1 || result > 999)
			{
				ShowToast("Enter a " + GetDifficultyTerm() + " from 1 to 999.");
			}
			else
			{
				LastLockDifficulty.Value = result;
				ConfigureDoorLockInternal(lockDialog.DoorId, locked: true, result, lockDialog.KeyCode, broadcast: true);
				_lockDialog = null;
				ShowToast("The door is now locked.");
			}
		}
		if (GUILayout.Button(lockDialog.IsRemoteRequest ? "Deny" : "Cancel", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(30f) }))
		{
			if (lockDialog.IsRemoteRequest && !string.IsNullOrEmpty(lockDialog.Requester))
			{
				SendRpc(BuildDenyMessage(lockDialog.BoardId, lockDialog.DoorId, lockDialog.Requester));
			}
			_lockDialog = null;
		}
		GUILayout.EndHorizontal();
		GUI.DragWindow();
	}

	private void DrawManualCheckDialog(int id)
	{
		ManualCheckDialogState checkDialog = _checkDialog;
		if (checkDialog == null || checkDialog.Request == null)
		{
			return;
		}
		GUILayout.Label(checkDialog.Request.DisplayName + " — " + checkDialog.Request.DifficultyTerm + " " + checkDialog.Request.Difficulty, (GUILayoutOption[])(object)new GUILayoutOption[0]);
		GUILayout.Label("Enter the final check total:", (GUILayoutOption[])(object)new GUILayoutOption[0]);
		checkDialog.TotalText = GUILayout.TextField((checkDialog.TotalText == null) ? "" : checkDialog.TotalText, 5, (GUILayoutOption[])(object)new GUILayoutOption[0]);
		GUILayout.FlexibleSpace();
		GUILayout.BeginHorizontal((GUILayoutOption[])(object)new GUILayoutOption[0]);
		if (GUILayout.Button("Submit", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(30f) }))
		{
			if (!int.TryParse(checkDialog.TotalText, out var result))
			{
				ShowToast("Enter a numeric check total.");
			}
			else
			{
				_checkDialog = null;
				DoorControlApi.SubmitCheckResult(checkDialog.Request.RequestId, result);
			}
		}
		if (GUILayout.Button("Cancel", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(30f) }))
		{
			DoorControlApi.CancelCheck(checkDialog.Request.RequestId);
			_checkDialog = null;
		}
		GUILayout.EndHorizontal();
		GUI.DragWindow();
	}

	private void DrawDoorPropertiesDialog(int id)
	{
		DoorPropertiesDialogState doorPropsDialog = _doorPropsDialog;
		if (doorPropsDialog == null)
		{
			return;
		}
		GUILayout.Label("Structural Health", (GUILayoutOption[])(object)new GUILayoutOption[0]);
		GUILayout.Label("SDP is structural durability. SP reduces incoming damage before it reaches SDP.", (GUILayoutOption[])(object)new GUILayoutOption[0]);
		GUILayout.Space(6f);
		GUILayout.BeginHorizontal((GUILayoutOption[])(object)new GUILayoutOption[0]);
		GUILayout.Label("Maximum SDP", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(120f) });
		doorPropsDialog.MaxSdpText = GUILayout.TextField((doorPropsDialog.MaxSdpText == null) ? "" : doorPropsDialog.MaxSdpText, 5, (GUILayoutOption[])(object)new GUILayoutOption[0]);
		GUILayout.EndHorizontal();
		GUILayout.BeginHorizontal((GUILayoutOption[])(object)new GUILayoutOption[0]);
		GUILayout.Label("Current SDP", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(120f) });
		doorPropsDialog.CurrentSdpText = GUILayout.TextField((doorPropsDialog.CurrentSdpText == null) ? "" : doorPropsDialog.CurrentSdpText, 5, (GUILayoutOption[])(object)new GUILayoutOption[0]);
		GUILayout.EndHorizontal();
		GUILayout.BeginHorizontal((GUILayoutOption[])(object)new GUILayoutOption[0]);
		GUILayout.Label("Maximum SP", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(120f) });
		doorPropsDialog.MaxSpText = GUILayout.TextField((doorPropsDialog.MaxSpText == null) ? "" : doorPropsDialog.MaxSpText, 5, (GUILayoutOption[])(object)new GUILayoutOption[0]);
		GUILayout.EndHorizontal();
		GUILayout.BeginHorizontal((GUILayoutOption[])(object)new GUILayoutOption[0]);
		GUILayout.Label("Current SP", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(120f) });
		doorPropsDialog.CurrentSpText = GUILayout.TextField((doorPropsDialog.CurrentSpText == null) ? "" : doorPropsDialog.CurrentSpText, 5, (GUILayoutOption[])(object)new GUILayoutOption[0]);
		GUILayout.EndHorizontal();
		GUILayout.Space(5f);
		GUILayout.Label("Set Maximum SDP to 0 to disable structural health for this door.", (GUILayoutOption[])(object)new GUILayoutOption[0]);
		GUILayout.FlexibleSpace();
		GUILayout.BeginHorizontal((GUILayoutOption[])(object)new GUILayoutOption[0]);
		if (GUILayout.Button("Save", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(30f) }))
		{
			if (!int.TryParse(doorPropsDialog.MaxSdpText, out var result) || result < 0 || result > 9999 || !int.TryParse(doorPropsDialog.CurrentSdpText, out var result2) || result2 < 0 || result2 > 9999 || !int.TryParse(doorPropsDialog.MaxSpText, out var result3) || result3 < 0 || result3 > 999 || !int.TryParse(doorPropsDialog.CurrentSpText, out var result4) || result4 < 0 || result4 > 999)
			{
				ShowToast("Enter valid non-negative SDP/SP values.");
			}
			else if (result == 0)
			{
				ConfigureDoorHealthInternal(doorPropsDialog.DoorId, configured: false, 0, 0, 0, 0, broadcast: true);
				_doorPropsDialog = null;
				ShowToast("Structural health disabled for this door.");
			}
			else
			{
				if (result2 > result)
				{
					result2 = result;
				}
				if (result4 > result3)
				{
					result4 = result3;
				}
				LastDoorSDP.Value = result;
				LastDoorSP.Value = result3;
				ConfigureDoorHealthInternal(doorPropsDialog.DoorId, configured: true, result, result2, result3, result4, broadcast: true);
				_doorPropsDialog = null;
				ShowToast("Door structural health updated.");
			}
		}
		if (GUILayout.Button("Reset to Max", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(30f) }) && int.TryParse(doorPropsDialog.MaxSdpText, out var result5) && result5 > 0 && int.TryParse(doorPropsDialog.MaxSpText, out var result6) && result6 >= 0)
		{
			doorPropsDialog.CurrentSdpText = result5.ToString();
			doorPropsDialog.CurrentSpText = result6.ToString();
		}
		if (GUILayout.Button("Cancel", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(30f) }))
		{
			_doorPropsDialog = null;
		}
		GUILayout.EndHorizontal();
		GUI.DragWindow();
	}

	internal void ShowDoorPropertiesDialog(DoorContext ctx)
	{
		if (ctx != null)
		{
			DoorRecord orCreateDoorRecord = GetOrCreateDoorRecord(ctx.DoorId);
			DoorPropertiesDialogState doorPropertiesDialogState = new DoorPropertiesDialogState();
			doorPropertiesDialogState.DoorId = ctx.DoorId;
			int num = (orCreateDoorRecord.HealthConfigured ? orCreateDoorRecord.MaxSDP : Math.Max(1, (LastDoorSDP == null) ? 20 : LastDoorSDP.Value));
			int num2 = (orCreateDoorRecord.HealthConfigured ? orCreateDoorRecord.MaxSP : Math.Max(0, (LastDoorSP != null) ? LastDoorSP.Value : 0));
			doorPropertiesDialogState.MaxSdpText = num.ToString();
			doorPropertiesDialogState.CurrentSdpText = (orCreateDoorRecord.HealthConfigured ? orCreateDoorRecord.CurrentSDP : num).ToString();
			doorPropertiesDialogState.MaxSpText = num2.ToString();
			doorPropertiesDialogState.CurrentSpText = (orCreateDoorRecord.HealthConfigured ? orCreateDoorRecord.CurrentSP : num2).ToString();
			_doorPropsDialog = doorPropertiesDialogState;
			((Rect)(ref _doorPropsWindowRect)).x = 0f;
			((Rect)(ref _doorPropsWindowRect)).y = 0f;
		}
	}

	internal void ShowLockDialog(DoorContext ctx, string requester, bool remoteRequest)
	{
		if (ctx != null && !string.IsNullOrEmpty(ctx.DoorId))
		{
			EnsureDoorDataLoadedForCurrentBoard();
			DoorRecord orCreateDoorRecord = GetOrCreateDoorRecord(ctx.DoorId);
			int num = ((orCreateDoorRecord.LockDifficulty > 0) ? orCreateDoorRecord.LockDifficulty : Math.Max(1, LastLockDifficulty.Value));
			LockDialogState lockDialogState = new LockDialogState();
			lockDialogState.BoardId = LoadedBoardId;
			lockDialogState.DoorId = ctx.DoorId;
			lockDialogState.Requester = requester;
			lockDialogState.IsRemoteRequest = remoteRequest;
			lockDialogState.DifficultyText = num.ToString();
			lockDialogState.KeyCode = ((orCreateDoorRecord.KeyCode == null) ? "" : orCreateDoorRecord.KeyCode);
			_lockDialog = lockDialogState;
		}
	}

	internal void ShowManualCheckDialog(DoorCheckRequest request)
	{
		if (request != null)
		{
			ManualCheckDialogState manualCheckDialogState = new ManualCheckDialogState();
			manualCheckDialogState.Request = request;
			manualCheckDialogState.TotalText = "";
			_checkDialog = manualCheckDialogState;
		}
	}

	internal void ShowToast(string text)
	{
		_toastText = text;
		_toastUntil = Time.realtimeSinceStartup + 4f;
		if (Log != null)
		{
			Log.LogMessage((object)text);
		}
		DoorRulesBridge.EmitToast(text);
	}

	internal static string GetDifficultyTerm()
	{
		string text = ((DifficultyTerm == null) ? "DV" : DifficultyTerm.Value);
		if (string.IsNullOrEmpty(text))
		{
			text = "Difficulty";
		}
		return text.Trim();
	}

	internal static DoorContext BuildDoorContext(string key, string contentId, string nativeAction)
	{
		EnsureDoorDataLoadedForCurrentBoard();
		DoorRecord orCreateDoorRecord = GetOrCreateDoorRecord(key);
		DoorContext doorContext = new DoorContext();
		doorContext.DoorId = key;
		doorContext.ContentId = contentId;
		doorContext.BoardId = LoadedBoardId;
		doorContext.NativeAction = nativeAction;
		doorContext.IsLocked = orCreateDoorRecord.IsLocked;
		doorContext.LockDifficulty = orCreateDoorRecord.LockDifficulty;
		doorContext.HasKeyCode = orCreateDoorRecord.KeyConfigured;
		doorContext.Obstruction = orCreateDoorRecord.Obstruction;
		doorContext.HealthConfigured = orCreateDoorRecord.HealthConfigured;
		doorContext.MaxSDP = orCreateDoorRecord.MaxSDP;
		doorContext.CurrentSDP = orCreateDoorRecord.CurrentSDP;
		doorContext.MaxSP = orCreateDoorRecord.MaxSP;
		doorContext.CurrentSP = orCreateDoorRecord.CurrentSP;
		doorContext.IsDestroyed = orCreateDoorRecord.IsDestroyed;
		doorContext.IsGm = LocalClient.IsInGmMode;
		return doorContext;
	}

	internal static void LockDoorFromMenu(MapMenuItem item, object context)
	{
		if (context is DoorContext doorContext && !((Object)(object)Instance == (Object)null))
		{
			if (LocalClient.IsInGmMode)
			{
				Instance.ShowLockDialog(doorContext, null, remoteRequest: false);
				return;
			}
			SendRpc(BuildLockRequestMessage(doorContext.BoardId, doorContext.DoorId));
			Instance.ShowToast("Lock request sent to the GM.");
		}
	}

	internal static void UnlockDoorFromMenu(MapMenuItem item, object context)
	{
		if (context is DoorContext doorContext && LocalClient.IsInGmMode)
		{
			ConfigureDoorLockInternal(doorContext.DoorId, locked: false, null, null, broadcast: true);
			if ((Object)(object)Instance != (Object)null)
			{
				Instance.ShowToast("The door is now unlocked.");
			}
		}
	}

	internal static void PickLockFromMenu(MapMenuItem item, object context)
	{
		if (!(context is DoorContext doorContext))
		{
			return;
		}
		DoorRecord orCreateDoorRecord = GetOrCreateDoorRecord(doorContext.DoorId);
		DoorCheckRequest doorCheckRequest = DoorControlApi.BeginCheck("pick-lock", "Pick Lock", doorContext, (orCreateDoorRecord.LockDifficulty > 0) ? orCreateDoorRecord.LockDifficulty : Math.Max(1, LastLockDifficulty.Value));
		if (doorCheckRequest == null || DoorRulesBridge.TryDispatchCheck(doorCheckRequest) || DoorControlApi.DispatchCheck(doorCheckRequest))
		{
			return;
		}
		if (ManualCheckFallback != null && ManualCheckFallback.Value && (Object)(object)Instance != (Object)null)
		{
			Instance.ShowManualCheckDialog(doorCheckRequest);
			return;
		}
		DoorControlApi.CancelCheck(doorCheckRequest.RequestId);
		if ((Object)(object)Instance != (Object)null)
		{
			Instance.ShowToast("No check provider is registered for Pick Lock.");
		}
	}

	internal static void TryKeyFromMenu(MapMenuItem item, object context)
	{
		if (!(context is DoorContext doorContext))
		{
			return;
		}
		DoorRecord orCreateDoorRecord = GetOrCreateDoorRecord(doorContext.DoorId);
		if (!orCreateDoorRecord.IsLocked || !orCreateDoorRecord.KeyConfigured)
		{
			if ((Object)(object)Instance != (Object)null)
			{
				Instance.ShowToast("This door has no configured key.");
			}
			return;
		}
		DoorKeyRequest doorKeyRequest = DoorControlApi.BeginKeyAttempt(doorContext);
		if (doorKeyRequest != null && !DoorRulesBridge.TryDispatchKey(doorKeyRequest) && !DoorControlApi.DispatchKeyAttempt(doorKeyRequest))
		{
			DoorControlApi.CancelKeyAttempt(doorKeyRequest.RequestId);
			if ((Object)(object)Instance != (Object)null)
			{
				Instance.ShowToast("No key provider is registered.");
			}
		}
	}

	internal static void JamDoorFromMenu(MapMenuItem item, object context)
	{
		if (context is DoorContext doorContext)
		{
			DoorRecord orCreateDoorRecord = GetOrCreateDoorRecord(doorContext.DoorId);
			if (orCreateDoorRecord.Obstruction == DoorObstructionState.Normal)
			{
				DoorCheckRequest request = DoorControlApi.BeginCheck("jam", "Jam Door", doorContext, (orCreateDoorRecord.LockDifficulty > 0) ? orCreateDoorRecord.LockDifficulty : Math.Max(1, LastLockDifficulty.Value));
				DispatchDoorCheck(request);
			}
		}
	}

	internal static void ClearJamFromMenu(MapMenuItem item, object context)
	{
		if (!(context is DoorContext doorContext))
		{
			return;
		}
		DoorRecord orCreateDoorRecord = GetOrCreateDoorRecord(doorContext.DoorId);
		if (orCreateDoorRecord.Obstruction != DoorObstructionState.Jammed)
		{
			return;
		}
		if (LocalClient.IsInGmMode)
		{
			ClearObstructionInternal(doorContext.DoorId, DoorObstructionState.Jammed, broadcast: true);
			if ((Object)(object)Instance != (Object)null)
			{
				Instance.ShowToast(orCreateDoorRecord.PreviousLockState ? "The jam is cleared. The door is locked." : "The jam is cleared. The door is unlocked.");
			}
		}
		else
		{
			DoorCheckRequest request = DoorControlApi.BeginCheck("clear-jam", "Clear Jam", doorContext, Math.Max(1, orCreateDoorRecord.JamStrength));
			DispatchDoorCheck(request);
		}
	}

	internal static void BarricadeDoorFromMenu(MapMenuItem item, object context)
	{
		if (context is DoorContext doorContext)
		{
			DoorRecord orCreateDoorRecord = GetOrCreateDoorRecord(doorContext.DoorId);
			if (orCreateDoorRecord.Obstruction == DoorObstructionState.Normal)
			{
				DoorCheckRequest request = DoorControlApi.BeginCheck("barricade", "Barricade", doorContext, 15);
				DispatchDoorCheck(request);
			}
		}
	}

	internal static void ClearBarricadeFromMenu(MapMenuItem item, object context)
	{
		if (!(context is DoorContext doorContext) || !LocalClient.IsInGmMode)
		{
			return;
		}
		DoorRecord orCreateDoorRecord = GetOrCreateDoorRecord(doorContext.DoorId);
		if (orCreateDoorRecord.Obstruction == DoorObstructionState.Barricaded)
		{
			bool previousLockState = orCreateDoorRecord.PreviousLockState;
			ClearObstructionInternal(doorContext.DoorId, DoorObstructionState.Barricaded, broadcast: true);
			if ((Object)(object)Instance != (Object)null)
			{
				Instance.ShowToast(previousLockState ? "The barricade is cleared. The door is locked." : "The barricade is cleared. The door is unlocked.");
			}
		}
	}

	internal static void AttackDoorFromMenu(MapMenuItem item, object context)
	{
		if (!(context is DoorContext doorContext))
		{
			return;
		}
		DoorRecord orCreateDoorRecord = GetOrCreateDoorRecord(doorContext.DoorId);
		if (!orCreateDoorRecord.HealthConfigured || orCreateDoorRecord.IsDestroyed)
		{
			if ((Object)(object)Instance != (Object)null)
			{
				Instance.ShowToast(orCreateDoorRecord.IsDestroyed ? "The door has already been destroyed." : "This door has no structural health configured.");
			}
			return;
		}
		DoorDamageRequest doorDamageRequest = DoorControlApi.BeginDamage(doorContext);
		if (doorDamageRequest != null && !DoorRulesBridge.TryDispatchDamage(doorDamageRequest))
		{
			DoorControlApi.CancelDamage(doorDamageRequest.RequestId);
			if ((Object)(object)Instance != (Object)null)
			{
				Instance.ShowToast("No rules provider is registered for door damage.");
			}
		}
	}

	internal static void DoorPropertiesFromMenu(MapMenuItem item, object context)
	{
		if (context is DoorContext ctx && LocalClient.IsInGmMode && !((Object)(object)Instance == (Object)null))
		{
			Instance.ShowDoorPropertiesDialog(ctx);
		}
	}

	internal static void DestroyedStatusFromMenu(MapMenuItem item, object context)
	{
		if ((Object)(object)Instance != (Object)null)
		{
			Instance.ShowToast("The door has been destroyed.");
		}
	}

	internal static void RestoreDoorFromMenu(MapMenuItem item, object context)
	{
		if (!(context is DoorContext doorContext) || !LocalClient.IsInGmMode)
		{
			return;
		}
		DoorRecord orCreateDoorRecord = GetOrCreateDoorRecord(doorContext.DoorId);
		if (orCreateDoorRecord.HealthConfigured)
		{
			ConfigureDoorHealthInternal(doorContext.DoorId, configured: true, orCreateDoorRecord.MaxSDP, orCreateDoorRecord.MaxSDP, orCreateDoorRecord.MaxSP, orCreateDoorRecord.MaxSP, broadcast: true);
			if ((Object)(object)Instance != (Object)null)
			{
				Instance.ShowToast("The door has been restored to full structural health.");
			}
		}
	}

	internal static void DispatchDoorCheck(DoorCheckRequest request)
	{
		if (request == null || DoorRulesBridge.TryDispatchCheck(request) || DoorControlApi.DispatchCheck(request))
		{
			return;
		}
		if (ManualCheckFallback != null && ManualCheckFallback.Value && (Object)(object)Instance != (Object)null)
		{
			Instance.ShowManualCheckDialog(request);
			return;
		}
		DoorControlApi.CancelCheck(request.RequestId);
		if ((Object)(object)Instance != (Object)null)
		{
			Instance.ShowToast("No rules provider is registered for " + request.DisplayName + ".");
		}
	}

	internal static void InspectDoor(MapMenuItem item, object context)
	{
		if (context is DoorContext doorContext)
		{
			DoorRecord orCreateDoorRecord = GetOrCreateDoorRecord(doorContext.DoorId);
			string text = ((!orCreateDoorRecord.KeyConfigured) ? "<none>" : ((!LocalClient.IsInGmMode) ? "<configured>" : (string.IsNullOrEmpty(orCreateDoorRecord.KeyCode) ? "<configured, value unavailable>" : orCreateDoorRecord.KeyCode)));
			string text2 = DoorRulesBridge.DescribeConnection();
			string text3 = (orCreateDoorRecord.IsDestroyed ? "Destroyed" : ((orCreateDoorRecord.Obstruction == DoorObstructionState.Barricaded) ? "Barricaded" : ((orCreateDoorRecord.Obstruction == DoorObstructionState.Jammed) ? "Jammed" : (orCreateDoorRecord.IsLocked ? "Locked" : "Unlocked"))));
			Log.LogInfo((object)string.Concat("[Door Inspect] DoorId=", doorContext.DoorId, " | BoardId=", doorContext.BoardId, " | NativeAction=", doorContext.NativeAction, " | State=", text3, " | Locked=", orCreateDoorRecord.IsLocked, " | ", GetDifficultyTerm(), "=", orCreateDoorRecord.LockDifficulty, " | KeyCode=", text, " | Obstruction=", orCreateDoorRecord.Obstruction, " | HealthConfigured=", orCreateDoorRecord.HealthConfigured, " | SP=", orCreateDoorRecord.CurrentSP, "/", orCreateDoorRecord.MaxSP, " | SDP=", orCreateDoorRecord.CurrentSDP, "/", orCreateDoorRecord.MaxSDP, " | Destroyed=", orCreateDoorRecord.IsDestroyed, " | RulesClient=", text2, " | GM=", doorContext.IsGm));
			if ((Object)(object)Instance != (Object)null)
			{
				string text4 = (orCreateDoorRecord.HealthConfigured ? (" | SP " + orCreateDoorRecord.CurrentSP + "/" + orCreateDoorRecord.MaxSP + " | SDP " + orCreateDoorRecord.CurrentSDP + "/" + orCreateDoorRecord.MaxSDP) : " | Structure: not configured");
				string text5 = text3 + " | " + GetDifficultyTerm() + " " + orCreateDoorRecord.LockDifficulty + " | Key Code: " + text + text4 + " | Rules: " + text2;
				Instance.ShowToast(text5);
			}
		}
	}

	internal static void AddRegisteredLockedDoorActions(MapMenu menu, DoorContext ctx)
	{
		//IL_0097: Unknown result type (might be due to invalid IL or missing references)
		//IL_009e: Expected O, but got Unknown
		ExternalDoorAction[] registeredLockedDoorActionsSnapshot = DoorControlApi.GetRegisteredLockedDoorActionsSnapshot();
		foreach (ExternalDoorAction externalDoorAction in registeredLockedDoorActionsSnapshot)
		{
			if (externalDoorAction == null || externalDoorAction.Callback == null)
			{
				continue;
			}
			bool flag = true;
			if (externalDoorAction.IsVisible != null)
			{
				try
				{
					flag = externalDoorAction.IsVisible(ctx);
				}
				catch (Exception ex)
				{
					flag = false;
					Log.LogError((object)("External action visibility failed for '" + externalDoorAction.Id + "': " + ex));
				}
			}
			if (flag)
			{
				ExternalDoorActionContext externalDoorActionContext = new ExternalDoorActionContext();
				externalDoorActionContext.Action = externalDoorAction;
				externalDoorActionContext.Door = ctx;
				ItemArgs val = new ItemArgs();
				val.Action = InvokeExternalDoorAction;
				val.Title = externalDoorAction.Title;
				val.Obj = externalDoorActionContext;
				val.CloseMenuOnActivate = true;
				menu.AddItem(val);
			}
		}
	}

	internal static void InvokeExternalDoorAction(MapMenuItem item, object context)
	{
		if (!(context is ExternalDoorActionContext { Action: not null } externalDoorActionContext) || externalDoorActionContext.Action.Callback == null)
		{
			return;
		}
		try
		{
			externalDoorActionContext.Action.Callback(externalDoorActionContext.Door);
		}
		catch (Exception ex)
		{
			Log.LogError((object)("External door action failed: " + ex));
		}
	}

	internal static DoorRecord GetOrCreateDoorRecord(string doorId)
	{
		EnsureDoorDataLoadedForCurrentBoard();
		if (!DoorRecords.TryGetValue(doorId, out var value))
		{
			value = new DoorRecord();
			value.DoorId = doorId;
			value.IsLocked = false;
			value.LockDifficulty = Math.Max(1, (LastLockDifficulty == null) ? 15 : LastLockDifficulty.Value);
			value.KeyCode = "";
			value.KeyConfigured = false;
			value.Obstruction = DoorObstructionState.Normal;
			value.JamStrength = 0;
			value.PreviousLockState = false;
			value.HealthConfigured = false;
			value.MaxSDP = 0;
			value.CurrentSDP = 0;
			value.MaxSP = 0;
			value.CurrentSP = 0;
			DoorRecords[doorId] = value;
		}
		return value;
	}

	internal static bool IsLockedInternal(string doorId)
	{
		if (string.IsNullOrEmpty(doorId))
		{
			return false;
		}
		return GetOrCreateDoorRecord(doorId).IsLocked;
	}

	internal static DoorRecordSnapshot GetSnapshotInternal(string doorId)
	{
		if (string.IsNullOrEmpty(doorId))
		{
			return null;
		}
		DoorRecord orCreateDoorRecord = GetOrCreateDoorRecord(doorId);
		return new DoorRecordSnapshot(orCreateDoorRecord.DoorId, orCreateDoorRecord.IsLocked, orCreateDoorRecord.LockDifficulty, orCreateDoorRecord.KeyConfigured, orCreateDoorRecord.Obstruction, orCreateDoorRecord.JamStrength, orCreateDoorRecord.PreviousLockState, orCreateDoorRecord.HealthConfigured, orCreateDoorRecord.MaxSDP, orCreateDoorRecord.CurrentSDP, orCreateDoorRecord.MaxSP, orCreateDoorRecord.CurrentSP);
	}

	internal static bool KeyMatchesInternal(string doorId, string candidateCode)
	{
		if (string.IsNullOrEmpty(doorId) || string.IsNullOrEmpty(candidateCode))
		{
			return false;
		}
		DoorRecord orCreateDoorRecord = GetOrCreateDoorRecord(doorId);
		if (!orCreateDoorRecord.KeyConfigured || string.IsNullOrEmpty(orCreateDoorRecord.KeyCode))
		{
			return false;
		}
		return string.Equals(orCreateDoorRecord.KeyCode.Trim(), candidateCode.Trim(), StringComparison.OrdinalIgnoreCase);
	}

	internal static bool ConfigureDoorLockInternal(string doorId, bool locked, int? difficulty, string keyCode, bool broadcast)
	{
		if (string.IsNullOrEmpty(doorId))
		{
			return false;
		}
		EnsureDoorDataLoadedForCurrentBoard();
		DoorRecord orCreateDoorRecord = GetOrCreateDoorRecord(doorId);
		bool result = orCreateDoorRecord.IsLocked != locked;
		orCreateDoorRecord.IsLocked = locked;
		if (difficulty.HasValue)
		{
			orCreateDoorRecord.LockDifficulty = Math.Max(1, difficulty.Value);
		}
		if (keyCode != null)
		{
			orCreateDoorRecord.KeyCode = keyCode.Trim();
			orCreateDoorRecord.KeyConfigured = !string.IsNullOrEmpty(orCreateDoorRecord.KeyCode);
		}
		SaveDoorData();
		DoorControlApi.RaiseDoorStateChanged(LoadedBoardId, doorId, orCreateDoorRecord.IsLocked);
		if (broadcast)
		{
			BroadcastDoorState(orCreateDoorRecord);
		}
		Log.LogInfo((object)((locked ? "Locked" : "Unlocked") + " door. DoorId=" + doorId + " | " + GetDifficultyTerm() + "=" + orCreateDoorRecord.LockDifficulty + " | KeyConfigured=" + orCreateDoorRecord.KeyConfigured + "."));
		return result;
	}

	internal static bool ConfigureDoorHealthInternal(string doorId, bool configured, int maxSdp, int currentSdp, int maxSp, int currentSp, bool broadcast)
	{
		if (string.IsNullOrEmpty(doorId))
		{
			return false;
		}
		DoorRecord orCreateDoorRecord = GetOrCreateDoorRecord(doorId);
		orCreateDoorRecord.HealthConfigured = configured && maxSdp > 0;
		if (!orCreateDoorRecord.HealthConfigured)
		{
			orCreateDoorRecord.MaxSDP = 0;
			orCreateDoorRecord.CurrentSDP = 0;
			orCreateDoorRecord.MaxSP = 0;
			orCreateDoorRecord.CurrentSP = 0;
		}
		else
		{
			orCreateDoorRecord.MaxSDP = Math.Max(1, maxSdp);
			orCreateDoorRecord.CurrentSDP = Math.Max(0, Math.Min(orCreateDoorRecord.MaxSDP, currentSdp));
			orCreateDoorRecord.MaxSP = Math.Max(0, maxSp);
			orCreateDoorRecord.CurrentSP = Math.Max(0, Math.Min(orCreateDoorRecord.MaxSP, currentSp));
		}
		SaveDoorData();
		if (broadcast)
		{
			BroadcastDoorState(orCreateDoorRecord);
		}
		return true;
	}

	internal static DoorDamageResult ApplyDoorDamageInternal(string doorId, int rawDamage, int sdpLoss, int spLoss, string source, bool broadcast)
	{
		if (string.IsNullOrEmpty(doorId))
		{
			return null;
		}
		DoorRecord orCreateDoorRecord = GetOrCreateDoorRecord(doorId);
		if (!orCreateDoorRecord.HealthConfigured)
		{
			return null;
		}
		DoorDamageResult doorDamageResult = new DoorDamageResult();
		doorDamageResult.DoorId = doorId;
		doorDamageResult.Source = ((source == null) ? "" : source);
		doorDamageResult.RawDamage = Math.Max(0, rawDamage);
		doorDamageResult.BeforeSDP = orCreateDoorRecord.CurrentSDP;
		doorDamageResult.BeforeSP = orCreateDoorRecord.CurrentSP;
		if (!orCreateDoorRecord.IsDestroyed)
		{
			doorDamageResult.SDPLoss = Math.Min(orCreateDoorRecord.CurrentSDP, Math.Max(0, sdpLoss));
			doorDamageResult.SPLoss = Math.Min(orCreateDoorRecord.CurrentSP, Math.Max(0, spLoss));
			orCreateDoorRecord.CurrentSDP = Math.Max(0, orCreateDoorRecord.CurrentSDP - doorDamageResult.SDPLoss);
			orCreateDoorRecord.CurrentSP = Math.Max(0, orCreateDoorRecord.CurrentSP - doorDamageResult.SPLoss);
			SaveDoorData();
			if (broadcast)
			{
				BroadcastDoorState(orCreateDoorRecord);
			}
		}
		doorDamageResult.AfterSDP = orCreateDoorRecord.CurrentSDP;
		doorDamageResult.AfterSP = orCreateDoorRecord.CurrentSP;
		doorDamageResult.Destroyed = orCreateDoorRecord.IsDestroyed;
		return doorDamageResult;
	}

	internal static bool SetObstructionInternal(string doorId, DoorObstructionState obstruction, int strength, bool broadcast)
	{
		if (string.IsNullOrEmpty(doorId))
		{
			return false;
		}
		DoorRecord orCreateDoorRecord = GetOrCreateDoorRecord(doorId);
		if (orCreateDoorRecord.Obstruction != DoorObstructionState.Normal)
		{
			return false;
		}
		orCreateDoorRecord.PreviousLockState = orCreateDoorRecord.IsLocked;
		orCreateDoorRecord.Obstruction = obstruction;
		orCreateDoorRecord.JamStrength = ((obstruction == DoorObstructionState.Jammed) ? Math.Max(1, strength) : 0);
		SaveDoorData();
		if (broadcast)
		{
			BroadcastDoorState(orCreateDoorRecord);
		}
		return true;
	}

	internal static bool ClearObstructionInternal(string doorId, DoorObstructionState expected, bool broadcast)
	{
		if (string.IsNullOrEmpty(doorId))
		{
			return false;
		}
		DoorRecord orCreateDoorRecord = GetOrCreateDoorRecord(doorId);
		if (orCreateDoorRecord.Obstruction != expected)
		{
			return false;
		}
		orCreateDoorRecord.IsLocked = orCreateDoorRecord.PreviousLockState;
		orCreateDoorRecord.Obstruction = DoorObstructionState.Normal;
		orCreateDoorRecord.JamStrength = 0;
		SaveDoorData();
		DoorControlApi.RaiseDoorStateChanged(LoadedBoardId, doorId, orCreateDoorRecord.IsLocked);
		if (broadcast)
		{
			BroadcastDoorState(orCreateDoorRecord);
		}
		return true;
	}

	internal static void ApplyNetworkState(string boardId, string doorId, bool locked, int difficulty, bool keyConfigured, DoorObstructionState obstruction, int jamStrength, bool previousLockState, bool healthConfigured, int maxSdp, int currentSdp, int maxSp, int currentSp)
	{
		EnsureDoorDataLoadedForCurrentBoard();
		if (string.Equals(boardId, LoadedBoardId, StringComparison.Ordinal))
		{
			DoorRecord orCreateDoorRecord = GetOrCreateDoorRecord(doorId);
			orCreateDoorRecord.IsLocked = locked;
			orCreateDoorRecord.LockDifficulty = Math.Max(1, difficulty);
			orCreateDoorRecord.KeyConfigured = keyConfigured;
			if (!LocalClient.IsInGmMode)
			{
				orCreateDoorRecord.KeyCode = "";
			}
			orCreateDoorRecord.Obstruction = obstruction;
			orCreateDoorRecord.JamStrength = jamStrength;
			orCreateDoorRecord.PreviousLockState = previousLockState;
			orCreateDoorRecord.HealthConfigured = healthConfigured && maxSdp > 0;
			orCreateDoorRecord.MaxSDP = (orCreateDoorRecord.HealthConfigured ? Math.Max(1, maxSdp) : 0);
			orCreateDoorRecord.CurrentSDP = (orCreateDoorRecord.HealthConfigured ? Math.Max(0, Math.Min(orCreateDoorRecord.MaxSDP, currentSdp)) : 0);
			orCreateDoorRecord.MaxSP = (orCreateDoorRecord.HealthConfigured ? Math.Max(0, maxSp) : 0);
			orCreateDoorRecord.CurrentSP = (orCreateDoorRecord.HealthConfigured ? Math.Max(0, Math.Min(orCreateDoorRecord.MaxSP, currentSp)) : 0);
			SaveDoorData();
			DoorControlApi.RaiseDoorStateChanged(boardId, doorId, locked);
		}
	}

	internal unsafe static void EnsureDoorDataLoadedForCurrentBoard()
	{
		//IL_0000: Unknown result type (might be due to invalid IL or missing references)
		//IL_0005: Unknown result type (might be due to invalid IL or missing references)
		//IL_000a: Unknown result type (might be due to invalid IL or missing references)
		string text;
		try
		{
			BoardGuid id = BoardSessionManager.CurrentBoardInfo.Id;
			text = ((object)(*(BoardGuid*)(&id))/*cast due to .constrained prefix*/).ToString();
		}
		catch
		{
			return;
		}
		if (string.Equals(text, LoadedBoardId, StringComparison.Ordinal))
		{
			return;
		}
		LoadedBoardId = text;
		SyncRequestedBoardId = null;
		DoorRecords.Clear();
		string doorDataFilePath = GetDoorDataFilePath(text);
		try
		{
			if (File.Exists(doorDataFilePath))
			{
				string[] array = File.ReadAllLines(doorDataFilePath);
				for (int i = 0; i < array.Length; i++)
				{
					ParseDoorRecordLine(array[i]);
				}
			}
			else
			{
				MigrateLegacyLockFile(text);
			}
			Log.LogInfo((object)("Loaded " + DoorRecords.Count + " door record(s) for board " + text + "."));
		}
		catch (Exception ex)
		{
			Log.LogError((object)("Failed loading door data: " + ex));
		}
	}

	internal static void ParseDoorRecordLine(string line)
	{
		if (string.IsNullOrEmpty(line))
		{
			return;
		}
		string[] array = line.Split(new char[1] { '\t' });
		if (array.Length >= 4)
		{
			string text = array[0];
			if (!string.IsNullOrEmpty(text))
			{
				int num = ParseInt(array[1], 0);
				int lockDifficulty = Math.Max(1, ParseInt(array[2], 15));
				string text2 = DecodeText(array[3]);
				int obstruction = ((array.Length > 4) ? ParseInt(array[4], 0) : 0);
				int jamStrength = ((array.Length > 5) ? ParseInt(array[5], 0) : 0);
				bool previousLockState = array.Length > 6 && ParseInt(array[6], 0) != 0;
				bool keyConfigured = ((array.Length > 7) ? (ParseInt(array[7], 0) != 0) : (!string.IsNullOrEmpty(text2)));
				bool flag = array.Length > 8 && ParseInt(array[8], 0) != 0;
				int num2 = ((array.Length > 9) ? Math.Max(0, ParseInt(array[9], 0)) : 0);
				int val = ((array.Length > 10) ? Math.Max(0, ParseInt(array[10], num2)) : num2);
				int num3 = ((array.Length > 11) ? Math.Max(0, ParseInt(array[11], 0)) : 0);
				int val2 = ((array.Length > 12) ? Math.Max(0, ParseInt(array[12], num3)) : num3);
				DoorRecord doorRecord = new DoorRecord();
				doorRecord.DoorId = text;
				doorRecord.IsLocked = num != 0;
				doorRecord.LockDifficulty = lockDifficulty;
				doorRecord.KeyCode = (LocalClient.IsInGmMode ? text2 : "");
				doorRecord.KeyConfigured = keyConfigured;
				doorRecord.Obstruction = (DoorObstructionState)obstruction;
				doorRecord.JamStrength = jamStrength;
				doorRecord.PreviousLockState = previousLockState;
				doorRecord.HealthConfigured = flag && num2 > 0;
				doorRecord.MaxSDP = (doorRecord.HealthConfigured ? num2 : 0);
				doorRecord.CurrentSDP = (doorRecord.HealthConfigured ? Math.Min(doorRecord.MaxSDP, val) : 0);
				doorRecord.MaxSP = (doorRecord.HealthConfigured ? num3 : 0);
				doorRecord.CurrentSP = (doorRecord.HealthConfigured ? Math.Min(doorRecord.MaxSP, val2) : 0);
				DoorRecords[text] = doorRecord;
			}
		}
	}

	internal static void SaveDoorData()
	{
		if (string.IsNullOrEmpty(LoadedBoardId))
		{
			return;
		}
		try
		{
			List<string> list = new List<string>(DoorRecords.Keys);
			list.Sort(StringComparer.Ordinal);
			List<string> list2 = new List<string>();
			for (int i = 0; i < list.Count; i++)
			{
				DoorRecord doorRecord = DoorRecords[list[i]];
				if (doorRecord.IsLocked || doorRecord.KeyConfigured || doorRecord.HealthConfigured || doorRecord.Obstruction != DoorObstructionState.Normal || doorRecord.LockDifficulty != Math.Max(1, LastLockDifficulty.Value))
				{
					string value = (LocalClient.IsInGmMode ? doorRecord.KeyCode : "");
					list2.Add(doorRecord.DoorId + "\t" + (doorRecord.IsLocked ? "1" : "0") + "\t" + doorRecord.LockDifficulty + "\t" + EncodeText(value) + "\t" + (int)doorRecord.Obstruction + "\t" + doorRecord.JamStrength + "\t" + (doorRecord.PreviousLockState ? "1" : "0") + "\t" + (doorRecord.KeyConfigured ? "1" : "0") + "\t" + (doorRecord.HealthConfigured ? "1" : "0") + "\t" + doorRecord.MaxSDP + "\t" + doorRecord.CurrentSDP + "\t" + doorRecord.MaxSP + "\t" + doorRecord.CurrentSP);
				}
			}
			File.WriteAllLines(GetDoorDataFilePath(LoadedBoardId), list2.ToArray());
		}
		catch (Exception ex)
		{
			Log.LogError((object)("Failed saving door data: " + ex));
		}
	}

	internal static void MigrateLegacyLockFile(string boardId)
	{
		string legacyLockFilePath = GetLegacyLockFilePath(boardId);
		if (!File.Exists(legacyLockFilePath))
		{
			return;
		}
		string[] array = File.ReadAllLines(legacyLockFilePath);
		for (int i = 0; i < array.Length; i++)
		{
			string text = array[i].Trim();
			if (text.Length != 0)
			{
				DoorRecord doorRecord = new DoorRecord();
				doorRecord.DoorId = text;
				doorRecord.IsLocked = true;
				doorRecord.LockDifficulty = Math.Max(1, LastLockDifficulty.Value);
				doorRecord.KeyCode = "";
				doorRecord.KeyConfigured = false;
				doorRecord.Obstruction = DoorObstructionState.Normal;
				DoorRecords[text] = doorRecord;
			}
		}
		SaveDoorData();
		Log.LogInfo((object)("Migrated " + DoorRecords.Count + " legacy locked door(s) into current door metadata."));
	}

	internal static string GetDoorDataFilePath(string boardId)
	{
		string text = Path.Combine(Paths.ConfigPath, "DoorControl");
		if (!Directory.Exists(text))
		{
			Directory.CreateDirectory(text);
		}
		return Path.Combine(text, boardId + ".doors.txt");
	}

	internal static string GetLegacyLockFilePath(string boardId)
	{
		string text = Path.Combine(Paths.ConfigPath, "DoorControl");
		if (!Directory.Exists(text))
		{
			Directory.CreateDirectory(text);
		}
		return Path.Combine(text, boardId + ".locks.txt");
	}

	internal static void TryMigrateLegacyStateDirectory()
	{
		try
		{
			string path = Path.Combine(Paths.ConfigPath, "CyberpunkPINK.DoorControl");
			string text = Path.Combine(Paths.ConfigPath, "DoorControl");
			if (!Directory.Exists(path))
			{
				return;
			}
			if (!Directory.Exists(text))
			{
				Directory.CreateDirectory(text);
			}
			string[] files = Directory.GetFiles(path, "*.locks.txt");
			for (int i = 0; i < files.Length; i++)
			{
				string text2 = Path.Combine(text, Path.GetFileName(files[i]));
				if (!File.Exists(text2))
				{
					File.Copy(files[i], text2, overwrite: false);
				}
			}
		}
		catch (Exception ex)
		{
			if (Log != null)
			{
				Log.LogWarning((object)("Legacy migration failed: " + ex.Message));
			}
		}
	}

	internal static int ParseInt(string text, int fallback)
	{
		if (!int.TryParse(text, out var result))
		{
			return fallback;
		}
		return result;
	}

	internal static string EncodeText(string value)
	{
		if (string.IsNullOrEmpty(value))
		{
			return "";
		}
		return Convert.ToBase64String(Encoding.UTF8.GetBytes(value));
	}

	internal static string DecodeText(string value)
	{
		if (string.IsNullOrEmpty(value))
		{
			return "";
		}
		try
		{
			return Encoding.UTF8.GetString(Convert.FromBase64String(value));
		}
		catch
		{
			return "";
		}
	}

	internal static void RequestBoardSyncIfNeeded()
	{
		if (!string.IsNullOrEmpty(LoadedBoardId) && !LocalClient.IsInGmMode && !string.Equals(SyncRequestedBoardId, LoadedBoardId, StringComparison.Ordinal))
		{
			SyncRequestedBoardId = LoadedBoardId;
			SendRpc("/doorcontrol/v2|SYNC_REQUEST|" + EncodeText(LoadedBoardId));
		}
	}

	internal static void BroadcastDoorState(DoorRecord record)
	{
		if (record != null && !string.IsNullOrEmpty(LoadedBoardId))
		{
			SendRpc(BuildStateMessage(LoadedBoardId, record));
		}
	}

	internal static string BuildLockRequestMessage(string boardId, string doorId)
	{
		return "/doorcontrol/v2|LOCK_REQUEST|" + EncodeText(boardId) + "|" + EncodeText(doorId);
	}

	internal static string BuildDenyMessage(string boardId, string doorId, string requester)
	{
		return "/doorcontrol/v2|LOCK_DENIED|" + EncodeText(boardId) + "|" + EncodeText(doorId) + "|" + EncodeText(requester);
	}

	internal static string BuildStateMessage(string boardId, DoorRecord r)
	{
		return "/doorcontrol/v2|STATE|" + EncodeText(boardId) + "|" + EncodeText(r.DoorId) + "|" + (r.IsLocked ? "1" : "0") + "|" + r.LockDifficulty + "|" + (r.KeyConfigured ? "1" : "0") + "|" + (int)r.Obstruction + "|" + r.JamStrength + "|" + (r.PreviousLockState ? "1" : "0") + "|" + (r.HealthConfigured ? "1" : "0") + "|" + r.MaxSDP + "|" + r.CurrentSDP + "|" + r.MaxSP + "|" + r.CurrentSP;
	}

	internal static string BuildCheckResultMessage(DoorCheckRequest request, int total, bool providerSuccess)
	{
		return "/doorcontrol/v2|CHECK_RESULT|" + EncodeText(request.Door.BoardId) + "|" + EncodeText(request.Door.DoorId) + "|" + EncodeText(request.RequestId) + "|" + EncodeText(request.CheckId) + "|" + total + "|" + (providerSuccess ? "1" : "0");
	}

	internal static string BuildCheckResolutionMessage(string boardId, string doorId, string requestId, string requester, bool success, int total, int difficulty)
	{
		return "/doorcontrol/v2|CHECK_RESOLUTION|" + EncodeText(boardId) + "|" + EncodeText(doorId) + "|" + EncodeText(requestId) + "|" + EncodeText(requester) + "|" + (success ? "1" : "0") + "|" + total + "|" + difficulty;
	}

	internal static string BuildKeyAttemptMessage(DoorKeyRequest request, IList<string> candidateCodes)
	{
		string value = ((candidateCodes == null) ? "" : string.Join("\n", new List<string>(candidateCodes).ToArray()));
		return "/doorcontrol/v2|KEY_ATTEMPT|" + EncodeText(request.Door.BoardId) + "|" + EncodeText(request.Door.DoorId) + "|" + EncodeText(request.RequestId) + "|" + EncodeText(value);
	}

	internal static string BuildKeyResultMessage(string boardId, string doorId, string requestId, string requester, bool success)
	{
		return "/doorcontrol/v2|KEY_RESULT|" + EncodeText(boardId) + "|" + EncodeText(doorId) + "|" + EncodeText(requestId) + "|" + EncodeText(requester) + "|" + (success ? "1" : "0");
	}

	internal static string BuildDamageAttemptMessage(DoorDamageRequest request, int rawDamage, int sdpLoss, int spLoss, string source)
	{
		return "/doorcontrol/v2|DAMAGE_ATTEMPT|" + EncodeText(request.Door.BoardId) + "|" + EncodeText(request.Door.DoorId) + "|" + EncodeText(request.RequestId) + "|" + Math.Max(0, rawDamage) + "|" + Math.Max(0, sdpLoss) + "|" + Math.Max(0, spLoss) + "|" + EncodeText((source == null) ? "" : source);
	}

	internal static string BuildDamageResolutionMessage(string boardId, string doorId, string requestId, string requester, DoorDamageResult result)
	{
		return "/doorcontrol/v2|DAMAGE_RESOLUTION|" + EncodeText(boardId) + "|" + EncodeText(doorId) + "|" + EncodeText(requestId) + "|" + EncodeText(requester) + "|" + ((result == null) ? "0" : result.SDPLoss.ToString()) + "|" + ((result == null) ? "0" : result.SPLoss.ToString()) + "|" + ((result != null && result.Destroyed) ? "1" : "0") + "|" + ((result == null) ? "0" : result.AfterSDP.ToString()) + "|" + ((result == null) ? "0" : result.AfterSP.ToString());
	}

	internal static void SendRpc(string message)
	{
		//IL_0001: Unknown result type (might be due to invalid IL or missing references)
		//IL_0006: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			RPCManager.SendMessage(message, LocalPlayer.Id.Value);
			if (DebugMode != null && DebugMode.Value)
			{
				Log.LogInfo((object)("RPC sent: " + message.Substring(0, Math.Min(message.Length, 80))));
			}
		}
		catch (Exception ex)
		{
			Log.LogError((object)("Door Control RPC send failed: " + ex));
		}
	}

	internal static string HandleRpcMessage(string message, string sender, SourceRole source)
	{
		try
		{
			if (string.IsNullOrEmpty(message) || !message.StartsWith("/doorcontrol/v2|"))
			{
				return message;
			}
			string text = message.Substring("/doorcontrol/v2|".Length);
			string[] array = text.Split(new char[1] { '|' });
			if (array.Length == 0)
			{
				return "";
			}
			string text2 = array[0];
			if (text2 == "LOCK_REQUEST" && array.Length >= 3)
			{
				string a = DecodeText(array[1]);
				string text3 = DecodeText(array[2]);
				EnsureDoorDataLoadedForCurrentBoard();
				if (LocalClient.IsInGmMode && string.Equals(a, LoadedBoardId, StringComparison.Ordinal))
				{
					DoorContext doorContext = BuildDoorContext(text3, text3, "Open");
					if (!doorContext.IsLocked && (Object)(object)Instance != (Object)null)
					{
						Instance.ShowLockDialog(doorContext, sender, remoteRequest: true);
					}
				}
				return "";
			}
			if (text2 == "STATE" && array.Length >= 9)
			{
				string boardId = DecodeText(array[1]);
				string doorId = DecodeText(array[2]);
				bool locked = ParseInt(array[3], 0) != 0;
				int difficulty = Math.Max(1, ParseInt(array[4], 15));
				bool keyConfigured = ParseInt(array[5], 0) != 0;
				DoorObstructionState obstruction = (DoorObstructionState)ParseInt(array[6], 0);
				int jamStrength = ParseInt(array[7], 0);
				bool previousLockState = ParseInt(array[8], 0) != 0;
				bool healthConfigured = array.Length > 9 && ParseInt(array[9], 0) != 0;
				int num = ((array.Length > 10) ? Math.Max(0, ParseInt(array[10], 0)) : 0);
				int currentSdp = ((array.Length > 11) ? Math.Max(0, ParseInt(array[11], num)) : num);
				int num2 = ((array.Length > 12) ? Math.Max(0, ParseInt(array[12], 0)) : 0);
				int currentSp = ((array.Length > 13) ? Math.Max(0, ParseInt(array[13], num2)) : num2);
				ApplyNetworkState(boardId, doorId, locked, difficulty, keyConfigured, obstruction, jamStrength, previousLockState, healthConfigured, num, currentSdp, num2, currentSp);
				return "";
			}
			if (text2 == "CHECK_RESULT" && array.Length >= 7)
			{
				string text4 = DecodeText(array[1]);
				string doorId2 = DecodeText(array[2]);
				string requestId = DecodeText(array[3]);
				string checkId = DecodeText(array[4]);
				int num3 = ParseInt(array[5], int.MinValue);
				bool providerSuccess = ParseInt(array[6], 0) != 0;
				EnsureDoorDataLoadedForCurrentBoard();
				if (LocalClient.IsInGmMode && num3 != int.MinValue && string.Equals(text4, LoadedBoardId, StringComparison.Ordinal))
				{
					DoorControlApi.ResolveRemoteCheckAsGm(text4, doorId2, requestId, checkId, num3, providerSuccess, sender);
				}
				return "";
			}
			if (text2 == "CHECK_RESOLUTION" && array.Length >= 8)
			{
				string a2 = DecodeText(array[1]);
				string requestId2 = DecodeText(array[3]);
				string a3 = DecodeText(array[4]);
				bool success = ParseInt(array[5], 0) != 0;
				int total = ParseInt(array[6], 0);
				int difficulty2 = Math.Max(1, ParseInt(array[7], 1));
				string localPlayerIdString = GetLocalPlayerIdString();
				if (string.Equals(a2, LoadedBoardId, StringComparison.Ordinal) && string.Equals(a3, localPlayerIdString, StringComparison.Ordinal))
				{
					DoorControlApi.CompletePendingCheckFromAuthority(requestId2, success, total, difficulty2);
				}
				return "";
			}
			if (text2 == "KEY_ATTEMPT" && array.Length >= 5)
			{
				string text5 = DecodeText(array[1]);
				string doorId3 = DecodeText(array[2]);
				string requestId3 = DecodeText(array[3]);
				string text6 = DecodeText(array[4]);
				string[] candidateCodes = (string.IsNullOrEmpty(text6) ? new string[0] : text6.Split(new char[1] { '\n' }));
				EnsureDoorDataLoadedForCurrentBoard();
				if (LocalClient.IsInGmMode && string.Equals(text5, LoadedBoardId, StringComparison.Ordinal))
				{
					DoorControlApi.ResolveRemoteKeyAttemptAsGm(text5, doorId3, requestId3, candidateCodes, sender);
				}
				return "";
			}
			if (text2 == "KEY_RESULT" && array.Length >= 6)
			{
				string a4 = DecodeText(array[1]);
				string requestId4 = DecodeText(array[3]);
				string a5 = DecodeText(array[4]);
				bool success2 = ParseInt(array[5], 0) != 0;
				string localPlayerIdString2 = GetLocalPlayerIdString();
				if (string.Equals(a4, LoadedBoardId, StringComparison.Ordinal) && string.Equals(a5, localPlayerIdString2, StringComparison.Ordinal))
				{
					DoorControlApi.CompletePendingKeyFromAuthority(requestId4, success2);
				}
				return "";
			}
			if (text2 == "DAMAGE_ATTEMPT" && array.Length >= 8)
			{
				string text7 = DecodeText(array[1]);
				string doorId4 = DecodeText(array[2]);
				string requestId5 = DecodeText(array[3]);
				int rawDamage = Math.Max(0, ParseInt(array[4], 0));
				int sdpLoss = Math.Max(0, ParseInt(array[5], 0));
				int spLoss = Math.Max(0, ParseInt(array[6], 0));
				string source2 = DecodeText(array[7]);
				EnsureDoorDataLoadedForCurrentBoard();
				if (LocalClient.IsInGmMode && string.Equals(text7, LoadedBoardId, StringComparison.Ordinal))
				{
					DoorControlApi.ResolveRemoteDamageAsGm(text7, doorId4, requestId5, rawDamage, sdpLoss, spLoss, source2, sender);
				}
				return "";
			}
			if (text2 == "DAMAGE_RESOLUTION" && array.Length >= 10)
			{
				string a6 = DecodeText(array[1]);
				string requestId6 = DecodeText(array[3]);
				string a7 = DecodeText(array[4]);
				int sdpLoss2 = Math.Max(0, ParseInt(array[5], 0));
				int spLoss2 = Math.Max(0, ParseInt(array[6], 0));
				bool destroyed = ParseInt(array[7], 0) != 0;
				int currentSdp2 = Math.Max(0, ParseInt(array[8], 0));
				int currentSp2 = Math.Max(0, ParseInt(array[9], 0));
				string localPlayerIdString3 = GetLocalPlayerIdString();
				if (string.Equals(a6, LoadedBoardId, StringComparison.Ordinal) && string.Equals(a7, localPlayerIdString3, StringComparison.Ordinal))
				{
					DoorControlApi.CompletePendingDamageFromAuthority(requestId6, sdpLoss2, spLoss2, destroyed, currentSdp2, currentSp2);
				}
				return "";
			}
			if (text2 == "SYNC_REQUEST" && array.Length >= 2)
			{
				string text8 = DecodeText(array[1]);
				EnsureDoorDataLoadedForCurrentBoard();
				if (LocalClient.IsInGmMode && string.Equals(text8, LoadedBoardId, StringComparison.Ordinal))
				{
					foreach (DoorRecord value in DoorRecords.Values)
					{
						SendRpc(BuildStateMessage(text8, value));
					}
				}
				return "";
			}
			if (text2 == "LOCK_DENIED" && array.Length >= 4)
			{
				string a8 = DecodeText(array[1]);
				string a9 = DecodeText(array[3]);
				string localPlayerIdString4 = GetLocalPlayerIdString();
				if (string.Equals(a8, LoadedBoardId, StringComparison.Ordinal) && string.Equals(a9, localPlayerIdString4, StringComparison.Ordinal) && (Object)(object)Instance != (Object)null)
				{
					Instance.ShowToast("GM denied the lock request.");
				}
				return "";
			}
		}
		catch (Exception ex)
		{
			Log.LogError((object)("Door Control RPC handler failed: " + ex));
		}
		return "";
	}

	internal unsafe static string GetLocalPlayerIdString()
	{
		//IL_0000: Unknown result type (might be due to invalid IL or missing references)
		//IL_0005: Unknown result type (might be due to invalid IL or missing references)
		//IL_000a: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			NGuid value = LocalPlayer.Id.Value;
			return ((object)(*(NGuid*)(&value))/*cast due to .constrained prefix*/).ToString();
		}
		catch
		{
			return "";
		}
	}

	internal static string GetAddItemTitle(MethodBase method, object[] args)
	{
		if (method == null || args == null)
		{
			return null;
		}
		ParameterInfo[] parameters = method.GetParameters();
		for (int i = 0; i < parameters.Length && i < args.Length; i++)
		{
			if (parameters[i].ParameterType == typeof(string) && string.Equals(parameters[i].Name, "title", StringComparison.OrdinalIgnoreCase))
			{
				return args[i] as string;
			}
		}
		if (args.Length == 1 && args[0] != null)
		{
			return GetStringMember(args[0], "Title");
		}
		return null;
	}

	internal static bool ReplaceAddItemAction(MethodBase method, object[] args, Action<MapMenuItem, object> replacement)
	{
		if (method == null || args == null || replacement == null)
		{
			return false;
		}
		ParameterInfo[] parameters = method.GetParameters();
		for (int i = 0; i < parameters.Length && i < args.Length; i++)
		{
			Type parameterType = parameters[i].ParameterType;
			if (typeof(Delegate).IsAssignableFrom(parameterType) && parameterType.IsAssignableFrom(replacement.GetType()))
			{
				args[i] = replacement;
				return true;
			}
		}
		if (args.Length == 1 && args[0] != null)
		{
			object obj = args[0];
			Type type = obj.GetType();
			FieldInfo fieldInfo = FindFieldIgnoreCase(type, "Action");
			if (fieldInfo != null && fieldInfo.FieldType.IsAssignableFrom(replacement.GetType()))
			{
				fieldInfo.SetValue(obj, replacement);
				args[0] = obj;
				return true;
			}
			PropertyInfo propertyInfo = FindPropertyIgnoreCase(type, "Action");
			if (propertyInfo != null && propertyInfo.CanWrite && propertyInfo.PropertyType.IsAssignableFrom(replacement.GetType()))
			{
				propertyInfo.SetValue(obj, replacement, null);
				args[0] = obj;
				return true;
			}
		}
		return false;
	}

	internal static bool ReplaceAddItemTitle(MethodBase method, object[] args, string replacementTitle)
	{
		if (method == null || args == null)
		{
			return false;
		}
		ParameterInfo[] parameters = method.GetParameters();
		for (int i = 0; i < parameters.Length && i < args.Length; i++)
		{
			if (parameters[i].ParameterType == typeof(string) && string.Equals(parameters[i].Name, "title", StringComparison.OrdinalIgnoreCase))
			{
				args[i] = replacementTitle;
				return true;
			}
		}
		if (args.Length == 1 && args[0] != null)
		{
			object obj = args[0];
			Type type = obj.GetType();
			FieldInfo fieldInfo = FindFieldIgnoreCase(type, "Title");
			if (fieldInfo != null && fieldInfo.FieldType == typeof(string))
			{
				fieldInfo.SetValue(obj, replacementTitle);
				args[0] = obj;
				return true;
			}
			PropertyInfo propertyInfo = FindPropertyIgnoreCase(type, "Title");
			if (propertyInfo != null && propertyInfo.CanWrite && propertyInfo.PropertyType == typeof(string))
			{
				propertyInfo.SetValue(obj, replacementTitle, null);
				args[0] = obj;
				return true;
			}
		}
		return false;
	}

	internal static string GetStringMember(object obj, string name)
	{
		if (obj == null)
		{
			return null;
		}
		Type type = obj.GetType();
		FieldInfo fieldInfo = FindFieldIgnoreCase(type, name);
		if (fieldInfo != null)
		{
			return fieldInfo.GetValue(obj) as string;
		}
		PropertyInfo propertyInfo = FindPropertyIgnoreCase(type, name);
		if (propertyInfo != null && propertyInfo.CanRead)
		{
			return propertyInfo.GetValue(obj, null) as string;
		}
		return null;
	}

	internal static FieldInfo FindFieldIgnoreCase(Type type, string name)
	{
		FieldInfo[] fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
		for (int i = 0; i < fields.Length; i++)
		{
			if (string.Equals(fields[i].Name, name, StringComparison.OrdinalIgnoreCase))
			{
				return fields[i];
			}
		}
		return null;
	}

	internal static PropertyInfo FindPropertyIgnoreCase(Type type, string name)
	{
		PropertyInfo[] properties = type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
		for (int i = 0; i < properties.Length; i++)
		{
			if (string.Equals(properties[i].Name, name, StringComparison.OrdinalIgnoreCase))
			{
				return properties[i];
			}
		}
		return null;
	}
}
public static class DoorControlApi
{
	private static readonly Dictionary<string, ExternalDoorAction> LockedDoorActions = new Dictionary<string, ExternalDoorAction>(StringComparer.Ordinal);

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

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

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

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

	private static Action<DoorKeyRequest> KeyProvider = null;

	public static string CurrentBoardId
	{
		get
		{
			DoorControlPlugin.EnsureDoorDataLoadedForCurrentBoard();
			return DoorControlPlugin.LoadedBoardId;
		}
	}

	public static string DifficultyLabel => DoorControlPlugin.GetDifficultyTerm();

	public static bool HasKeyProvider => KeyProvider != null;

	public static event EventHandler<DoorStateChangedEventArgs> DoorStateChanged;

	public static event EventHandler<DoorCheckResolvedEventArgs> DoorCheckResolved;

	public static event EventHandler<DoorKeyResolvedEventArgs> DoorKeyResolved;

	public static event EventHandler<DoorDamageResolvedEventArgs> DoorDamageResolved;

	public static bool IsLocked(string doorId)
	{
		return DoorControlPlugin.IsLockedInternal(doorId);
	}

	public static DoorRecordSnapshot GetDoor(string doorId)
	{
		return DoorControlPlugin.GetSnapshotInternal(doorId);
	}

	public static bool KeyMatches(string doorId, string candidateCode)
	{
		return DoorControlPlugin.KeyMatchesInternal(doorId, candidateCode);
	}

	public static bool Lock(string doorId, int difficulty, string keyCode)
	{
		return DoorControlPlugin.ConfigureDoorLockInternal(doorId, locked: true, difficulty, keyCode, broadcast: true);
	}

	public static bool Unlock(string doorId)
	{
		return DoorControlPlugin.ConfigureDoorLockInternal(doorId, locked: false, null, null, broadcast: true);
	}

	public static bool ConfigureHealth(string doorId, int maxSdp, int currentSdp, int maxSp, int currentSp)
	{
		return DoorControlPlugin.ConfigureDoorHealthInternal(doorId, maxSdp > 0, maxSdp, currentSdp, maxSp, currentSp, broadcast: true);
	}

	public static void RegisterCheckHandler(string checkId, Action<DoorCheckRequest> callback)
	{
		if (string.IsNullOrEmpty(checkId))
		{
			throw new ArgumentException("Check id is required.", "checkId");
		}
		if (callback == null)
		{
			throw new ArgumentNullException("callback");
		}
		DoorCheckHandler doorCheckHandler = new DoorCheckHandler();
		doorCheckHandler.CheckId = checkId;
		doorCheckHandler.Callback = callback;
		CheckHandlers[checkId] = doorCheckHandler;
		if (DoorControlPlugin.Log != null)
		{
			DoorControlPlugin.Log.LogInfo((object)("Registered check handler: " + checkId + "."));
		}
	}

	public static bool UnregisterCheckHandler(string checkId)
	{
		if (string.IsNullOrEmpty(checkId))
		{
			return false;
		}
		return CheckHandlers.Remove(checkId);
	}

	public static void RegisterKeyProvider(Action<DoorKeyRequest> callback)
	{
		if (callback == null)
		{
			throw new ArgumentNullException("callback");
		}
		KeyProvider = callback;
		if (DoorControlPlugin.Log != null)
		{
			DoorControlPlugin.Log.LogInfo((object)"Registered key provider.");
		}
	}

	public static bool UnregisterKeyProvider()
	{
		bool result = KeyProvider != null;
		KeyProvider = null;
		return result;
	}

	internal static DoorCheckRequest BeginCheck(string checkId, string displayName, DoorContext door, int difficulty)
	{
		DoorCheckRequest doorCheckRequest = new DoorCheckRequest();
		doorCheckRequest.RequestId = Guid.NewGuid().ToString("N");
		doorCheckRequest.CheckId = checkId;
		doorCheckRequest.DisplayName = displayName;
		doorCheckRequest.Door = door;
		doorCheckRequest.Difficulty = Math.Max(1, difficulty);
		doorCheckRequest.DifficultyTerm = DoorControlPlugin.GetDifficultyTerm();
		PendingChecks[doorCheckRequest.RequestId] = doorCheckRequest;
		return doorCheckRequest;
	}

	internal static bool DispatchCheck(DoorCheckRequest request)
	{
		if (request == null)
		{
			return false;
		}
		if (!CheckHandlers.TryGetValue(request.CheckId, out var value) || value == null || value.Callback == null)
		{
			return false;
		}
		try
		{
			value.Callback(request);
			return true;
		}
		catch (Exception ex)
		{
			if (DoorControlPlugin.Log != null)
			{
				DoorControlPlugin.Log.LogError((object)("Check provider failed for " + request.CheckId + ": " + ex));
			}
			return false;
		}
	}

	public static bool SubmitCheckResult(string requestId, int total)
	{
		if (string.IsNullOrEmpty(requestId) || !PendingChecks.TryGetValue(requestId, out var value))
		{
			return false;
		}
		if (!LocalClient.IsInGmMode)
		{
			DoorControlPlugin.SendRpc(DoorControlPlugin.BuildCheckResultMessage(value, total, EvaluateDefaultSuccess(value, total)));
			return true;
		}
		DoorRecord orCreateDoorRecord = DoorControlPlugin.GetOrCreateDoorRecord(value.Door.DoorId);
		int difficulty = CheckDifficultyForCurrentState(value, orCreateDoorRecord);
		bool success = EvaluateDefaultSuccess(value, total);
		return CompleteCheckAsAuthority(value, success, total, difficulty, null);
	}

	public static bool ResolveCheck(string requestId, bool success, int total)
	{
		if (string.IsNullOrEmpty(requestId) || !PendingChecks.TryGetValue(requestId, out var value))
		{
			return false;
		}
		if (!LocalClient.IsInGmMode)
		{
			DoorControlPlugin.SendRpc(DoorControlPlugin.BuildCheckResultMessage(value, total, success));
			return true;
		}
		DoorRecord orCreateDoorRecord = DoorControlPlugin.GetOrCreateDoorRecord(value.Door.DoorId);
		return CompleteCheckAsAuthority(value, success, total, CheckDifficultyForCurrentState(value, orCreateDoorRecord), null);
	}

	internal static void ResolveRemoteCheckAsGm(string boardId, string doorId, string requestId, string checkId, int total, bool providerSuccess, string requester)
	{
		if (LocalClient.IsInGmMode)
		{
			DoorControlPlugin.EnsureDoorDataLoadedForCurrentBoard();
			if (string.Equals(boardId, DoorControlPlugin.LoadedBoardId, StringComparison.Ordinal) && !string.IsNullOrEmpty(doorId) && !string.IsNullOrEmpty(requestId))
			{
				DoorRecord orCreateDoorRecord = DoorControlPlugin.GetOrCreateDoorRecord(doorId);
				int difficulty = Math.Max(1, orCreateDoorRecord.LockDifficulty);
				bool success = providerSuccess;
				DoorContext door = DoorControlPlugin.BuildDoorContext(doorId, doorId, "Open");
				DoorCheckRequest doorCheckRequest = new DoorCheckRequest();
				doorCheckRequest.RequestId = requestId;
				doorCheckRequest.CheckId = (string.IsNullOrEmpty(checkId) ? "pick-lock" : checkId);
				doorCheckRequest.DisplayName = (string.Equals(doorCheckRequest.CheckId, "pick-lock", StringComparison.OrdinalIgnoreCase) ? "Pick Lock" : (string.Equals(doorCheckRequest.CheckId, "jam", StringComparison.OrdinalIgnoreCase) ? "Jam Door" : (string.Equals(doorCheckRequest.CheckId, "clear-jam", StringComparison.OrdinalIgnoreCase) ? "Clear Jam" : (string.Equals(doorCheckRequest.CheckId, "barricade", StringComparison.OrdinalIgnoreCase) ? "Barricade" : doorCheckRequest.CheckId))));
				doorCheckRequest.Door = door;
				doorCheckRequest.Difficulty = difficulty;
				doorCheckRequest.DifficultyTerm = DoorControlPlugin.GetDifficultyTerm();
				difficulty = (doorCheckRequest.Difficulty = CheckDifficultyForCurrentState(doorCheckRequest, orCreateDoorRecord));
				CompleteCheckAsAuthority(doorCheckRequest, success, total, difficulty, requester);
			}
		}
	}

	private static int CheckDifficultyForCurrentState(DoorCheckRequest request, DoorRecord record)
	{
		if (request == null)
		{
			return 1;
		}
		switch (string.IsNullOrEmpty(request.CheckId) ? "" : request.CheckId.ToLowerInvariant())
		{
		case "clear-jam":
			return Math.Max(1, record?.JamStrength ?? request.Difficulty);
		case "barricade":
			return 15;
		case "jam":
		case "pick-lock":
			return Math.Max(1, record?.LockDifficulty ?? request.Difficulty);
		default:
			return Math.Max(1, request.Difficulty);
		}
	}

	private static bool EvaluateDefaultSuccess(DoorCheckRequest request, int total)
	{
		if (request == null)
		{
			return false;
		}
		DoorRecord orCreateDoorRecord = DoorControlPlugin.GetOrCreateDoorRecord(request.Door.DoorId);
		int num = CheckDifficultyForCurrentState(request, orCreateDoorRecord);
		switch (string.IsNullOrEmpty(request.CheckId) ? "" : request.CheckId.ToLowerInvariant())
		{
		case "pick-lock":
		case "jam":
		case "clear-jam":
		case "barricade":
			return total > num;
		default:
			return total >= num;
		}
	}

	private static bool CompleteCheckAsAuthority(DoorCheckRequest request, bool success, int total, int difficulty, string requester)
	{
		if (request == null || request.Door == null)
		{
			return false;
		}
		PendingChecks.Remove(request.RequestId);
		request.Difficulty = Math.Max(1, difficulty);
		request.DifficultyTerm = DoorControlPlugin.GetDifficultyTerm();
		string text = (string.IsNullOrEmpty(request.CheckId) ? "" : request.CheckId.ToLowerInvariant());
		DoorRecord orCreateDoorRecord = DoorControlPlugin.GetOrCreateDoorRecord(request.Door.DoorId);
		if (success)
		{
			switch (text)
			{
			case "pick-lock":
				success = orCreateDoorRecord.IsLocked && orCreateDoorRecord.Obstruction == DoorObstructionState.Normal;
				if (success)
				{
					DoorControlPlugin.ConfigureDoorLockInternal(request.Door.DoorId, locked: false, null, null, broadcast: true);
				}
				break;
			case "jam":
				success = orCreateDoorRecord.Obstruction == DoorObstructionState.Normal;
				if (success)
				{
					DoorControlPlugin.SetObstructionInternal(request.Door.DoorId, DoorObstructionState.Jammed, total, broadcast: true);
				}
				break;
			case "clear-jam":
				success = orCreateDoorRecord.Obstruction == DoorObstructionState.Jammed;
				if (success)
				{
					DoorControlPlugin.ClearObstructionInternal(request.Door.DoorId, DoorObstructionState.Jammed, broadcast: true);
				}
				break;
			case "barricade":
				success = orCreateDoorRecord.Obstruction == DoorObstructionState.Normal;
				if (success)
				{
					DoorControlPlugin.SetObstructionInternal(request.Door.DoorId, DoorObstructionState.Barricaded, 0, broadcast: true);
				}
				break;
			}
		}
		if (requester == null)
		{
			ShowCheckResultToast(request, success, total);
		}
		else
		{
			DoorControlPlugin.SendRpc(DoorControlPlugin.BuildCheckResolutionMessage(request.Door.BoardId, request.Door.DoorId, request.RequestId, requester, success, total, request.Difficulty));
		}
		RaiseDoorCheckResolved(request, success, total);
		return success;
	}

	internal static void CompletePendingCheckFromAuthority(string requestId, bool success, int total, int difficulty)
	{
		if (!string.IsNullOrEmpty(requestId) && PendingChecks.TryGetValue(requestId, out var value))
		{
			PendingChecks.Remove(requestId);
			value.Difficulty = Math.Max(1, difficulty);
			value.DifficultyTerm = DoorControlPlugin.GetDifficultyTerm();
			ShowCheckResultToast(value, success, total);
			RaiseDoorCheckResolved(value, success, total);
		}
	}

	private static void ShowCheckResultToast(DoorCheckRequest request, bool success, int total)
	{
		if (!((Object)(object)DoorControlPlugin.Instance == (Object)null) && request != null)
		{
			string text = (string.IsNullOrEmpty(request.CheckId) ? "" : request.CheckId.ToLowerInvariant());
			if (success && text == "pick-lock")
			{
				DoorControlPlugin.Instance.ShowToast("The lock opens. The door is now unlocked.");
			}
			else if (success && text == "jam")
			{
				DoorControlPlugin.Instance.ShowToast("The door is now jammed.");
			}
			else if (success && text == "clear-jam")
			{
				DoorRecord orCreateDoorRecord = DoorControlPlugin.GetOrCreateDoorRecord(request.Door.DoorId);
				DoorControlPlugin.Instance.ShowToast(orCreateDoorRecord.IsLocked ? "The jam is cleared. The door is locked." : "The jam is cleared. The door is unlocked.");
			}
			else if (success && text == "barricade")
			{
				DoorControlPlugin.Instance.ShowToast("The door is now barricaded.");
			}
			else if (success)
			{
				DoorControlPlugin.Instance.ShowToast(request.DisplayName + " succeeded: " + total + " vs " + request.DifficultyTerm + " " + request.Difficulty + ".");
			}
			else
			{
				DoorControlPlugin.Instance.ShowToast(request.DisplayName + " failed: " + total + " vs " + request.DifficultyTerm + " " + request.Difficulty + ".");
			}
		}
	}

	public static bool CancelCheck(string requestId)
	{
		if (string.IsNullOrEmpty(requestId))
		{
			return false;
		}
		return PendingChecks.Remove(requestId);
	}

	internal static DoorKeyRequest BeginKeyAttempt(DoorContext door)
	{
		if (door == null || string.IsNullOrEmpty(door.DoorId))
		{
			return null;
		}
		DoorKeyRequest doorKeyRequest = new DoorKeyRequest();
		doorKeyRequest.RequestId = Guid.NewGuid().ToString("N");
		doorKeyRequest.Door = door;
		PendingKeyAttempts[doorKeyRequest.RequestId] = doorKeyRequest;
		return doorKeyRequest;
	}

	internal static bool DispatchKeyAttempt(DoorKeyRequest request)
	{
		if (request == null || KeyProvider == null)
		{
			return false;
		}
		try
		{
			KeyProvider(request);
			return true;
		}
		catch (Exception ex)
		{
			if (DoorControlPlugin.Log != null)
			{
				DoorControlPlugin.Log.LogError((object)("Key provider failed: " + ex));
			}
			return false;
		}
	}

	public static bool SubmitKeyCodes(string requestId, IEnumerable<string> candidateCodes)
	{
		if (string.IsNullOrEmpty(requestId) || !PendingKeyAttempts.TryGetValue(requestId, out var value))
		{
			return false;
		}
		List<string> candidateCodes2 = NormalizeCandidateCodes(candidateCodes);
		if (!LocalClient.IsInGmMode)
		{
			DoorControlPlugin.SendRpc(DoorControlPlugin.BuildKeyAttemptMessage(value, candidateCodes2));
			return true;
		}
		bool success = CandidateCodesMatch(value.Door.DoorId, candidateCodes2);
		return CompleteKeyAsAuthority(value, success, null);
	}

	internal static void ResolveRemoteKeyAttemptAsGm(string boardId, string doorId, string requestId, IEnumerable<string> candidateCodes, string requester)
	{
		if (LocalClient.IsInGmMode)
		{
			DoorControlPlugin.EnsureDoorDataLoadedForCurrentBoard();
			if (string.Equals(boardId, DoorControlPlugin.LoadedBoardId, StringComparison.Ordinal))
			{
				DoorContext door = DoorControlPlugin.BuildDoorContext(doorId, doorId, "Open");
				DoorKeyRequest doorKeyRequest = new DoorKeyRequest();
				doorKeyRequest.RequestId = requestId;
				doorKeyRequest.Door = door;
				bool success = CandidateCodesMatch(doorId, NormalizeCandidateCodes(candidateCodes));
				CompleteKeyAsAuthority(doorKeyRequest, success, requester);
			}
		}
	}

	private static bool CompleteKeyAsAuthority(DoorKeyRequest request, bool success, string requester)
	{
		if (request == null || request.Door == null)
		{
			return false;
		}
		PendingKeyAttempts.Remove(request.RequestId);
		DoorRecord orCreateDoorRecord = DoorControlPlugin.GetOrCreateDoorRecord(request.Door.DoorId);
		success = success && orCreateDoorRecord.IsLocked && orCreateDoorRecord.KeyConfigured && orCreateDoorRecord.Obstruction == DoorObstructionState.Normal;
		if (success)
		{
			DoorControlPlugin.ConfigureDoorLockInternal(request.Door.DoorId, locked: false, null, null, broadcast: true);
		}
		if (requester == null)
		{
			if ((Object)(object)DoorControlPlugin.Instance != (Object)null)
			{
				DoorControlPlugin.Instance.ShowToast(success ? "The key works. The door is now unlocked." : "No matching key.");
			}
		}
		else
		{
			DoorControlPlugin.SendRpc(DoorControlPlugin.BuildKeyResultMessage(request.Door.BoardId, request.Door.DoorId, request.RequestId, requester, success));
		}
		RaiseDoorKeyResolved(request, success);
		return success;
	}

	internal static void CompletePendingKeyFromAuthority(string requestId, bool success)
	{
		if (!string.IsNullOrEmpty(requestId) && PendingKeyAttempts.TryGetValue(requestId, out var value))
		{
			PendingKeyAttempts.Remove(requestId);
			if ((Object)(object)DoorControlPlugin.Instance != (Object)null)
			{
				DoorControlPlugin.Instance.ShowToast(success ? "The key works. The door is now unlocked." : "No matching key.");
			}
			RaiseDoorKeyResolved(value, success);
		}
	}

	public static bool CancelKeyAttempt(string requestId)
	{
		if (string.IsNullOrEmpty(requestId))
		{
			return false;
		}
		return PendingKeyAttempts.Remove(requestId);
	}

	private static List<string> NormalizeCandidateCodes(IEnumerable<string> candidateCodes)
	{
		List<string> list = new List<string>();
		HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
		if (candidateCodes == null)
		{
			return list;
		}
		foreach (string candidateCode in candidateCodes)
		{
			string text = ((candidateCode == null) ? "" : candidateCode.Trim());
			if (text.Length != 0 && hashSet.Add(text))
			{
				list.Add(text);
			}
		}
		return list;
	}

	private static bool CandidateCodesMatch(string doorId, IList<string> candidateCodes)
	{
		if (candidateCodes == null)
		{
			return false;
		}
		for (int i = 0; i < candidateCodes.Count; i++)
		{
			if (DoorControlPlugin.KeyMatchesInternal(doorId, candidateCodes[i]))
			{
				return true;
			}
		}
		return false;
	}

	internal static DoorDamageRequest BeginDamage(DoorContext door)
	{
		if (door == null || string.IsNullOrEmpty(door.DoorId))
		{
			return null;
		}
		DoorRecord orCreateDoorRecord = DoorControlPlugin.GetOrCreateDoorRecord(door.DoorId);
		if (!orCreateDoorRecord.HealthConfigured || orCreateDoorRecord.IsDestroyed)
		{
			return null;
		}
		DoorDamageRequest doorDamageRequest = new DoorDamageRequest();
		doorDamageRequest.RequestId = Guid.NewGuid().ToString("N");
		doorDamageRequest.Door = DoorControlPlugin.BuildDoorContext(door.DoorId, door.ContentId, door.NativeAction);
		PendingDamage[doorDamageRequest.RequestId] = doorDamageRequest;
		return doorDamageRequest;
	}

	public static bool SubmitDoorDamage(string requestId, int rawDamage, int sdpLoss, int spLoss, string source)
	{
		if (string.IsNullOrEmpty(requestId) || !PendingDamage.TryGetValue(requestId, out var value))
		{
			return false;
		}
		if (!LocalClient.IsInGmMode)
		{
			DoorControlPlugin.SendRpc(DoorControlPlugin.BuildDamageAttemptMessage(value, rawDamage, sdpLoss, spLoss, source));
			return true;
		}
		return CompleteDamageAsAuthority(value, rawDamage, sdpLoss, spLoss, source, null) != null;
	}

	internal static void ResolveRemoteDamageAsGm(string boardId, string doorId, string requestId, int rawDamage, int sdpLoss, int spLoss, string source, string requester)
	{
		if (LocalClient.IsInGmMode)
		{
			DoorControlPlugin.EnsureDoorDataLoadedForCurrentBoard();
			if (string.Equals(boardId, DoorControlPlugin.LoadedBoardId, StringComparison.Ordinal))
			{
				DoorContext door = DoorControlPlugin.BuildDoorContext(doorId, doorId, "Open");
				DoorDamageRequest doorDamageRequest = new DoorDamageRequest();
				doorDamageRequest.RequestId = requestId;
				doorDamageRequest.Door = door;
				CompleteDamageAsAuthority(doorDamageRequest, rawDamage, sdpLoss, spLoss, source, requester);
			}
		}
	}

	private static DoorDamageResult CompleteDamageAsAuthority(DoorDamageRequest request, int rawDamage, int sdpLoss, int spLoss, string source, string requester)
	{
		if (request == null || request.Door == null)
		{
			return null;
		}
		PendingDamage.Remove(request.RequestId);
		DoorDamageResult doorDamageResult = DoorControlPlugin.ApplyDoorDamageInternal(request.Door.DoorId, rawDamage, sdpLoss, spLoss, source, broadcast: true);
		if (doorDamageResult == null)
		{
			return null;
		}
		if (requester == null)
		{
			ShowDamageToast(doorDamageResult);
		}
		else
		{
			DoorControlPlugin.SendRpc(DoorControlPlugin.BuildDamageResolutionMessage(request.Door.BoardId, request.Door.DoorId, request.RequestId, requester, doorDamageResult));
		}
		RaiseDoorDamageResolved(request, doorDamageResult);
		return doorDamageResult;
	}

	internal static void CompletePendingDamageFromAuthority(string requestId, int sdpLoss, int spLoss, bool destroyed, int currentSdp, int currentSp)
	{
		if (!string.IsNullOrEmpty(requestId) && PendingDamage.TryGetValue(requestId, out var value))
		{
			PendingDamage.Remove(requestId);
			DoorDamageResult doorDamageResult = new DoorDamageResult();
			doorDamageResult.DoorId = ((value.Door == null) ? "" : value.Door.DoorId);
			doorDamageResult.SDPLoss = sdpLoss;
			doorDamageResult.SPLoss = spLoss;
			doorDamageResult.Destroyed = destroyed;
			doorDamageResult.AfterSDP = currentSdp;
			doorDamageResult.AfterSP = currentSp;
			ShowDamageToast(doorDamageResult);
			RaiseDoorDamageResolved(value, doorDamageResult);
		}
	}

	private static void ShowDamageToast(DoorDamageResult result)
	{
		if (!((Object)(object)DoorControlPlugin.Instance == (Object)null) && result != null)
		{
			if (result.Destroyed)
			{
				DoorControlPlugin.Instance.ShowToast("The door is destroyed.");
				return;
			}
			if (result.SDPLoss <= 0 && result.SPLoss <= 0)
			{
				DoorControlPlugin.Instance.ShowToast("The attack fails to damage the door.");
				return;
			}
			DoorControlPlugin.Instance.ShowToast("Door takes " + result.SDPLoss + " SDP damage" + ((result.SPLoss > 0) ? (" and loses " + result.SPLoss + " SP") : "") + ". SDP " + result.AfterSDP + ", SP " + result.AfterSP + ".");
		}
	}

	public static bool CancelDamage(string requestId)
	{
		if (string.IsNullOrEmpty(requestId))
		{
			return false;
		}
		return PendingDamage.Remove(requestId);
	}

	public static void RegisterLockedDoorAction(string id, string title, Action<DoorContext> callback)
	{
		RegisterLockedDoorAction(id, title, callback, null);
	}

	public static void RegisterLockedDoorAction(string id, string title, Action<DoorContext> callback, Func<DoorContext, bool> isVisible)
	{
		if (string.IsNullOrEmpty(id))
		{
			throw new ArgumentException("Action id is required.", "id");
		}
		if (string.IsNullOrEmpty(title))
		{
			throw new ArgumentException("Action title is required.", "title");
		}
		if (callback == null)
		{
			throw new ArgumentNullException("callback");
		}
		ExternalDoorAction externalDoorAction = new ExternalDoorAction();
		externalDoorAction.Id = id;
		externalDoorAction.Title = title;
		externalDoorAction.Callback = callback;
		externalDoorAction.IsVisible = isVisible;
		LockedDoorActions[id] = externalDoorAction;
	}

	public static bool UnregisterLockedDoorAction(string id)
	{
		if (string.IsNullOrEmpty(id))
		{
			return false;
		}
		return LockedDoorActions.Remove(id);
	}

	internal static ExternalDoorAction[] GetRegisteredLockedDoorActionsSnapshot()
	{
		ExternalDoorAction[] array = new ExternalDoorAction[LockedDoorActions.Count];
		LockedDoorActions.Values.CopyTo(array, 0);
		return array;
	}

	internal static void RaiseDoorStateChanged(string boardId, string doorId, bool locked)
	{
		EventHandler<DoorStateChangedEventArgs> doorStateChanged = DoorControlApi.DoorStateChanged;
		if (doorStateChanged == null)
		{
			return;
		}
		try
		{
			doorStateChanged(null, new DoorStateChangedEventArgs(boardId, doorId, locked));
		}
		catch (Exception ex)
		{
			if (DoorControlPlugin.Log != null)
			{
				DoorControlPlugin.Log.LogError((object)("DoorStateChanged subscriber failed: " + ex));
			}
		}
	}

	internal static void RaiseDoorCheckResolved(DoorCheckRequest request, bool success, int total)
	{
		EventHandler<DoorCheckResolvedEventArgs> doorCheckResolved = DoorControlApi.DoorCheckResolved;
		if (doorCheckResolved == null)
		{
			return;
		}
		try
		{
			doorCheckResolved(null, new DoorCheckResolvedEventArgs(request, success, total));
		}
		catch (Exception ex)
		{
			if (DoorControlPlugin.Log != null)
			{
				DoorControlPlugin.Log.LogError((object)("DoorCheckResolved subscriber failed: " + ex));
			}
		}
	}

	internal static void RaiseDoorKeyResolved(DoorKeyRequest request, bool success)
	{
		EventHandler<DoorKeyResolvedEventArgs> doorKeyResolved = DoorControlApi.DoorKeyResolved;
		if (doorKeyResolved == null)
		{
			return;
		}
		try
		{
			doorKeyResolved(null, new DoorKeyResolvedEventArgs(request, success));
		}
		catch (Exception ex)
		{
			if (DoorControlPlugin.Log != null)
			{
				DoorControlPlugin.Log.LogError((object)("DoorKeyResolved subscriber failed: " + ex));
			}
		}
	}

	internal static void RaiseDoorDamageResolved(DoorDamageRequest request, DoorDamageResult result)
	{
		EventHandler<DoorDamageResolvedEventArgs> doorDamageResolved = DoorControlApi.DoorDamageResolved;
		if (doorDamageResolved == null)
		{
			return;
		}
		try
		{
			doorDamageResolved(null, new DoorDamageResolvedEventArgs(request, result));
		}
		catch (Exception ex)
		{
			if (DoorControlPlugin.Log != null)
			{
				DoorControlPlugin.Log.LogError((object)("DoorDamageResolved subscriber failed: " + ex));
			}
		}
	}
}
public enum DoorObstructionState
{
	Normal,
	Jammed,
	Barricaded
}
internal sealed class DoorRecord
{
	internal string DoorId;

	internal bool IsLocked;

	internal int LockDifficulty;

	internal string KeyCode;

	internal bool KeyConfigured;

	internal DoorObstructionState Obstruction;

	internal int JamStrength;

	internal bool PreviousLockState;

	internal bool HealthConfigured;

	internal int MaxSDP;

	internal int CurrentSDP;

	internal int MaxSP;

	internal int CurrentSP;

	internal bool IsDestroyed
	{
		get
		{
			if (HealthConfigured && MaxSDP > 0)
			{
				return CurrentSDP <= 0;
			}
			return false;
		}
	}
}
public sealed class DoorRecordSnapshot
{
	public string DoorId { get; private set; }

	public bool IsLocked { get; private set; }

	public int LockDifficulty { get; private set; }

	public bool HasKeyCode { get; private set; }

	public DoorObstructionState Obstruction { get; private set; }

	public int JamStrength { get; private set; }

	public bool PreviousLockState { get; private set; }

	public bool HealthConfigured { get; private set; }

	public int MaxSDP { get; private set; }

	public int CurrentSDP { get; private set; }

	public int MaxSP { get; private set; }

	public int CurrentSP { get; private set; }

	public bool IsDestroyed
	{
		get
		{
			if (HealthConfigured && MaxSDP > 0)
			{
				return CurrentSDP <= 0;
			}
			return false;
		}
	}

	public DoorRecordSnapshot(string doorId, bool isLocked, int lockDifficulty, bool hasKeyCode, DoorObstructionState obstruction, int jamStrength, bool previousLockState, bool healthConfigured, int maxSdp, int currentSdp, int maxSp, int currentSp)
	{
		DoorId = doorId;
		IsLocked = isLocked;
		LockDifficulty = lockDifficulty;
		HasKeyCode = hasKeyCode;
		Obstruction = obstruction;
		JamStrength = jamStrength;
		PreviousLockState = previousLockState;
		HealthConfigured = healthConfigured;
		MaxSDP = maxSdp;
		CurrentSDP = currentSdp;
		MaxSP = maxSp;
		CurrentSP = currentSp;
	}
}
public sealed class DoorContext
{
	public string DoorId { get; internal set; }

	public string ContentId { get; internal set; }

	public string BoardId { get; internal set; }

	public string NativeAction { get; internal set; }

	public bool IsLocked { get; internal set; }

	public int LockDifficulty { get; internal set; }

	public bool HasKeyCode { get; internal set; }

	public DoorObstructionState Obstruction { get; internal set; }

	public bool HealthConfigured { get; internal set; }

	public int MaxSDP { get; internal set; }

	public int CurrentSDP { get; internal set; }

	public int MaxSP { get; internal set; }

	public int CurrentSP { get; internal set; }

	public bool IsDestroyed { get; internal set; }

	public bool IsGm { get; internal set; }
}
public sealed class DoorCheckRequest
{
	public string RequestId { get; internal set; }

	public string CheckId { get; internal set; }

	public string DisplayName { get; internal set; }

	public DoorContext Door { get; internal set; }

	public int Difficulty { get; internal set; }

	public string DifficultyTerm { get; internal set; }
}
public sealed class DoorKeyRequest
{
	public string RequestId { get; internal set; }

	public DoorContext Door { get; internal set; }
}
public sealed class DoorDamageRequest
{
	public string RequestId { get; internal set; }

	public DoorContext Door { get; internal set; }
}
public sealed class DoorDamageResult
{
	public string DoorId { get; internal set; }

	public strin