using System; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using HarmonyLib; using UnityEngine; [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: AssemblyVersion("0.0.0.0")] namespace ControllerChords; [BepInPlugin("marc.valheim.controllerchords", "Controller Chords", "0.4.0")] [BepInProcess("valheim.exe")] public sealed class ControllerChordsPlugin : BaseUnityPlugin { private sealed class ChordSlot { public int Index; public ConfigEntry Enabled; public ConfigEntry Modifier; public ConfigEntry Button; public ConfigEntry TargetKey; public ConfigEntry BlockModifierDefaultAction; public ConfigEntry VirtualPressFrames; public ConfigEntry HoldMode; } private sealed class SingleButtonSlot { public int Index; public ConfigEntry Enabled; public ConfigEntry Button; public ConfigEntry TargetKey; public ConfigEntry HoldMode; public ConfigEntry BlockButtonDefaultAction; public ConfigEntry VirtualPressFrames; } private sealed class ScrollSlot { public int Index; public ConfigEntry Enabled; public ConfigEntry Modifier; public ConfigEntry Button; public ConfigEntry Direction; public ConfigEntry ScrollAmount; public ConfigEntry PulseFrames; public ConfigEntry FireMode; public ConfigEntry BlockTriggerDefaultAction; public ConfigEntry BlockModifierDefaultAction; } private struct ChordDefaults { public readonly bool Enabled; public readonly ControllerButton Modifier; public readonly ControllerButton Button; public readonly KeyCode TargetKey; public readonly bool BlockModifierDefaultAction; public readonly int VirtualPressFrames; public ChordDefaults(bool enabled, ControllerButton modifier, ControllerButton button, KeyCode targetKey, bool blockModifierDefaultAction, int virtualPressFrames) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) Enabled = enabled; Modifier = modifier; Button = button; TargetKey = targetKey; BlockModifierDefaultAction = blockModifierDefaultAction; VirtualPressFrames = virtualPressFrames; } } private struct SingleButtonDefaults { public readonly bool Enabled; public readonly ControllerButton Button; public readonly KeyCode TargetKey; public readonly bool HoldMode; public readonly bool BlockButtonDefaultAction; public readonly int VirtualPressFrames; public SingleButtonDefaults(bool enabled, ControllerButton button, KeyCode targetKey, bool holdMode, bool blockButtonDefaultAction, int virtualPressFrames) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) Enabled = enabled; Button = button; TargetKey = targetKey; HoldMode = holdMode; BlockButtonDefaultAction = blockButtonDefaultAction; VirtualPressFrames = virtualPressFrames; } } private struct ScrollDefaults { public readonly bool Enabled; public readonly ControllerButton Modifier; public readonly ControllerButton Button; public readonly ScrollDirection Direction; public readonly float ScrollAmount; public readonly int PulseFrames; public readonly ScrollFireMode FireMode; public readonly bool BlockTriggerDefaultAction; public readonly bool BlockModifierDefaultAction; public ScrollDefaults(bool enabled, ControllerButton modifier, ControllerButton button, ScrollDirection direction, float scrollAmount, int pulseFrames, ScrollFireMode fireMode, bool blockTriggerDefaultAction, bool blockModifierDefaultAction) { Enabled = enabled; Modifier = modifier; Button = button; Direction = direction; ScrollAmount = scrollAmount; PulseFrames = pulseFrames; FireMode = fireMode; BlockTriggerDefaultAction = blockTriggerDefaultAction; BlockModifierDefaultAction = blockModifierDefaultAction; } } public const string PluginGuid = "marc.valheim.controllerchords"; public const string PluginName = "Controller Chords"; public const string PluginVersion = "0.4.0"; private const int SlotCount = 12; private const int SingleButtonSlotCount = 8; private const int ScrollSlotCount = 4; private const int MinimumVirtualPressFrames = 1; private const int MaximumVirtualPressFrames = 8; internal static ControllerChordsPlugin Instance; private ConfigEntry enabledConfig; private ConfigEntry allowInMenus; private ConfigEntry logChordEvents; private readonly List slots = new List(12); private readonly List singleSlots = new List(8); private readonly List scrollSlots = new List(4); private readonly Dictionary virtualKeyFrames = new Dictionary(); private readonly Dictionary virtualButtonFrames = new Dictionary(StringComparer.OrdinalIgnoreCase); private readonly Dictionary blockedButtonFrames = new Dictionary(StringComparer.OrdinalIgnoreCase); private readonly List keyScratch = new List(); private readonly List stringScratch = new List(); private readonly HashSet heldChordActive = new HashSet(); private readonly HashSet heldSingleActive = new HashSet(); private float virtualScrollValue; private int virtualScrollFrames; private Harmony harmony; private Type zInputType; private Func zGetButton; private Func zGetButtonDown; private PropertyInfo zVirtualKeyboardOpenProperty; private MethodInfo menuIsVisibleMethod; private MethodInfo inventoryGuiIsVisibleMethod; private MethodInfo storeGuiIsVisibleMethod; private MethodInfo textInputIsVisibleMethod; private MethodInfo consoleIsVisibleMethod; private bool zInputReady; private bool patchesInstalled; private int chordsFired; private void Awake() { Instance = this; BindConfig(); if (!enabledConfig.Value) { ((BaseUnityPlugin)this).Logger.LogInfo((object)"Controller Chords is disabled by config. No Harmony patches were installed."); return; } ResolveZInput(); ResolveMenuGuards(); InstallPatches(); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Controller Chords 0.4.0 loaded. Supports single-button, double-button (chord), and scroll-wheel controller mappings."); } private void OnDestroy() { if (harmony != null) { harmony.UnpatchSelf(); harmony = null; } virtualKeyFrames.Clear(); virtualButtonFrames.Clear(); blockedButtonFrames.Clear(); virtualScrollValue = 0f; virtualScrollFrames = 0; if ((Object)(object)Instance == (Object)(object)this) { Instance = null; } } private void Update() { if (enabledConfig.Value && zInputReady && (allowInMenus.Value || !IsMenuInputContext())) { ProcessSingleButtonSlots(); ProcessScrollSlots(); ProcessChordSlots(); } } private unsafe void ProcessSingleButtonSlots() { //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_01d2: Unknown result type (might be due to invalid IL or missing references) for (int i = 0; i < singleSlots.Count; i++) { SingleButtonSlot singleButtonSlot = singleSlots[i]; if (singleButtonSlot == null || !singleButtonSlot.Enabled.Value) { continue; } ControllerButton value = singleButtonSlot.Button.Value; if (value == ControllerButton.None || (int)singleButtonSlot.TargetKey.Value == 0) { continue; } string text = ToZInputButtonName(value); if (string.IsNullOrEmpty(text)) { continue; } KeyCode value2 = singleButtonSlot.TargetKey.Value; if (singleButtonSlot.HoldMode.Value) { if (SafeGetZButton(text)) { SetMaxFrameCount(virtualKeyFrames, value2, 2); if (singleButtonSlot.BlockButtonDefaultAction.Value) { SetMaxFrameCount(blockedButtonFrames, text, 2); } if (!heldSingleActive.Contains(singleButtonSlot.Index)) { heldSingleActive.Add(singleButtonSlot.Index); if (logChordEvents.Value) { ((BaseUnityPlugin)this).Logger.LogInfo((object)("Controller Chords: single-button " + singleButtonSlot.Index + " (" + text + " HOLD -> " + ((object)(*(KeyCode*)(&value2))/*cast due to .constrained prefix*/).ToString() + ") ENGAGED.")); } } } else if (heldSingleActive.Contains(singleButtonSlot.Index)) { heldSingleActive.Remove(singleButtonSlot.Index); if (logChordEvents.Value) { ((BaseUnityPlugin)this).Logger.LogInfo((object)("Controller Chords: single-button " + singleButtonSlot.Index + " HOLD released.")); } } } else if (SafeGetZButtonDown(text)) { int num = singleButtonSlot.VirtualPressFrames.Value; if (num < 1 || num > 8) { num = 2; } SetMaxFrameCount(virtualKeyFrames, value2, num); if (singleButtonSlot.BlockButtonDefaultAction.Value) { SetMaxFrameCount(blockedButtonFrames, text, num); } if (logChordEvents.Value) { ((BaseUnityPlugin)this).Logger.LogInfo((object)("Controller Chords: single-button " + singleButtonSlot.Index + " (" + text + " -> " + ((object)(*(KeyCode*)(&value2))/*cast due to .constrained prefix*/).ToString() + ") FIRED.")); } chordsFired++; } } } private void ProcessScrollSlots() { for (int i = 0; i < scrollSlots.Count; i++) { ScrollSlot scrollSlot = scrollSlots[i]; if (scrollSlot == null || !scrollSlot.Enabled.Value) { continue; } ControllerButton value = scrollSlot.Button.Value; if (value == ControllerButton.None) { continue; } string text = ToZInputButtonName(value); if (string.IsNullOrEmpty(text)) { continue; } ControllerButton value2 = scrollSlot.Modifier.Value; string text2 = string.Empty; if (value2 != ControllerButton.None) { text2 = ToZInputButtonName(value2); if (string.IsNullOrEmpty(text2) || !SafeGetZButton(text2)) { continue; } } if ((scrollSlot.FireMode.Value != ScrollFireMode.WhileHeld) ? SafeGetZButtonDown(text) : SafeGetZButton(text)) { EmitScroll(scrollSlot.ScrollAmount.Value, scrollSlot.Direction.Value, scrollSlot.PulseFrames.Value); if (scrollSlot.BlockTriggerDefaultAction.Value) { SetMaxFrameCount(blockedButtonFrames, text, Math.Max(2, scrollSlot.PulseFrames.Value)); } if (value2 != ControllerButton.None && scrollSlot.BlockModifierDefaultAction.Value) { SetMaxFrameCount(blockedButtonFrames, text2, Math.Max(2, scrollSlot.PulseFrames.Value)); } if (logChordEvents.Value) { ((BaseUnityPlugin)this).Logger.LogInfo((object)("Controller Chords: scroll " + scrollSlot.Index + " (" + text + " -> scroll " + scrollSlot.Direction.Value.ToString() + " " + virtualScrollValue.ToString("0.###") + ") FIRED.")); } chordsFired++; } } } private void ProcessChordSlots() { //IL_0060: Unknown result type (might be due to invalid IL or missing references) for (int i = 0; i < slots.Count; i++) { ChordSlot chordSlot = slots[i]; if (chordSlot == null || !chordSlot.Enabled.Value) { continue; } ControllerButton value = chordSlot.Modifier.Value; if (!IsSupportedModifier(value)) { continue; } ControllerButton value2 = chordSlot.Button.Value; if (value2 == ControllerButton.None || value2 == value || (int)chordSlot.TargetKey.Value == 0) { continue; } string text = ToZInputButtonName(value); string text2 = ToZInputButtonName(value2); if (string.IsNullOrEmpty(text) || string.IsNullOrEmpty(text2)) { continue; } bool flag = SafeGetZButton(text); if (chordSlot.HoldMode.Value) { bool flag2 = SafeGetZButton(text2); if (flag && flag2) { HoldChord(chordSlot, text); } else if (heldChordActive.Contains(chordSlot.Index)) { heldChordActive.Remove(chordSlot.Index); if (logChordEvents.Value) { ((BaseUnityPlugin)this).Logger.LogInfo((object)("Controller Chords: chord " + chordSlot.Index + " HOLD released.")); } } } else { bool flag3 = SafeGetZButtonDown(text2); if (flag && flag3) { FireChord(chordSlot, text); } } } } private void LateUpdate() { AgeKeyFrames(); AgeStringFrames(virtualButtonFrames); AgeStringFrames(blockedButtonFrames); AgeScroll(); } private void BindConfig() { //IL_00e6: Unknown result type (might be due to invalid IL or missing references) //IL_03c1: Unknown result type (might be due to invalid IL or missing references) enabledConfig = ((BaseUnityPlugin)this).Config.Bind("General", "Enabled", true, "Master switch. If false at startup, Controller Chords installs no active input patches."); allowInMenus = ((BaseUnityPlugin)this).Config.Bind("General", "AllowInMenus", false, "Allow controller mappings while Valheim menus/text input are open."); logChordEvents = ((BaseUnityPlugin)this).Config.Bind("General", "LogChordEvents", false, "Log each fired mapping to BepInEx/LogOutput.log."); for (int i = 1; i <= 8; i++) { SingleButtonDefaults singleButtonDefaults = GetSingleButtonDefaults(i); string text = "SingleButton " + i.ToString("00"); SingleButtonSlot item = new SingleButtonSlot { Index = i, Enabled = ((BaseUnityPlugin)this).Config.Bind(text, "Enabled", singleButtonDefaults.Enabled, "Enable this single-button mapping."), Button = ((BaseUnityPlugin)this).Config.Bind(text, "Button", singleButtonDefaults.Button, "Controller button that triggers this action."), TargetKey = ((BaseUnityPlugin)this).Config.Bind(text, "TargetKey", singleButtonDefaults.TargetKey, "Keyboard KeyCode exposed virtually to other mods."), HoldMode = ((BaseUnityPlugin)this).Config.Bind(text, "HoldMode", singleButtonDefaults.HoldMode, "When true, the target key is held DOWN for as long as you hold the controller button, and released when you let go. Use this for hold-to-act mods (e.g. FrostJack Zoom's hold-to-zoom key). When false, sends a brief tap."), BlockButtonDefaultAction = ((BaseUnityPlugin)this).Config.Bind(text, "BlockButtonDefaultAction", singleButtonDefaults.BlockButtonDefaultAction, "Temporarily suppress this controller button's normal Valheim action while the mapping is active. Defaults false."), VirtualPressFrames = ((BaseUnityPlugin)this).Config.Bind(text, "VirtualPressFrames", singleButtonDefaults.VirtualPressFrames, "Number of frames the virtual key remains visible to input queries (tap mode only).") }; singleSlots.Add(item); } for (int j = 1; j <= 4; j++) { ScrollDefaults scrollDefaults = GetScrollDefaults(j); string text2 = "Scroll " + j.ToString("00"); ScrollSlot item2 = new ScrollSlot { Index = j, Enabled = ((BaseUnityPlugin)this).Config.Bind(text2, "Enabled", scrollDefaults.Enabled, "Enable this scroll-wheel mapping."), Modifier = ((BaseUnityPlugin)this).Config.Bind(text2, "Modifier", scrollDefaults.Modifier, "Optional held gate button. When set, the scroll only fires while this button is held. Set to None for no gate."), Button = ((BaseUnityPlugin)this).Config.Bind(text2, "Button", scrollDefaults.Button, "Controller button that emits a scroll notch."), Direction = ((BaseUnityPlugin)this).Config.Bind(text2, "Direction", scrollDefaults.Direction, "In = scroll up (usually zoom in). Out = scroll down (usually zoom out). Swap these if your zoom feels reversed."), ScrollAmount = ((BaseUnityPlugin)this).Config.Bind(text2, "ScrollAmount", scrollDefaults.ScrollAmount, "Magnitude of the emitted scroll value. Most zoom mods only care about the sign, so 1.0 is fine. Lower it if a single press zooms too far."), PulseFrames = ((BaseUnityPlugin)this).Config.Bind(text2, "PulseFrames", scrollDefaults.PulseFrames, "How many frames the scroll value is emitted per press. 1 = one notch per press (recommended). Raise to 2 only if presses are being missed; note that may step two levels at once."), FireMode = ((BaseUnityPlugin)this).Config.Bind(text2, "FireMode", scrollDefaults.FireMode, "OnPress = one scroll notch each time you press the button (recommended for stepped zoom). WhileHeld = emit continuously while the button is held."), BlockTriggerDefaultAction = ((BaseUnityPlugin)this).Config.Bind(text2, "BlockTriggerDefaultAction", scrollDefaults.BlockTriggerDefaultAction, "Suppress the trigger button's normal Valheim action while scrolling (e.g. stop the D-pad from also moving the hotbar). Defaults true."), BlockModifierDefaultAction = ((BaseUnityPlugin)this).Config.Bind(text2, "BlockModifierDefaultAction", scrollDefaults.BlockModifierDefaultAction, "Suppress the gate button's normal Valheim action while scrolling. Defaults false.") }; scrollSlots.Add(item2); } for (int k = 1; k <= 12; k++) { ChordDefaults defaults = GetDefaults(k); string text3 = "Chord " + k.ToString("00"); ChordSlot item3 = new ChordSlot { Index = k, Enabled = ((BaseUnityPlugin)this).Config.Bind(text3, "Enabled", defaults.Enabled, "Enable this controller chord slot."), Modifier = ((BaseUnityPlugin)this).Config.Bind(text3, "Modifier", defaults.Modifier, "Held modifier button. Supported modifiers are RB, LB, RT, and LT."), Button = ((BaseUnityPlugin)this).Config.Bind(text3, "Button", defaults.Button, "Trigger button that fires on button-down while the modifier is held."), TargetKey = ((BaseUnityPlugin)this).Config.Bind(text3, "TargetKey", defaults.TargetKey, "Keyboard KeyCode exposed virtually to other mods."), BlockModifierDefaultAction = ((BaseUnityPlugin)this).Config.Bind(text3, "BlockModifierDefaultAction", defaults.BlockModifierDefaultAction, "Temporarily suppress this modifier's ZInput action when the chord fires. Defaults false."), VirtualPressFrames = ((BaseUnityPlugin)this).Config.Bind(text3, "VirtualPressFrames", defaults.VirtualPressFrames, "Number of frames the virtual key remains visible to input queries (tap mode only)."), HoldMode = ((BaseUnityPlugin)this).Config.Bind(text3, "HoldMode", false, "When true, the target key is held DOWN for as long as you hold the chord (modifier + trigger), and released when you let go. Use this for press-and-hold mods like climbing. When false (default), the chord sends a brief tap suitable for toggle hotkeys.") }; slots.Add(item3); } } private static SingleButtonDefaults GetSingleButtonDefaults(int index) { return index switch { 1 => new SingleButtonDefaults(enabled: true, ControllerButton.LB, (KeyCode)99, holdMode: true, blockButtonDefaultAction: false, 2), 2 => new SingleButtonDefaults(enabled: false, ControllerButton.None, (KeyCode)101, holdMode: false, blockButtonDefaultAction: false, 2), 3 => new SingleButtonDefaults(enabled: false, ControllerButton.None, (KeyCode)103, holdMode: false, blockButtonDefaultAction: false, 2), 4 => new SingleButtonDefaults(enabled: false, ControllerButton.None, (KeyCode)0, holdMode: false, blockButtonDefaultAction: false, 2), 5 => new SingleButtonDefaults(enabled: false, ControllerButton.None, (KeyCode)0, holdMode: false, blockButtonDefaultAction: false, 2), 6 => new SingleButtonDefaults(enabled: false, ControllerButton.None, (KeyCode)0, holdMode: false, blockButtonDefaultAction: false, 2), 7 => new SingleButtonDefaults(enabled: false, ControllerButton.None, (KeyCode)0, holdMode: false, blockButtonDefaultAction: false, 2), 8 => new SingleButtonDefaults(enabled: false, ControllerButton.None, (KeyCode)0, holdMode: false, blockButtonDefaultAction: false, 2), _ => new SingleButtonDefaults(enabled: false, ControllerButton.None, (KeyCode)0, holdMode: false, blockButtonDefaultAction: false, 2), }; } private static ScrollDefaults GetScrollDefaults(int index) { return index switch { 1 => new ScrollDefaults(enabled: true, ControllerButton.LB, ControllerButton.DPadUp, ScrollDirection.In, 1f, 1, ScrollFireMode.OnPress, blockTriggerDefaultAction: true, blockModifierDefaultAction: false), 2 => new ScrollDefaults(enabled: true, ControllerButton.LB, ControllerButton.DPadDown, ScrollDirection.Out, 1f, 1, ScrollFireMode.OnPress, blockTriggerDefaultAction: true, blockModifierDefaultAction: false), 3 => new ScrollDefaults(enabled: false, ControllerButton.None, ControllerButton.None, ScrollDirection.In, 1f, 1, ScrollFireMode.OnPress, blockTriggerDefaultAction: true, blockModifierDefaultAction: false), 4 => new ScrollDefaults(enabled: false, ControllerButton.None, ControllerButton.None, ScrollDirection.In, 1f, 1, ScrollFireMode.OnPress, blockTriggerDefaultAction: true, blockModifierDefaultAction: false), _ => new ScrollDefaults(enabled: false, ControllerButton.None, ControllerButton.None, ScrollDirection.In, 1f, 1, ScrollFireMode.OnPress, blockTriggerDefaultAction: true, blockModifierDefaultAction: false), }; } private static ChordDefaults GetDefaults(int index) { return index switch { 1 => new ChordDefaults(enabled: true, ControllerButton.RB, ControllerButton.A, (KeyCode)306, blockModifierDefaultAction: false, 2), 2 => new ChordDefaults(enabled: false, ControllerButton.RB, ControllerButton.B, (KeyCode)308, blockModifierDefaultAction: false, 2), 3 => new ChordDefaults(enabled: false, ControllerButton.RB, ControllerButton.X, (KeyCode)103, blockModifierDefaultAction: false, 2), 4 => new ChordDefaults(enabled: false, ControllerButton.RB, ControllerButton.Y, (KeyCode)104, blockModifierDefaultAction: false, 2), 5 => new ChordDefaults(enabled: false, ControllerButton.LB, ControllerButton.A, (KeyCode)106, blockModifierDefaultAction: false, 2), 6 => new ChordDefaults(enabled: false, ControllerButton.LB, ControllerButton.B, (KeyCode)107, blockModifierDefaultAction: false, 2), 7 => new ChordDefaults(enabled: false, ControllerButton.LB, ControllerButton.X, (KeyCode)108, blockModifierDefaultAction: false, 2), 8 => new ChordDefaults(enabled: false, ControllerButton.LB, ControllerButton.Y, (KeyCode)59, blockModifierDefaultAction: false, 2), 9 => new ChordDefaults(enabled: false, ControllerButton.RT, ControllerButton.A, (KeyCode)117, blockModifierDefaultAction: false, 2), 10 => new ChordDefaults(enabled: false, ControllerButton.RT, ControllerButton.B, (KeyCode)105, blockModifierDefaultAction: false, 2), 11 => new ChordDefaults(enabled: false, ControllerButton.RT, ControllerButton.X, (KeyCode)111, blockModifierDefaultAction: false, 2), 12 => new ChordDefaults(enabled: false, ControllerButton.RT, ControllerButton.Y, (KeyCode)112, blockModifierDefaultAction: false, 2), _ => new ChordDefaults(enabled: false, ControllerButton.None, ControllerButton.None, (KeyCode)0, blockModifierDefaultAction: false, 2), }; } private void EmitScroll(float amount, ScrollDirection direction, int frames) { float num = Mathf.Abs(amount); if (num <= 0f) { num = 1f; } virtualScrollValue = ((direction == ScrollDirection.Out) ? (0f - num) : num); if (frames < 1) { frames = 1; } else if (frames > 8) { frames = 8; } if (virtualScrollFrames < frames) { virtualScrollFrames = frames; } } private unsafe void FireChord(ChordSlot slot, string modifierName) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) int num = slot.VirtualPressFrames.Value; if (num < 1 || num > 8) { num = 2; } KeyCode value = slot.TargetKey.Value; SetMaxFrameCount(virtualKeyFrames, value, num); if (slot.BlockModifierDefaultAction.Value) { string key = ToZInputButtonName(slot.Modifier.Value); SetMaxFrameCount(blockedButtonFrames, key, num); } if (logChordEvents.Value) { ((BaseUnityPlugin)this).Logger.LogInfo((object)("Controller Chords: chord " + slot.Index + " (" + modifierName + " + trigger -> " + ((object)(*(KeyCode*)(&value))/*cast due to .constrained prefix*/).ToString() + ") FIRED.")); } chordsFired++; } private void HoldChord(ChordSlot slot, string modifierName) { //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) if (heldChordActive.Contains(slot.Index)) { SetMaxFrameCount(virtualKeyFrames, slot.TargetKey.Value, 2); return; } heldChordActive.Add(slot.Index); SetMaxFrameCount(virtualKeyFrames, slot.TargetKey.Value, 2); if (slot.BlockModifierDefaultAction.Value) { string key = ToZInputButtonName(slot.Modifier.Value); SetMaxFrameCount(blockedButtonFrames, key, 2); } if (logChordEvents.Value) { ((BaseUnityPlugin)this).Logger.LogInfo((object)("Controller Chords: chord " + slot.Index + " (" + modifierName + " + trigger HOLD) ENGAGED.")); } chordsFired++; } private bool IsVirtualKeyActive(KeyCode key) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) return virtualKeyFrames.ContainsKey(key); } private bool IsVirtualButtonActive(string buttonName) { return virtualButtonFrames.ContainsKey(buttonName); } private bool IsButtonBlocked(string buttonName) { return blockedButtonFrames.ContainsKey(buttonName); } private bool SafeGetZButton(string buttonName) { if (!zInputReady || string.IsNullOrEmpty(buttonName) || zGetButton == null) { return false; } try { return zGetButton(buttonName); } catch { return false; } } private bool SafeGetZButtonDown(string buttonName) { if (!zInputReady || string.IsNullOrEmpty(buttonName) || zGetButtonDown == null) { return false; } try { return zGetButtonDown(buttonName); } catch { return false; } } private void ResolveZInput() { zInputReady = false; try { zInputType = Type.GetType("ZInput, Assembly-CSharp", throwOnError: false); if (zInputType == null) { ((BaseUnityPlugin)this).Logger.LogWarning((object)"Controller Chords: could not find ZInput type."); return; } MethodInfo method = zInputType.GetMethod("GetButton", BindingFlags.Static | BindingFlags.Public, null, new Type[1] { typeof(string) }, null); MethodInfo method2 = zInputType.GetMethod("GetButtonDown", BindingFlags.Static | BindingFlags.Public, null, new Type[1] { typeof(string) }, null); if (method != null) { zGetButton = (Func)Delegate.CreateDelegate(typeof(Func), method); } if (method2 != null) { zGetButtonDown = (Func)Delegate.CreateDelegate(typeof(Func), method2); } zVirtualKeyboardOpenProperty = zInputType.GetProperty("IsKeyboardOpen", BindingFlags.Static | BindingFlags.Public); if (zGetButton != null && zGetButtonDown != null) { zInputReady = true; ((BaseUnityPlugin)this).Logger.LogInfo((object)"Controller Chords: ZInput initialized successfully."); } else { ((BaseUnityPlugin)this).Logger.LogWarning((object)"Controller Chords: could not initialize ZInput methods."); } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Controller Chords: exception during ZInput resolution: " + ex.Message)); } } private void ResolveMenuGuards() { try { Type type = Type.GetType("GUI, Assembly-CSharp", throwOnError: false); if (type != null) { menuIsVisibleMethod = type.GetMethod("IsVisible", BindingFlags.Static | BindingFlags.Public); } Type type2 = Type.GetType("InventoryGui, Assembly-CSharp", throwOnError: false); if (type2 != null) { inventoryGuiIsVisibleMethod = type2.GetMethod("IsVisible", BindingFlags.Static | BindingFlags.Public); } Type type3 = Type.GetType("StoreGui, Assembly-CSharp", throwOnError: false); if (type3 != null) { storeGuiIsVisibleMethod = type3.GetMethod("IsVisible", BindingFlags.Static | BindingFlags.Public); } Type type4 = Type.GetType("TextInput, Assembly-CSharp", throwOnError: false); if (type4 != null) { textInputIsVisibleMethod = type4.GetMethod("IsVisible", BindingFlags.Static | BindingFlags.Public); } Type type5 = Type.GetType("Console, Assembly-CSharp", throwOnError: false); if (type5 != null) { consoleIsVisibleMethod = type5.GetMethod("IsVisible", BindingFlags.Static | BindingFlags.Public); } } catch { } } private bool IsMenuInputContext() { if (menuIsVisibleMethod != null) { try { if ((bool)menuIsVisibleMethod.Invoke(null, null)) { return true; } } catch { } } if (inventoryGuiIsVisibleMethod != null) { try { if ((bool)inventoryGuiIsVisibleMethod.Invoke(null, null)) { return true; } } catch { } } if (storeGuiIsVisibleMethod != null) { try { if ((bool)storeGuiIsVisibleMethod.Invoke(null, null)) { return true; } } catch { } } if (textInputIsVisibleMethod != null) { try { if ((bool)textInputIsVisibleMethod.Invoke(null, null)) { return true; } } catch { } } if (consoleIsVisibleMethod != null) { try { if ((bool)consoleIsVisibleMethod.Invoke(null, null)) { return true; } } catch { } } if (zVirtualKeyboardOpenProperty != null) { try { if ((bool)zVirtualKeyboardOpenProperty.GetValue(null)) { return true; } } catch { } } return false; } private void InstallPatches() { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Expected O, but got Unknown //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Expected O, but got Unknown //IL_0200: Unknown result type (might be due to invalid IL or missing references) //IL_020d: Expected O, but got Unknown //IL_00d3: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Expected O, but got Unknown //IL_025c: Unknown result type (might be due to invalid IL or missing references) //IL_0269: Expected O, but got Unknown //IL_012b: Unknown result type (might be due to invalid IL or missing references) //IL_0138: Expected O, but got Unknown //IL_02c2: Unknown result type (might be due to invalid IL or missing references) //IL_02cf: Expected O, but got Unknown //IL_0184: Unknown result type (might be due to invalid IL or missing references) //IL_0191: Expected O, but got Unknown if (patchesInstalled) { return; } harmony = new Harmony("marc.valheim.controllerchords"); try { Type type = Type.GetType("UnityEngine.Input, UnityEngine.InputLegacyModule", throwOnError: false); if (type != null) { MethodInfo method = type.GetMethod("GetKey", BindingFlags.Static | BindingFlags.Public, null, new Type[1] { typeof(KeyCode) }, null); if (method != null) { harmony.Patch((MethodBase)method, (HarmonyMethod)null, new HarmonyMethod(typeof(ControllerChordsPlugin).GetMethod("InputKeyPostfix", BindingFlags.Static | BindingFlags.NonPublic)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } MethodInfo method2 = type.GetMethod("GetAxis", BindingFlags.Static | BindingFlags.Public, null, new Type[1] { typeof(string) }, null); if (method2 != null) { harmony.Patch((MethodBase)method2, (HarmonyMethod)null, new HarmonyMethod(typeof(ControllerChordsPlugin).GetMethod("InputGetAxisPostfix", BindingFlags.Static | BindingFlags.NonPublic)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } MethodInfo method3 = type.GetMethod("GetAxisRaw", BindingFlags.Static | BindingFlags.Public, null, new Type[1] { typeof(string) }, null); if (method3 != null) { harmony.Patch((MethodBase)method3, (HarmonyMethod)null, new HarmonyMethod(typeof(ControllerChordsPlugin).GetMethod("InputGetAxisRawPostfix", BindingFlags.Static | BindingFlags.NonPublic)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } PropertyInfo property = type.GetProperty("mouseScrollDelta", BindingFlags.Static | BindingFlags.Public); if (property != null) { MethodInfo getMethod = property.GetGetMethod(); if (getMethod != null) { harmony.Patch((MethodBase)getMethod, (HarmonyMethod)null, new HarmonyMethod(typeof(ControllerChordsPlugin).GetMethod("InputMouseScrollDeltaPostfix", BindingFlags.Static | BindingFlags.NonPublic)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } } } } catch { } try { Type type2 = Type.GetType("ZInput, Assembly-CSharp", throwOnError: false); if (type2 != null) { MethodInfo method4 = type2.GetMethod("GetKey", BindingFlags.Static | BindingFlags.Public, null, new Type[1] { typeof(KeyCode) }, null); if (method4 != null) { harmony.Patch((MethodBase)method4, (HarmonyMethod)null, new HarmonyMethod(typeof(ControllerChordsPlugin).GetMethod("ZInputKeyPostfix", BindingFlags.Static | BindingFlags.NonPublic)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } MethodInfo method5 = type2.GetMethod("GetButton", BindingFlags.Static | BindingFlags.Public, null, new Type[1] { typeof(string) }, null); if (method5 != null) { harmony.Patch((MethodBase)method5, (HarmonyMethod)null, new HarmonyMethod(typeof(ControllerChordsPlugin).GetMethod("ZInputButtonPostfix", BindingFlags.Static | BindingFlags.NonPublic)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } MethodInfo method6 = type2.GetMethod("GetMouseScrollWheel", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null); if (method6 != null && method6.ReturnType == typeof(float)) { harmony.Patch((MethodBase)method6, (HarmonyMethod)null, new HarmonyMethod(typeof(ControllerChordsPlugin).GetMethod("ZInputMouseScrollWheelPostfix", BindingFlags.Static | BindingFlags.NonPublic)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } } } catch { } patchesInstalled = true; ((BaseUnityPlugin)this).Logger.LogInfo((object)"Controller Chords: Harmony patches installed."); } private void AgeKeyFrames() { //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) if (virtualKeyFrames.Count == 0) { return; } keyScratch.Clear(); foreach (KeyValuePair virtualKeyFrame in virtualKeyFrames) { keyScratch.Add(virtualKeyFrame.Key); } for (int i = 0; i < keyScratch.Count; i++) { KeyCode key = keyScratch[i]; int num = virtualKeyFrames[key] - 1; if (num <= 0) { virtualKeyFrames.Remove(key); } else { virtualKeyFrames[key] = num; } } } private void AgeStringFrames(Dictionary framesByName) { if (framesByName.Count == 0) { return; } stringScratch.Clear(); foreach (KeyValuePair item in framesByName) { stringScratch.Add(item.Key); } for (int i = 0; i < stringScratch.Count; i++) { string key = stringScratch[i]; int num = framesByName[key] - 1; if (num <= 0) { framesByName.Remove(key); } else { framesByName[key] = num; } } } private void AgeScroll() { if (virtualScrollFrames > 0) { virtualScrollFrames--; if (virtualScrollFrames <= 0) { virtualScrollFrames = 0; virtualScrollValue = 0f; } } } private static void SetMaxFrameCount(Dictionary dictionary, KeyCode key, int frames) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) if (!dictionary.TryGetValue(key, out var value) || value < frames) { dictionary[key] = frames; } } private static void SetMaxFrameCount(Dictionary dictionary, string key, int frames) { if (!string.IsNullOrEmpty(key) && (!dictionary.TryGetValue(key, out var value) || value < frames)) { dictionary[key] = frames; } } private static bool IsSupportedModifier(ControllerButton button) { if (button != ControllerButton.RB && button != ControllerButton.LB && button != ControllerButton.RT) { return button == ControllerButton.LT; } return true; } private static string ToZInputButtonName(ControllerButton button) { return button switch { ControllerButton.A => "JoyButtonA", ControllerButton.B => "JoyButtonB", ControllerButton.X => "JoyButtonX", ControllerButton.Y => "JoyButtonY", ControllerButton.DPadLeft => "JoyDPadLeft", ControllerButton.DPadRight => "JoyDPadRight", ControllerButton.DPadUp => "JoyDPadUp", ControllerButton.DPadDown => "JoyDPadDown", ControllerButton.LB => "JoyLBumper", ControllerButton.RB => "JoyRBumper", ControllerButton.LT => "JoyLTrigger", ControllerButton.RT => "JoyRTrigger", ControllerButton.LeftStick => "JoyLStick", ControllerButton.RightStick => "JoyRStick", ControllerButton.Back => "JoyBack", ControllerButton.Start => "JoyStart", _ => string.Empty, }; } private static void InputKeyPostfix(KeyCode __0, ref bool __result) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) ControllerChordsPlugin instance = Instance; if (!__result && !((Object)(object)instance == (Object)null) && instance.patchesInstalled && instance.IsVirtualKeyActive(__0)) { __result = true; } } private static void ZInputKeyPostfix(KeyCode __0, ref bool __result) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) ControllerChordsPlugin instance = Instance; if (!__result && !((Object)(object)instance == (Object)null) && instance.patchesInstalled && instance.IsVirtualKeyActive(__0)) { __result = true; } } private static void ZInputButtonPostfix(string __0, ref bool __result) { ControllerChordsPlugin instance = Instance; if (!((Object)(object)instance == (Object)null) && instance.patchesInstalled) { if (instance.IsButtonBlocked(__0)) { __result = false; } else if (!__result && instance.IsVirtualButtonActive(__0)) { __result = true; } } } private static void InputGetAxisPostfix(string __0, ref float __result) { ControllerChordsPlugin instance = Instance; if (!((Object)(object)instance == (Object)null) && instance.patchesInstalled && instance.virtualScrollFrames > 0 && string.Equals(__0, "Mouse ScrollWheel", StringComparison.OrdinalIgnoreCase)) { __result += instance.virtualScrollValue; } } private static void InputGetAxisRawPostfix(string __0, ref float __result) { ControllerChordsPlugin instance = Instance; if (!((Object)(object)instance == (Object)null) && instance.patchesInstalled && instance.virtualScrollFrames > 0 && string.Equals(__0, "Mouse ScrollWheel", StringComparison.OrdinalIgnoreCase)) { __result += instance.virtualScrollValue; } } private static void InputMouseScrollDeltaPostfix(ref Vector2 __result) { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) ControllerChordsPlugin instance = Instance; if (!((Object)(object)instance == (Object)null) && instance.patchesInstalled && instance.virtualScrollFrames > 0) { Vector2 val = __result; val.y += ((instance.virtualScrollValue > 0f) ? 1f : (-1f)); __result = val; } } private static void ZInputMouseScrollWheelPostfix(ref float __result) { ControllerChordsPlugin instance = Instance; if (!((Object)(object)instance == (Object)null) && instance.patchesInstalled && instance.virtualScrollFrames > 0) { __result += instance.virtualScrollValue; } } } public enum ControllerButton { None, A, B, X, Y, DPadLeft, DPadRight, DPadUp, DPadDown, LB, RB, LT, RT, LeftStick, RightStick, Back, Start } public enum ScrollDirection { In, Out } public enum ScrollFireMode { OnPress, WhileHeld }