Decompiled source of DoorControl v0.9.0
BepInEx/plugins/DoorControl/DoorControl.dll
Decompiled a week ago
The result has been truncated due to the large size, download it to view full contents!
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