Decompiled source of LC ControlTipKeys v1.0.0

LC-ControlTipKeys.dll

Decompiled a day ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using GameNetcodeStuff;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using TMPro;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Utilities;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyCompany("LC-ControlTipKeys")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("LC-ControlTipKeys")]
[assembly: AssemblyTitle("LC-ControlTipKeys")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace ControlTipKeys
{
	internal enum TipContext
	{
		ControlTipLine,
		HoverPrompt,
		VotingTip
	}
	internal sealed class TokenRule
	{
		public readonly string ActionName;

		public readonly string KeyboardPath;

		public readonly string GamepadPath;

		public TokenRule(string actionName, string keyboardPath, string gamepadPath)
		{
			ActionName = actionName;
			KeyboardPath = keyboardPath;
			GamepadPath = gamepadPath;
		}
	}
	internal static class KeybindResolver
	{
		private static readonly Regex TokenRegex = new Regex("\\[\\s*([A-Za-z][A-Za-z0-9-]*)\\s*\\]", RegexOptions.Compiled);

		private static readonly Regex ControlPathRegex = new Regex("^<([A-Za-z0-9]+)>/([A-Za-z0-9/]+)$", RegexOptions.Compiled);

		private static readonly Dictionary<string, TokenRule> ControlTipLineRules = new Dictionary<string, TokenRule>
		{
			["G"] = new TokenRule("Discard", "<Keyboard>/g", "<Gamepad>/buttonEast"),
			["E"] = new TokenRule("QEItemInteract", "<Keyboard>/e", "<Gamepad>/dpad/up"),
			["Q"] = new TokenRule("QEItemInteract", "<Keyboard>/q", "<Gamepad>/dpad/down"),
			["LMB"] = new TokenRule("Use", "<Mouse>/leftButton", "<Gamepad>/rightTrigger"),
			["RMB"] = new TokenRule("ActivateItem", "<Mouse>/leftButton", "<Gamepad>/rightTrigger"),
			["TAB"] = new TokenRule("UseUtilitySlot", "<Keyboard>/tab", "<Gamepad>/leftShoulder"),
			["START"] = new TokenRule("OpenMenu", "<Keyboard>/escape", "<Gamepad>/start"),
			["R-TRIGGER"] = new TokenRule("PingScan", "<Mouse>/rightButton", "<Gamepad>/rightShoulder")
		};

		private static readonly Dictionary<string, TokenRule> HoverRules = new Dictionary<string, TokenRule>
		{
			["G"] = new TokenRule("Discard", "<Keyboard>/g", "<Gamepad>/buttonEast"),
			["E"] = new TokenRule("Interact", "<Keyboard>/e", "<Gamepad>/buttonWest"),
			["LMB"] = new TokenRule("Interact", "<Keyboard>/e", "<Gamepad>/buttonWest"),
			["RMB"] = new TokenRule("PingScan", "<Mouse>/rightButton", "<Gamepad>/rightShoulder"),
			["Q"] = new TokenRule("QEItemInteract", "<Keyboard>/q", "<Gamepad>/dpad/down"),
			["TAB"] = new TokenRule("UseUtilitySlot", "<Keyboard>/tab", "<Gamepad>/leftShoulder"),
			["START"] = new TokenRule("OpenMenu", "<Keyboard>/escape", "<Gamepad>/start"),
			["R-TRIGGER"] = new TokenRule("PingScan", "<Mouse>/rightButton", "<Gamepad>/rightShoulder")
		};

		private static readonly Dictionary<string, TokenRule> VotingRules = new Dictionary<string, TokenRule>
		{
			["RMB"] = new TokenRule("PingScan", "<Mouse>/rightButton", "<Gamepad>/rightShoulder"),
			["R-TRIGGER"] = new TokenRule("PingScan", "<Mouse>/rightButton", "<Gamepad>/rightShoulder")
		};

		private static readonly Dictionary<string, string> MouseNames = new Dictionary<string, string>
		{
			["leftButton"] = "LMB",
			["rightButton"] = "RMB",
			["middleButton"] = "MMB",
			["forwardButton"] = "Mouse4",
			["backButton"] = "Mouse5",
			["scroll/up"] = "Scroll Up",
			["scroll/down"] = "Scroll Down",
			["scroll/y"] = "Scroll"
		};

		private static readonly Dictionary<string, string> KeyboardNames = new Dictionary<string, string>
		{
			["tab"] = "TAB",
			["space"] = "SPACE",
			["escape"] = "ESC",
			["enter"] = "ENTER",
			["leftShift"] = "LSHIFT",
			["rightShift"] = "RSHIFT",
			["leftCtrl"] = "LCTRL",
			["rightCtrl"] = "RCTRL",
			["leftAlt"] = "LALT",
			["rightAlt"] = "RALT",
			["upArrow"] = "UP",
			["downArrow"] = "DOWN",
			["leftArrow"] = "LEFT",
			["rightArrow"] = "RIGHT",
			["slash"] = "/",
			["comma"] = ",",
			["period"] = ".",
			["semicolon"] = ";",
			["quote"] = "'",
			["backquote"] = "`",
			["minus"] = "-",
			["equals"] = "=",
			["leftBracket"] = "[",
			["rightBracket"] = "]",
			["backslash"] = "\\",
			["backspace"] = "BACKSPACE",
			["capsLock"] = "CAPS",
			["delete"] = "DEL",
			["home"] = "HOME",
			["end"] = "END",
			["pageUp"] = "PGUP",
			["pageDown"] = "PGDN",
			["insert"] = "INS"
		};

		private static readonly Dictionary<string, string> GamepadNames = new Dictionary<string, string>
		{
			["buttonSouth"] = "A",
			["buttonEast"] = "B",
			["buttonWest"] = "X",
			["buttonNorth"] = "Y",
			["dpad/up"] = "D-pad up",
			["dpad/down"] = "D-pad down",
			["dpad/left"] = "D-pad left",
			["dpad/right"] = "D-pad right",
			["rightTrigger"] = "R-Trigger",
			["leftTrigger"] = "L-Trigger",
			["rightShoulder"] = "R-shoulder",
			["leftShoulder"] = "L-shoulder",
			["start"] = "Start",
			["select"] = "Select",
			["leftStickPress"] = "L-stick",
			["rightStickPress"] = "R-stick"
		};

		public static bool IsUsingController()
		{
			StartOfRound instance = StartOfRound.Instance;
			if ((Object)(object)instance != (Object)null)
			{
				return instance.localPlayerUsingController;
			}
			return false;
		}

		public static string ReplaceTokens(string text, TipContext context)
		{
			if (string.IsNullOrEmpty(text) || text.IndexOf('[') < 0)
			{
				return text;
			}
			bool gamepad = IsUsingController();
			string text2 = TokenRegex.Replace(text, delegate(Match match)
			{
				string text3 = ResolveToken(match.Groups[1].Value.ToUpperInvariant(), context, gamepad);
				return (text3 == null) ? match.Value : ("[" + text3 + "]");
			});
			if ((object)text2 != text && text2 != text)
			{
				ConfigEntry<bool> debugLogging = Plugin.DebugLogging;
				if (debugLogging != null && debugLogging.Value)
				{
					ManualLogSource log = Plugin.Log;
					if (log != null)
					{
						log.LogDebug((object)$"[{context}] '{text}' -> '{text2}'");
					}
				}
			}
			return text2;
		}

		public static string ResolveToken(string tokenUpper, TipContext context, bool gamepad)
		{
			if (!(context switch
			{
				TipContext.HoverPrompt => HoverRules, 
				TipContext.VotingTip => VotingRules, 
				_ => ControlTipLineRules, 
			}).TryGetValue(tokenUpper, out var value))
			{
				return null;
			}
			return GetBindingDisplay(value, gamepad);
		}

		public static string GetBindingDisplay(TokenRule rule, bool gamepad)
		{
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				InputActionAsset actions = InputSystem.actions;
				InputAction val = (((Object)(object)actions != (Object)null) ? actions.FindAction(rule.ActionName, false) : null);
				if (val == null)
				{
					return null;
				}
				string text = (gamepad ? rule.GamepadPath : rule.KeyboardPath);
				InputBinding? val2 = null;
				ReadOnlyArray<InputBinding> bindings = val.bindings;
				for (int i = 0; i < bindings.Count; i++)
				{
					InputBinding value = bindings[i];
					if (((InputBinding)(ref value)).isComposite)
					{
						continue;
					}
					string effectivePath = ((InputBinding)(ref value)).effectivePath;
					if (!string.IsNullOrEmpty(effectivePath) && !(effectivePath == "[Disabled]") && effectivePath.StartsWith("<Gamepad>", StringComparison.OrdinalIgnoreCase) == gamepad)
					{
						if (text != null && string.Equals(((InputBinding)(ref value)).path, text, StringComparison.OrdinalIgnoreCase))
						{
							val2 = value;
							break;
						}
						if (!val2.HasValue)
						{
							val2 = value;
						}
					}
				}
				object result;
				if (!val2.HasValue)
				{
					result = null;
				}
				else
				{
					InputBinding value2 = val2.Value;
					result = NormalizePath(((InputBinding)(ref value2)).effectivePath);
				}
				return (string)result;
			}
			catch (Exception)
			{
				return null;
			}
		}

		public static string NormalizePath(string effectivePath)
		{
			if (string.IsNullOrEmpty(effectivePath))
			{
				return null;
			}
			try
			{
				Match match = ControlPathRegex.Match(effectivePath);
				if (match.Success)
				{
					string value = match.Groups[1].Value;
					string value2 = match.Groups[2].Value;
					switch (value)
					{
					case "Mouse":
					{
						string value5;
						return MouseNames.TryGetValue(value2, out value5) ? value5 : null;
					}
					case "Keyboard":
					{
						if (KeyboardNames.TryGetValue(value2, out var value4))
						{
							return value4;
						}
						return (value2.Length == 1) ? value2.ToUpperInvariant() : null;
					}
					case "Gamepad":
					{
						string value3;
						return GamepadNames.TryGetValue(value2, out value3) ? value3 : null;
					}
					}
				}
				string text = InputControlPath.ToHumanReadableString(effectivePath, (HumanReadableStringOptions)2, (InputControl)null);
				return string.IsNullOrWhiteSpace(text) ? null : text.Trim();
			}
			catch (Exception)
			{
				return null;
			}
		}
	}
	[HarmonyPatch]
	internal static class Patches
	{
		private static readonly TokenRule BuildModeConfirmRule = new TokenRule("ConfirmBuildMode", "<Keyboard>/v", null);

		private static readonly TokenRule BuildModeStoreRule = new TokenRule("Delete", "<Keyboard>/x", null);

		private static readonly Regex BuildConfirmRegex = new Regex("Confirm: \\[\\s*\\w+\\s*\\]", RegexOptions.Compiled);

		private static readonly Regex BuildStoreRegex = new Regex("Store: \\[\\s*\\w+\\s*\\]", RegexOptions.Compiled);

		[HarmonyPrefix]
		[HarmonyPatch(typeof(HUDManager), "ChangeControlTipMultiple")]
		internal static void ChangeControlTipMultiple_Prefix(ref string[] allLines)
		{
			if (!Plugin.ControlTipsOn || allLines == null)
			{
				return;
			}
			try
			{
				string[] array = allLines;
				bool flag = false;
				for (int i = 0; i < allLines.Length; i++)
				{
					string text = KeybindResolver.ReplaceTokens(allLines[i], TipContext.ControlTipLine);
					if (text != null && text != allLines[i])
					{
						if (!flag)
						{
							array = (string[])allLines.Clone();
							flag = true;
						}
						array[i] = text;
					}
				}
				if (flag)
				{
					allLines = array;
				}
			}
			catch (Exception ex)
			{
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogError((object)("ChangeControlTipMultiple prefix failed: " + ex));
				}
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(HUDManager), "ChangeControlTipMultiple")]
		internal static void ChangeControlTipMultiple_Postfix(bool holdingItem)
		{
			if (!Plugin.ControlTipsOn || !holdingItem)
			{
				return;
			}
			try
			{
				TextMeshProUGUI[] array = HUDManager.Instance?.controlTipLines;
				if (array != null && array.Length != 0 && !((Object)(object)array[0] == (Object)null))
				{
					((TMP_Text)array[0]).text = KeybindResolver.ReplaceTokens(((TMP_Text)array[0]).text, TipContext.ControlTipLine);
				}
			}
			catch (Exception ex)
			{
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogError((object)("ChangeControlTipMultiple postfix failed: " + ex));
				}
			}
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(HUDManager), "ChangeControlTip")]
		internal static void ChangeControlTip_Prefix(ref string changeTo)
		{
			if (!Plugin.ControlTipsOn || changeTo == null)
			{
				return;
			}
			try
			{
				changeTo = KeybindResolver.ReplaceTokens(changeTo, TipContext.ControlTipLine);
			}
			catch (Exception ex)
			{
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogError((object)("ChangeControlTip prefix failed: " + ex));
				}
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(PlayerControllerB), "SetHoverTipAndCurrentInteractTrigger")]
		internal static void SetHoverTip_Postfix(PlayerControllerB __instance)
		{
			if (!Plugin.HoverOn)
			{
				return;
			}
			try
			{
				TextMeshProUGUI cursorTip = __instance.cursorTip;
				if (!((Object)(object)cursorTip == (Object)null) && !string.IsNullOrEmpty(((TMP_Text)cursorTip).text) && ((TMP_Text)cursorTip).text.IndexOf('[') >= 0)
				{
					((TMP_Text)cursorTip).text = KeybindResolver.ReplaceTokens(((TMP_Text)cursorTip).text, TipContext.HoverPrompt);
				}
			}
			catch (Exception ex)
			{
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogError((object)("Hover tip postfix failed: " + ex));
				}
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(HUDManager), "Update")]
		internal static void HudUpdate_Postfix(HUDManager __instance)
		{
			if (!Plugin.VotingOn)
			{
				return;
			}
			try
			{
				TextMeshProUGUI holdButtonToEndGameEarlyText = __instance.holdButtonToEndGameEarlyText;
				if (!((Object)(object)holdButtonToEndGameEarlyText == (Object)null) && !string.IsNullOrEmpty(((TMP_Text)holdButtonToEndGameEarlyText).text) && ((TMP_Text)holdButtonToEndGameEarlyText).text.IndexOf('[') >= 0)
				{
					string text = KeybindResolver.ReplaceTokens(((TMP_Text)holdButtonToEndGameEarlyText).text, TipContext.VotingTip);
					if (text != ((TMP_Text)holdButtonToEndGameEarlyText).text)
					{
						((TMP_Text)holdButtonToEndGameEarlyText).text = text;
					}
				}
			}
			catch (Exception ex)
			{
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogError((object)("HUD update postfix failed: " + ex));
				}
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(HUDManager), "DisplaySpectatorVoteTip")]
		internal static void SpectatorVoteTip_Postfix(HUDManager __instance)
		{
			if (!Plugin.VotingOn)
			{
				return;
			}
			try
			{
				TextMeshProUGUI spectatorTipText = __instance.spectatorTipText;
				if (!((Object)(object)spectatorTipText == (Object)null) && !string.IsNullOrEmpty(((TMP_Text)spectatorTipText).text) && ((TMP_Text)spectatorTipText).text.IndexOf('[') >= 0)
				{
					string text = KeybindResolver.ReplaceTokens(((TMP_Text)spectatorTipText).text, TipContext.VotingTip);
					if (text != ((TMP_Text)spectatorTipText).text)
					{
						((TMP_Text)spectatorTipText).text = text;
					}
				}
			}
			catch (Exception ex)
			{
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogError((object)("Spectator vote tip postfix failed: " + ex));
				}
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(ShipBuildModeManager), "CreateGhostObjectAndHighlight")]
		internal static void BuildModeTip_Postfix()
		{
			if (!Plugin.BuildModeOn || KeybindResolver.IsUsingController())
			{
				return;
			}
			try
			{
				TextMeshProUGUI val = HUDManager.Instance?.buildModeControlTip;
				if (!((Object)(object)val == (Object)null) && !string.IsNullOrEmpty(((TMP_Text)val).text))
				{
					string text = ((TMP_Text)val).text;
					string bindingDisplay = KeybindResolver.GetBindingDisplay(BuildModeConfirmRule, gamepad: false);
					if (bindingDisplay != null)
					{
						text = BuildConfirmRegex.Replace(text, "Confirm: [" + bindingDisplay + "]");
					}
					string bindingDisplay2 = KeybindResolver.GetBindingDisplay(BuildModeStoreRule, gamepad: false);
					if (bindingDisplay2 != null)
					{
						text = BuildStoreRegex.Replace(text, "Store: [" + bindingDisplay2 + "]");
					}
					if (text != ((TMP_Text)val).text)
					{
						((TMP_Text)val).text = text;
					}
				}
			}
			catch (Exception ex)
			{
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogError((object)("Build mode tip postfix failed: " + ex));
				}
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(IngamePlayerSettings), "UpdateGameToMatchSettings")]
		internal static void RefreshAfterRebind_Postfix()
		{
			if (!Plugin.RefreshOn)
			{
				return;
			}
			try
			{
				PlayerControllerB val = StartOfRound.Instance?.localPlayerController;
				if ((Object)(object)val != (Object)null && (Object)(object)val.currentlyHeldObjectServer != (Object)null)
				{
					val.currentlyHeldObjectServer.SetControlTipsForItem();
				}
			}
			catch (Exception ex)
			{
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogError((object)("Refresh-after-rebind postfix failed: " + ex));
				}
			}
		}
	}
	[BepInPlugin("syrupdesu.LC_ControlTipKeys", "LC-ControlTipKeys", "1.0.0")]
	public class Plugin : BaseUnityPlugin
	{
		public const string Guid = "syrupdesu.LC_ControlTipKeys";

		public const string Name = "LC-ControlTipKeys";

		public const string Version = "1.0.0";

		internal static ManualLogSource Log;

		internal static ConfigEntry<bool> MasterEnabled;

		internal static ConfigEntry<bool> ControlTipLinesEnabled;

		internal static ConfigEntry<bool> HoverPromptsEnabled;

		internal static ConfigEntry<bool> VotingTipsEnabled;

		internal static ConfigEntry<bool> BuildModeTipEnabled;

		internal static ConfigEntry<bool> RefreshAfterRebind;

		internal static ConfigEntry<bool> DebugLogging;

		internal static bool ControlTipsOn
		{
			get
			{
				if (MasterEnabled.Value)
				{
					return ControlTipLinesEnabled.Value;
				}
				return false;
			}
		}

		internal static bool HoverOn
		{
			get
			{
				if (MasterEnabled.Value)
				{
					return HoverPromptsEnabled.Value;
				}
				return false;
			}
		}

		internal static bool VotingOn
		{
			get
			{
				if (MasterEnabled.Value)
				{
					return VotingTipsEnabled.Value;
				}
				return false;
			}
		}

		internal static bool BuildModeOn
		{
			get
			{
				if (MasterEnabled.Value)
				{
					return BuildModeTipEnabled.Value;
				}
				return false;
			}
		}

		internal static bool RefreshOn
		{
			get
			{
				if (MasterEnabled.Value)
				{
					return RefreshAfterRebind.Value;
				}
				return false;
			}
		}

		private void Awake()
		{
			//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
			Log = ((BaseUnityPlugin)this).Logger;
			MasterEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "总开关。关闭后插件完全不介入,所有提示保持原版行为。");
			ControlTipLinesEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "ControlTipLines", true, "左下角控制提示行(Drop item : [G]、霰弹枪 [Q]、终端 [TAB]、物品行等)。载具的 W/S/Space 提示 v1 不处理。");
			HoverPromptsEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "HoverPrompts", true, "准星悬停提示(Grab : [E]、Use door : [LMB] 等,均按 Interact 实际键显示)。");
			VotingTipsEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "VotingTips", true, "提前离船按住提示与观战投票提示(按 PingScan 实际键显示)。");
			BuildModeTipEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "BuildModeTip", true, "造船模式提示,仅修正键鼠侧(Confirm→ConfirmBuildMode 实际键,Store→Delete 实际键,Rotate 保留原文;手柄侧保留原文)。");
			RefreshAfterRebind = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "RefreshAfterRebind", true, "重绑按键保存后立即刷新当前可见的物品控制提示。");
			DebugLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "DebugLogging", false, "开启后把每次文本替换写入 BepInEx/LogOutput.log(Debug 级别)。");
			new Harmony("syrupdesu.LC_ControlTipKeys").PatchAll();
			Log.LogInfo((object)("LC-ControlTipKeys 1.0.0 loaded (master " + (MasterEnabled.Value ? "on" : "off") + ")."));
		}
	}
}