Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of LC ControlTipKeys v1.0.0
LC-ControlTipKeys.dll
Decompiled a day agousing 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") + ").")); } } }