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 ControllerGamepadRemapping v3.0.2
plugins/ControllerGamepadRemapping.dll
Decompiled 2 weeks ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using BepInEx; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using UnityEngine; using UnityEngine.InputSystem; using UnityEngine.SceneManagement; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("nussbacker")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyDescription("Controller / Gamepad Remapping for Valheim.")] [assembly: AssemblyFileVersion("3.0.2.0")] [assembly: AssemblyInformationalVersion("3.0.2")] [assembly: AssemblyProduct("ControllerGamepadRemapping")] [assembly: AssemblyTitle("ControllerGamepadRemapping")] [assembly: AssemblyVersion("3.0.2.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 ControllerGamepadRemapping { [BepInPlugin("nussbacker.valheim.controllergamepadremapping", "Controller / Gamepad Remapping", "3.0.2")] public sealed class Plugin : BaseUnityPlugin { private sealed class RuntimeSetting<T> { private T value; public T Value { get { return value; } set { if (!EqualityComparer<T>.Default.Equals(this.value, value)) { this.value = value; this.SettingChanged?.Invoke(this, EventArgs.Empty); } } } public event EventHandler SettingChanged; public RuntimeSetting(T initialValue) { value = initialValue; } } private sealed class CustomLayoutBinding { public string Path; public bool AlternateFunction; public bool Unbound; } private sealed class PresetBehaviorState { public bool CrouchChord; public bool BlockJumpToDodge; public bool MoveWhileGui; public bool ProximityInteraction; } private sealed class SavedCustomPreset { public string Name; public string StoragePath; public int StorageOrder = -1; public readonly Dictionary<string, CustomLayoutBinding> Bindings = new Dictionary<string, CustomLayoutBinding>(StringComparer.OrdinalIgnoreCase); public PresetBehaviorState Behaviors = new PresetBehaviorState(); public bool HasBehaviorSnapshot; } private enum CustomPresetLoadSource { None, PerPresetFiles, PerPresetFilesWithoutState } private sealed class EditorSuppressedUiModuleState { public Behaviour Module; public bool WasEnabled; } private sealed class GamepadMapPresentationCacheState { public int MapIdentity; public string Signature; } private sealed class OriginalBinding { public string Path; public string AltPath; public bool AltKey; public bool ShowHints; public bool Rebindable; public float RepeatDelay; public float RepeatInterval; } private sealed class LayoutRegistrationSnapshot { public FieldInfo Field; public object Owner; public object Value; public object Key; } [Flags] private enum ConflictContext { None = 0, Gameplay = 1, RadialMenu = 2, Building = 4, Interface = 8, Chat = 0x10 } private sealed class ActionInfo { public readonly string Name; public readonly string Description; public ActionInfo(string name, string description) { Name = name; Description = description; } } private sealed class GamepadMapActionLabel { public string Action; public bool Alternate; public bool Conflicting; } public const string PluginGuid = "nussbacker.valheim.controllergamepadremapping"; public const string PluginName = "Controller / Gamepad Remapping"; public const string PluginVersion = "3.0.2"; public const string PluginAuthor = "nussbacker"; internal static Plugin Instance; internal static ManualLogSource Log; internal static bool EditorInputBlocked; private Harmony harmony; private Harmony editorInputHarmony; private bool editorGamepadInputHooksActive; private Harmony buildInputHarmony; private bool buildGamepadInputHooksActive; private Harmony movementGuiInputHarmony; private bool movementGuiInputHooksActive; private MethodInfo movementGuiPlayerControllerFixedUpdate; private bool movementGuiRunToggleBaselineReady; private bool movementGuiRunTogglePreviousHeld; private bool movementGuiRunToggleValueValid; private bool movementGuiRunToggleValue; private Harmony customAltConsumerHarmony; private string customAltConsumerPatchSignature = string.Empty; private bool appliedOnce; private bool applying; private bool dirty; private float applyAt; private float nextReadyPoll; private const string UnboundToken = "__UNBOUND__"; private RuntimeSetting<bool> debug; private RuntimeSetting<KeyCode> editorKey; private RuntimeSetting<bool> showEditor; private RuntimeSetting<bool> proximityInteractionEnabled; private RuntimeSetting<string> crouchInput; private RuntimeSetting<bool> crouchUseChord; private RuntimeSetting<bool> blockJumpToDodge; private RuntimeSetting<bool> moveWhileContainerGuiOpen; private RuntimeSetting<string> gamepadUiSelection; private RuntimeSetting<bool> nintendoFaceButtonNormalization; private RuntimeSetting<string> buildCopyInput; private RuntimeSetting<bool> buildCopyRequiresAlt; private RuntimeSetting<string> buildHammerInput; private string customPresetDirectoryPath; private string customPresetStateFilePath; private string globalSettingsFilePath; private const string CustomPresetDirectoryName = "ControllerPresets"; private const string CustomPresetStateFileName = "_ControllerPresets.state"; private const string GlobalSettingsFileName = "_ControllerGamepadRemapping.settings"; private const string GlobalSettingsFileFormatVersion = "2"; private const string CustomPresetFileExtension = ".preset"; private const string CustomPresetFileFormatVersion = "3"; private const string LegacyCustomPresetFileFormatVersion = "2"; private readonly Dictionary<string, string> loadedGlobalSettings = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); private string lastSavedGlobalSettingsSignature = string.Empty; private bool globalSettingsFileExisted; private readonly HashSet<string> pendingPresetFileDeletes = new HashSet<string>(StringComparer.OrdinalIgnoreCase); internal const float ProximityInteractionRadius = 2f; internal const bool ProximityInteractionRequireLineOfSight = true; internal const bool ProximityInteractionDisableWhileBuilding = true; private readonly Dictionary<string, RuntimeSetting<string>> bindings = new Dictionary<string, RuntimeSetting<string>>(); private readonly Dictionary<string, RuntimeSetting<string>> guiBindings = new Dictionary<string, RuntimeSetting<string>>(StringComparer.OrdinalIgnoreCase); private readonly Dictionary<string, RuntimeSetting<bool>> alternateFunctionRequirements = new Dictionary<string, RuntimeSetting<bool>>(StringComparer.OrdinalIgnoreCase); private readonly List<string> activeNativeAlternateActions = new List<string>(); private bool nativePlayerModifierOwnershipPossible; private readonly Dictionary<string, OriginalBinding> originalBindings = new Dictionary<string, OriginalBinding>(); private bool originalsCaptured; private string nativeControllerPresetSignature; private bool suppressPresetRuntimeStateEvents; private bool controllerPresetSelectionCallActive; private bool controllerPresetPreviewActive; private bool controllerPresetPreviewDetectedThisRefresh; private bool controllerPresetPreviewCancelInProgress; private string controllerPresetSignatureBeforePreview; private string pendingControllerPresetSignature; private readonly List<SavedCustomPreset> savedCustomPresets = new List<SavedCustomPreset>(); private readonly Dictionary<string, CustomLayoutBinding> workingLayoutBindings = new Dictionary<string, CustomLayoutBinding>(StringComparer.OrdinalIgnoreCase); private readonly Dictionary<string, CustomLayoutBinding> activePresetBaselineBindings = new Dictionary<string, CustomLayoutBinding>(StringComparer.OrdinalIgnoreCase); private bool workingLayoutInitialized; private bool workingLayoutDirty; private string activePresetBaselineStateSignature = string.Empty; private string workingLayoutStateSignature = string.Empty; private string activeCustomPresetName = string.Empty; private PresetBehaviorState activePresetBaselineBehaviors = new PresetBehaviorState(); private PresetBehaviorState workingPresetBehaviors = new PresetBehaviorState(); private bool presetBehaviorStateInitialized; private bool settingsCustomLayoutPreviewActive; private string settingsCustomPresetPreviewName = string.Empty; private bool settingsControllerLayoutSelectionTouched; private readonly Dictionary<string, CustomLayoutBinding> settingsPresetPreviewBindings = new Dictionary<string, CustomLayoutBinding>(StringComparer.OrdinalIgnoreCase); private PresetBehaviorState settingsPresetPreviewBehaviors = new PresetBehaviorState(); private bool settingsPresetPreviewActive; private object settingsNativePresetPreviewLayout; private object settingsCommittedNativeLayout; private bool settingsControllerTabActive; private int settingsControllerTabIndex = -1; private int settingsPendingTabIndex = -1; private bool settingsPreviewPresentationRefreshInProgress; private bool settingsNativePresetCommitInProgress; private object activeGamepadSettingsInstance; private Type cachedGamepadSettingsType; private FieldInfo gamepadSettingsCurrentControllerLayoutField; private FieldInfo gamepadSettingsControllerLayoutTextField; private MethodInfo gamepadSettingsOnZInputLayoutChangedMethod; private const int CustomPresetNameMaxLength = 20; private bool savePresetPromptOpen; private bool savePresetPromptNeedsFocus; private string savePresetNameInput = "Custom Layout"; private Rect savePresetPromptRect = new Rect(0f, 0f, 360f, 128f); private bool deletePresetPromptOpen; private string deletePresetPromptName = string.Empty; private Rect deletePresetPromptRect = new Rect(0f, 0f, 330f, 104f); private readonly HashSet<MethodBase> patchedControllerPresetLifecycleMethods = new HashSet<MethodBase>(); private bool controllerPresetLifecyclePatchDiscoveryComplete; private Type zInputType; private MethodInfo addButton; private MethodInfo unsubscribeButton; private FieldInfo buttonsField; private FieldInfo buttonActionField; private PropertyInfo buttonActionProperty; private MethodInfo zInputUpdate; private MethodInfo zInputGetButton; private MethodInfo zInputGetButtonDown; private MethodInfo zInputGetButtonUp; private MethodInfo zInputGetKey; private MethodInfo zInputGetKeyDown; private MethodInfo zInputGetKeyUp; private MethodInfo zInputGetMouseButton; private MethodInfo zInputGetMouseButtonDown; private MethodInfo zInputGetMouseButtonUp; private MethodInfo zInputGetMouseDelta; private MethodInfo zInputChangeInputLayout; private MethodInfo zInputUpdateGamepadInputLayout; private MethodInfo zInputIsGamepadEnabled; private MethodInfo zInputSetGamepadEnabled; private MethodInfo zInputGetBoundKeyString; private FieldInfo zInputGamepadEnabledField; private FieldInfo zInputInstanceField; private PropertyInfo zInputInstanceProperty; private bool zInputInstanceAccessorPrepared; private bool zInputReflectionDiscoveryComplete; private int gamepadEnabledSampleFrame = -1; private bool cachedGamepadEnabledKnown; private bool cachedGamepadEnabled = true; private bool? lastLoggedNativeGamepadEnabled; private int lastAllowedNativeGamepadLayoutFrame = -1; private float lastAllowedNativeGamepadLayoutAt = -10f; [ThreadStatic] private static bool skippedRedundantNativeGamepadLayoutCall; private bool gamepadEnabledLifecycleInitialized; private bool lastObservedGamepadEnabled = true; private bool gamepadReenableRepairPending; private MethodInfo zInputGetJoyLeftStick; private MethodInfo zInputGetJoyRightStick; private MethodInfo zInputGetJoyLeftStickX; private MethodInfo zInputGetJoyLeftStickY; private MethodInfo zInputGetJoyRightStickX; private MethodInfo zInputGetJoyRightStickY; private MethodInfo zInputGetJoyLTrigger; private MethodInfo zInputGetJoyRTrigger; private MethodInfo zInputGetMouseScrollWheel; private bool patchedChangeInputLayout; private bool patchedGamepadLayout; private bool patchedSetGamepadEnabled; private bool patchedBoundKeyString; private readonly HashSet<MethodBase> patchedEditorExclusiveBoolInputMethods = new HashSet<MethodBase>(); private bool editorExclusiveInputOwnershipActive; private CursorLockMode editorPreviousCursorLockState; private bool editorPreviousCursorVisible; private Type editorBaseInputModuleType; private Object editorObservedEventSystem; private readonly List<EditorSuppressedUiModuleState> editorSuppressedUiModules = new List<EditorSuppressedUiModuleState>(); private readonly HashSet<MethodBase> patchedInventoryGuiShortcutMethods = new HashSet<MethodBase>(); private bool patchedInventoryGuiShortcutUpdate; private bool patchedInventoryGuiCraftQuantityContext; [ThreadStatic] private static int inventoryGuiCraftQuantityContextDepth; [ThreadStatic] private static object inventoryGuiCraftQuantityContextInstance; [ThreadStatic] private static bool inventoryGuiCraftQuantityPaneOwnsFocus; [ThreadStatic] private static bool inventoryGuiCraftQuantityActiveGroupAvailable; [ThreadStatic] private static int inventoryGuiCraftQuantityActiveGroup; private bool craftQuantitySuppressionLogged; private bool craftQuantityHeldDecisionLogged; private readonly Dictionary<string, MethodInfo> contextualGuiShortcutMethods = new Dictionary<string, MethodInfo>(StringComparer.Ordinal); private Type inventoryGuiType; private bool contextualGuiCustomBindingActive; private bool movementGuiInventoryOpen; private object activeInventoryGuiInstance; private bool movementGuiStoreOpen; private bool movementGuiBuildSelectionOpen; private bool movementGuiFullMapOpen; private bool movementGuiSplitDialogOpen; private GameObject movementGuiSplitDialogRoot; private readonly HashSet<MethodBase> patchedMovementGuiLifecycleMethods = new HashSet<MethodBase>(); private bool movementGuiLifecyclePatchDiscoveryComplete; private bool movementGuiMenuOpen; private bool movementGuiSettingsOpen; private bool movementGuiTextInputOpen; private int settingsLifecycleTransitionDepth; private float lastSettingsLifecycleTransitionAt = -10f; private bool settingsControllerStateDirty; private string keyboardMouseSettingsBaselineSignature; private readonly HashSet<string> keyboardMouseSettingsActionNames = new HashSet<string>(StringComparer.Ordinal); private readonly HashSet<MethodBase> patchedKeyboardMouseSettingsMethods = new HashSet<MethodBase>(); private bool keyboardMouseSettingsPatchDiscoveryComplete; private readonly ConditionalWeakTable<object, GamepadMapPresentationCacheState> gamepadMapPresentationCache = new ConditionalWeakTable<object, GamepadMapPresentationCacheState>(); private readonly List<Object> observedGamepadMapControllers = new List<Object>(); private MethodInfo cachedGamepadMapUpdateMethod; private float tabSoundTraceUntil = -10f; private readonly HashSet<MethodBase> patchedTabSoundTraceMethods = new HashSet<MethodBase>(); private bool tabSoundTracePatchDiscoveryComplete; private FieldInfo inventoryGuiActiveGroupField; private bool inventoryGuiCraftFocusFieldsPrepared; private readonly HashSet<MethodBase> patchedBuildUiMoveMethods = new HashSet<MethodBase>(); private bool patchedBuildUiSelectableNavigation; private Type buildUiSelectableType; private Type buildUiEventSystemType; private PropertyInfo buildUiEventSystemCurrentProperty; private PropertyInfo buildUiCurrentSelectedGameObjectProperty; private MethodInfo buildUiSelectableFindUp; private MethodInfo buildUiSelectableFindDown; private MethodInfo buildUiSelectableFindLeft; private MethodInfo buildUiSelectableFindRight; private MethodInfo buildUiSelectableSelect; private string buildUiManualNavigationDirection; private float buildUiManualNavigationNextRepeatAt; private string splitDialogManualNavigationDirection; private float splitDialogManualNavigationNextRepeatAt; private Type splitDialogAxisEventDataType; private PropertyInfo splitDialogMoveVectorProperty; private PropertyInfo splitDialogMoveDirectionProperty; private ConstructorInfo splitDialogAxisEventDataConstructor; private readonly Dictionary<Type, MethodInfo> splitDialogOnMoveMethodCache = new Dictionary<Type, MethodInfo>(); [ThreadStatic] private static bool manualUnityUiMoveDispatchInProgress; [ThreadStatic] private static bool nativeGuiDirectionQueryInProgress; private const float BuildUiNavigationInitialRepeatDelay = 0.3f; private const float BuildUiNavigationRepeatInterval = 0.12f; private const float BuildUiNavigationDeadzone = 0.5f; [ThreadStatic] private static bool composingAlternateFunctionHintGlyph; [ThreadStatic] private static int keyHintGenerationDepth; [ThreadStatic] private static bool unboundHintMarkerEmittedThisGeneration; [ThreadStatic] private static int radialHintGenerationDepth; [ThreadStatic] private static bool radialUnboundHintMarkerEmittedThisGeneration; private const string UnboundHintMarker = "__CGR_UNBOUND_HINT__:"; private int alternateFunctionGlyphCacheFrame = -1; private bool alternateFunctionGlyphCacheNormalReady; private bool alternateFunctionGlyphCacheAlternateReady; private string alternateFunctionGlyphCacheNormal; private string alternateFunctionGlyphCacheAlternate; private readonly HashSet<MethodBase> patchedControllerPresentationMethods = new HashSet<MethodBase>(); private readonly HashSet<MethodBase> patchedKeyHintsRotateSourceMethods = new HashSet<MethodBase>(); private readonly HashSet<MethodBase> patchedUniversalUnboundHintSourceMethods = new HashSet<MethodBase>(); private readonly HashSet<MethodBase> patchedRadialUnboundHintSourceMethods = new HashSet<MethodBase>(); private readonly Dictionary<int, List<object>> universalUnboundHintTextCache = new Dictionary<int, List<object>>(); private bool universalUnboundHintDiscoveryComplete; private bool radialUnboundHintDiscoveryComplete; private object cachedKeyHintsOwner; private bool keyHintPresentationCacheInitialized; private readonly List<Object> observedKeyHintsInstances = new List<Object>(); private MethodInfo cachedKeyHintsRefreshMethod; private readonly List<object> cachedKeyHintTextObjects = new List<object>(); private readonly List<object> cachedBuildCopyHintTextObjects = new List<object>(); private object cachedBuildCopyHintTextObject; private object cachedDodgeHintTextObject; private object cachedSecondaryAttackHintTextObject; private object cachedSnapCycleHintTextObject; private object cachedBuildMenuHintTextObject; private readonly Dictionary<string, List<GameObject>> hiddenUnboundKeyHintObjects = new Dictionary<string, List<GameObject>>(StringComparer.OrdinalIgnoreCase); private int keyHintPresentationCacheValidationFrame = -1; private bool buildRotateHintPresentationLogged; private bool buildCopyHintPresentationLogged; private object cachedBuildUiHammerOwner; private object cachedBuildUiHammerTextObject; private string cachedBuildUiHammerOriginalGlyph; private bool gamepadUiDropdownOpen; private Rect gamepadUiDropdownScreenAnchor; private bool gamepadUiDropdownAnchorValid; private bool presetDropdownOpen; private Rect presetDropdownScreenAnchor; private bool presetDropdownAnchorValid; private bool dropdownPointerCaptureUntilMouseUp; private bool editorDeferredPresentationRefresh; private bool gamepadPresentationStartupApplied; private string scheduledFullApplyReason; private string scheduledIncrementalApplyReason; private bool fullApplyScheduled; private bool logicalUnbindNativeRefreshPending; private float logicalUnbindNativeRefreshAt = -1f; private readonly HashSet<string> logicalUnbindRefreshActions = new HashSet<string>(StringComparer.OrdinalIgnoreCase); private bool nativeInitialGamepadLayoutObserved; private bool startupApplyFallbackConsumed; private float startupApplyFallbackAt = -1f; private Player radialRepairObservedLocalPlayer; private bool radialWorldReadyRepairPending; private float radialWorldReadyRepairAt = -1f; private readonly HashSet<string> pendingApplyActions = new HashSet<string>(StringComparer.OrdinalIgnoreCase); private readonly HashSet<string> pendingNeutralApplyActions = new HashSet<string>(StringComparer.OrdinalIgnoreCase); private readonly Dictionary<string, string> pendingNeutralApplyPaths = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); private readonly HashSet<string> loggedNeutralDeferredActions = new HashSet<string>(StringComparer.OrdinalIgnoreCase); private bool physicalGamepadLifecycleInitialized; private object lastPhysicalGamepadObject; private int lastPhysicalGamepadDeviceId = int.MinValue; private string lastLegacyJoystickLifecycleSignature; private const float LegacyJoystickLifecyclePollInterval = 0.5f; private float nextLegacyJoystickLifecyclePoll; private string cachedLegacyJoystickLifecycleSignature; private readonly HashSet<MethodInfo> patchedFloatMethods = new HashSet<MethodInfo>(); private readonly HashSet<MethodInfo> patchedVector2Methods = new HashSet<MethodInfo>(); private bool loggedReflectionStatus; private readonly Dictionary<string, bool> customPressedState = new Dictionary<string, bool>(StringComparer.OrdinalIgnoreCase); private readonly Dictionary<string, bool> customDownState = new Dictionary<string, bool>(StringComparer.OrdinalIgnoreCase); private readonly Dictionary<string, bool> customUpState = new Dictionary<string, bool>(StringComparer.OrdinalIgnoreCase); private readonly Dictionary<string, int> physicalSampleFrame = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase); private int physicalPathSampleCacheFrame = -1; private readonly Dictionary<string, bool> physicalPathPressedCache = new Dictionary<string, bool>(StringComparer.OrdinalIgnoreCase); private readonly HashSet<string> unresolvedPhysicalPathSampleCache = new HashSet<string>(StringComparer.OrdinalIgnoreCase); private int customDigitalStatesUpdatedFrame = -1; private int plainLogicalEdgesUpdatedFrame = -1; private int routedLogicalEdgesUpdatedFrame = -1; private int customRoutingDecisionCacheFrame = -1; private readonly Dictionary<string, bool> customRoutingDecisionFrameCache = new Dictionary<string, bool>(StringComparer.OrdinalIgnoreCase); private const float RoutedEdgeLatchLifetime = 0.25f; private readonly Dictionary<string, bool> routedEffectiveHeldState = new Dictionary<string, bool>(StringComparer.OrdinalIgnoreCase); private readonly HashSet<string> routedDownLatch = new HashSet<string>(StringComparer.OrdinalIgnoreCase); private readonly HashSet<string> routedUpLatch = new HashSet<string>(StringComparer.OrdinalIgnoreCase); private readonly Dictionary<string, float> routedDownLatchTime = new Dictionary<string, float>(StringComparer.OrdinalIgnoreCase); private readonly Dictionary<string, float> routedUpLatchTime = new Dictionary<string, float>(StringComparer.OrdinalIgnoreCase); private readonly Dictionary<string, int> routedDownDeliveredFrame = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase); private readonly Dictionary<string, int> routedUpDeliveredFrame = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase); private readonly Dictionary<string, bool> plainEffectiveHeldState = new Dictionary<string, bool>(StringComparer.OrdinalIgnoreCase); private readonly HashSet<string> plainDownLatch = new HashSet<string>(StringComparer.OrdinalIgnoreCase); private readonly HashSet<string> plainUpLatch = new HashSet<string>(StringComparer.OrdinalIgnoreCase); private readonly Dictionary<string, float> plainDownLatchTime = new Dictionary<string, float>(StringComparer.OrdinalIgnoreCase); private readonly Dictionary<string, float> plainUpLatchTime = new Dictionary<string, float>(StringComparer.OrdinalIgnoreCase); private readonly Dictionary<string, int> plainDownDeliveredFrame = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase); private readonly Dictionary<string, int> plainUpDeliveredFrame = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase); private readonly List<string> edgeCleanupScratch = new List<string>(); private readonly HashSet<string> reconstructedActions = new HashSet<string>(StringComparer.OrdinalIgnoreCase); private readonly HashSet<string> loggedNativeHeldDivergenceActions = new HashSet<string>(StringComparer.OrdinalIgnoreCase); private bool previousNativeCrouchHeld; private bool previousJoyDodgeChordHeld; private MethodInfo characterJumpMethod; private PropertyInfo playerToggleBlockProperty; private FieldInfo characterBlockingField; private bool previousAuthoritativeBlockJumpJumpHeld; private bool previousAlternateJumpHandoffHeld; private readonly HashSet<string> gameplayUiQuarantinedPaths = new HashSet<string>(StringComparer.OrdinalIgnoreCase); private readonly List<string> gameplayUiReleaseScratch = new List<string>(); private bool gameplayUiWasBlocking; private bool gameplayUiPostExitReleaseGateActive; private int gameplayUiQuarantineUpdatedFrame = -1; private bool gameplayUiQuarantineLastBlockingState; private readonly HashSet<string> guiEntryQuarantinedPaths = new HashSet<string>(StringComparer.OrdinalIgnoreCase); private readonly Dictionary<string, int> guiEntryReleaseObservedFrame = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase); private readonly List<string> guiEntryReleaseScratch = new List<string>(); private MethodInfo playerInPlaceModeMethod; private FieldInfo playerLocalPlayerField; private readonly HashSet<MethodBase> patchedPlayerPlaceModeMethods = new HashSet<MethodBase>(); private bool buildPlacementStateInitialized; private bool cachedBuildPlacementModeActive; private bool routedContextInitialized; private bool routedContextBuildPlacement; private readonly HashSet<string> buildPriorityReleaseQuarantinedPaths = new HashSet<string>(StringComparer.OrdinalIgnoreCase); private readonly List<string> buildPriorityReleaseScratch = new List<string>(); private readonly List<string> activeBuildPriorityPaths = new List<string>(); private readonly HashSet<string> activeBuildPriorityPathSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase); private int activeBuildPriorityPathsFrame = -1; private int buildPriorityOwnershipGateUpdatedFrame = -1; private bool buildPriorityOwnershipGateLastActiveState; private bool buildPriorityContextInitialized; private bool buildPriorityWasActive; private const string BuildCopyAction = "BuildCopy"; private const string BuildHammerAction = "BuildHammer"; private const string SnapCycleHintKey = "__SnapCycleHint"; private readonly HashSet<string> buildCopyQuarantinedPaths = new HashSet<string>(StringComparer.OrdinalIgnoreCase); private bool buildCopyChordWasHeld; private bool buildCopyClearQuarantineAfterPlayerUpdate; private MethodInfo playerCopyPieceMethod; private FieldInfo playerBlockRemoveField; private bool patchedBuildCopyPlayerUpdate; private MethodInfo playerSetControls; private bool patchedPlayerSetControls; private readonly HashSet<MethodInfo> patchedNativeAltGuardMethods = new HashSet<MethodInfo>(); private readonly Dictionary<MethodBase, string[]> nativeAltGuardActionsByMethod = new Dictionary<MethodBase, string[]>(); [ThreadStatic] private static Stack<MethodBase> nativeAltGuardScopeStack; private readonly HashSet<string> activeModOwnedAlternateActions = new HashSet<string>(StringComparer.OrdinalIgnoreCase); private readonly HashSet<string> activeModOwnedAlternatePaths = new HashSet<string>(StringComparer.OrdinalIgnoreCase); private readonly HashSet<string> activeModOwnedAlternateShadowedActions = new HashSet<string>(StringComparer.OrdinalIgnoreCase); private readonly HashSet<string> modOwnedAlternateOwnedPathsUntilRelease = new HashSet<string>(StringComparer.OrdinalIgnoreCase); private readonly Dictionary<string, int> modOwnedAlternateOwnedPathLatchFrame = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase); private bool glyphMethodDiscoveryLogged; private Type gamepadType; private PropertyInfo gamepadCurrentProperty; private MethodInfo inputSystemFindControl; private MethodInfo inputSystemFindControls; private readonly Dictionary<string, PropertyInfo> controlProperties = new Dictionary<string, PropertyInfo>(); private readonly Dictionary<string, string[]> controlPathSegmentsCache = new Dictionary<string, string[]>(StringComparer.OrdinalIgnoreCase); private readonly Dictionary<string, MethodInfo> actionBoolMethodCache = new Dictionary<string, MethodInfo>(StringComparer.Ordinal); private readonly Dictionary<Type, PropertyInfo> gamepadDeviceIdPropertyCache = new Dictionary<Type, PropertyInfo>(); private readonly Dictionary<Type, FieldInfo> gamepadDeviceIdFieldCache = new Dictionary<Type, FieldInfo>(); private readonly Dictionary<string, object> resolvedControlCache = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase); private readonly HashSet<string> unresolvedControlCache = new HashSet<string>(StringComparer.OrdinalIgnoreCase); private string selectedAction = "JoyUse"; private bool waitingForBinding; private Rect windowRect = new Rect(40f, 40f, 650f, 700f); private Vector2 actionScroll; private string statusMessage = ""; private readonly Dictionary<string, List<string>> editorConflictCache = new Dictionary<string, List<string>>(StringComparer.OrdinalIgnoreCase); private readonly List<string> editorNoConflicts = new List<string>(0); private string editorConflictCacheWorkingSignature = string.Empty; private string editorConflictCacheBaselineSignature = string.Empty; private string editorConflictCacheGuiSource = string.Empty; private readonly Dictionary<string, string> editorBindingTextCache = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); private readonly Dictionary<string, string> editorDescriptionCache = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); private readonly Dictionary<string, string> editorAltTooltipCache = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); private string editorCrouchChordTooltip = string.Empty; private string editorBlockJumpToDodgeTooltip = string.Empty; private string editorPresentationCacheWorkingSignature = string.Empty; private string editorPresentationCacheBaselineSignature = string.Empty; private string editorPresentationCacheGamepadUi = string.Empty; private string editorPresentationCacheLastGuiSource = string.Empty; private bool editorPresentationCacheNintendoNormalization; private string actionSearchText = string.Empty; private string normalizedActionSearchText = string.Empty; private bool actionSearchFilterDirty; private readonly HashSet<string> actionSearchMatches = new HashSet<string>(StringComparer.OrdinalIgnoreCase); private GUIStyle windowStyle; private GUIStyle windowTitleStyle; private GUIStyle rowStyle; private GUIStyle groupedRowStyle; private GUIStyle groupedRowLineStyle; private GUIStyle actionStyle; private GUIStyle actionConflictStyle; private GUIStyle sectionHeaderStyle; private GUIStyle subsectionHeaderStyle; private GUIStyle bindingStyle; private GUIStyle smallButtonStyle; private GUIStyle deletePresetButtonStyle; private GUIStyle tooltipStyle; private GUIStyle crouchToggleStyle; private GUIStyle unsavedChangesStyle; private GUIStyle footerStatusStyle; private GUIStyle promptTextStyle; private GUIStyle presetNativeItemStyle; private GUIStyle presetCustomItemStyle; private GUIStyle toggleLabelStyle; private GUIStyle searchLabelStyle; private HashSet<string> verifiedStockIntentionalOverlapSignatures; private Texture2D toggleBoxTexture; private Texture2D toggleBoxHoverTexture; private Texture2D toggleInnerTexture; private Texture2D darkBackground; private Texture2D darkerBackground; private Texture2D buttonBackground; private Texture2D buttonHoverBackground; private Texture2D buttonActiveBackground; private Texture2D buttonSelectedBackground; private Texture2D deleteButtonBackground; private Texture2D deleteButtonHoverBackground; private Texture2D deleteButtonActiveBackground; private static readonly HashSet<string> VirtualLayerActions = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "JoyCrouch", "JoyRun", "JoyDodge" }; private static readonly HashSet<string> NativeAlternateFunctionDefaults = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "JoyAutoPickup", "JoyCamZoomIn", "JoyCamZoomOut", "JoyMiniMapZoomIn", "JoyMiniMapZoomOut", "JoyChat", "JoyToggleHUD" }; private static readonly HashSet<string> ForcedUnboundInternalActions = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "JoyBuildMenu", "JoyRotateRight" }; private static readonly string[] DigitalActions = new string[43] { "JoyUse", "JoyInventory", "JoyAttack", "JoySecondaryAttack", "JoyBlock", "JoyJump", "JoyRun", "JoyCrouch", "JoyDodge", "JoySit", "JoyHide", "JoyAutoPickup", "JoyGP", "JoyHotbarUse", "JoyHotbarLeft", "JoyHotbarRight", "JoyRadial", "JoyRadialInteract", "JoyRadialSecondaryInteract", "JoyRadialBack", "JoyRadialClose", "JoyBuildMenu", "JoyPlace", "JoyRemove", "JoyRotate", "JoyRotateRight", "JoyNextSnap", "JoyPrevSnap", "JoyCamZoomIn", "JoyCamZoomOut", "JoyMap", "JoyMiniMapZoomIn", "JoyMiniMapZoomOut", "JoyToggleHUD", "JoyMenu", "JoyBack", "JoyTabLeft", "JoyTabRight", "JoyChat", "JoyScrollChatUp", "JoyScrollChatDown", "JoyAltKeys", "JoyAltPlace" }; private static readonly HashSet<string> DigitalActionSet = new HashSet<string>(DigitalActions, StringComparer.OrdinalIgnoreCase); private static readonly string[] GameplayMenuActions = new string[18] { "JoyMenu", "JoyMap", "JoyInventory", "JoyUse", "JoyAttack", "JoySecondaryAttack", "JoyBlock", "JoyJump", "JoyRun", "JoyCrouch", "JoyDodge", "JoySit", "JoyHide", "JoyAutoPickup", "JoyHotbarLeft", "JoyHotbarRight", "JoyHotbarUse", "JoyGP" }; private static readonly string[] BuildingPrimaryMenuActions = new string[8] { "JoyPlace", "JoyRemove", "JoyRotate", "BuildCopy", "BuildHammer", "JoyAltPlace", "JoyNextSnap", "JoyPrevSnap" }; private static readonly HashSet<string> NativeOwnedFullMapActions = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "JoyMapZoomIn", "JoyMapZoomOut", "MapTogglePins", "MapPing", "MapRemovePin", "MapCrossOffPin", "MapAddPin", "MapVisiblePlayers" }; private static readonly HashSet<string> NativeOwnedReadOnlyActions = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "JoyBack", "JoyTabLeft", "JoyTabRight" }; private static readonly string[] PresetContextualActions = new string[4] { "BuildCopy", "BuildHammer", "GuiRepairItem", "GuiCraftUpgrade" }; private static readonly string[] PresetOwnedActions = (from action in DigitalActions.Concat(PresetContextualActions) where !NativeOwnedReadOnlyActions.Contains(action) && !IsForcedInternalUnbound(action) select action).Distinct<string>(StringComparer.OrdinalIgnoreCase).ToArray(); private static readonly string[] ConflictCandidateActions = DigitalActions.Concat(PresetContextualActions).Distinct<string>(StringComparer.OrdinalIgnoreCase).ToArray(); private static readonly string[] ContextualNativeSourceActions = new string[1] { "JoyButtonY" }; private static readonly string[] CameraMinimapMenuActions = new string[4] { "JoyCamZoomIn", "JoyCamZoomOut", "JoyMiniMapZoomIn", "JoyMiniMapZoomOut" }; private static readonly string[] RadialMenuActions = new string[5] { "JoyRadial", "JoyRadialInteract", "JoyRadialSecondaryInteract", "JoyRadialBack", "JoyRadialClose" }; private static readonly string[] RadialRuntimeInputActions = new string[4] { "JoyRadialInteract", "JoyRadialSecondaryInteract", "JoyRadialBack", "JoyRadialClose" }; private static readonly HashSet<string> RadialMenuActionSet = new HashSet<string>(RadialMenuActions, StringComparer.OrdinalIgnoreCase); private static readonly HashSet<string> VanillaControllerUiActions = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "JoyUse", "JoyInventory", "JoyAttack", "JoySecondaryAttack", "JoyBlock", "JoyJump", "JoyRun", "JoyCrouch", "JoyDodge", "JoySit", "JoyHide", "JoyAutoPickup", "JoyGP", "JoyHotbarUse", "JoyHotbarLeft", "JoyHotbarRight", "JoyCamZoomIn", "JoyCamZoomOut", "JoyMiniMapZoomIn", "JoyMiniMapZoomOut", "JoyMap", "JoyMenu", "JoyToggleHUD", "JoyChat", "JoyPlace", "JoyRemove", "JoyRotate", "JoyAltPlace", "JoyNextSnap", "JoyPrevSnap", "JoyRadial", "JoyRadialInteract", "JoyRadialSecondaryInteract", "JoyRadialBack", "JoyRadialClose" }; private const string GuiRepairItemAction = "GuiRepairItem"; private const string GuiCraftUpgradeAction = "GuiCraftUpgrade"; private const string GuiRepairNativeAction = "JoyRStick"; private const string GuiCraftUpgradeNativeAction = "JoyButtonX"; private const string NativeCraftQuantityModifierAction = "JoyLStick"; private const int InventoryGuiCraftGamepadGroup = 3; private static readonly string[] ContextualGuiActions = new string[2] { "GuiRepairItem", "GuiCraftUpgrade" }; private static readonly string[] GuiMenuActions = new string[2] { "GuiRepairItem", "GuiCraftUpgrade" }; private static readonly string[] HudMenuActions = new string[1] { "JoyToggleHUD" }; private static readonly string[] ChatMenuActions = new string[3] { "JoyChat", "JoyScrollChatUp", "JoyScrollChatDown" }; private static readonly HashSet<string> BuildPriorityActions = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "JoyPlace", "JoyRemove", "JoyRotate", "JoyNextSnap", "JoyPrevSnap", "JoyAltPlace" }; private static readonly string[] PlayerControlGameplayActions = new string[10] { "JoyUse", "JoyInventory", "JoyAttack", "JoySecondaryAttack", "JoyBlock", "JoyJump", "JoyRun", "JoyCrouch", "JoyDodge", "JoyGP" }; private static readonly HashSet<string> PlayerControlGameplayActionSet = new HashSet<string>(PlayerControlGameplayActions, StringComparer.OrdinalIgnoreCase); private const string GuiNavigationDpad = "D-pad"; private const string GuiNavigationRightStick = "Right Stick"; private const string GuiNavigationBoth = "Both"; private const string GuiNavigationLeftStick = "Left Stick"; private static readonly Dictionary<string, ActionInfo> ActionInfos = new Dictionary<string, ActionInfo>(StringComparer.OrdinalIgnoreCase) { { "JoyAttack", new ActionInfo("Attack", "Perform your equipped weapon's primary attack.") }, { "JoySecondaryAttack", new ActionInfo("Secondary attack", "Perform your equipped weapon's secondary attack, when available.") }, { "JoyBlock", new ActionInfo("Block", "Raise your equipped shield or weapon to block incoming attacks.") }, { "JoyDodge", new ActionInfo("Dodge", "Dodge.") }, { "JoyJump", new ActionInfo("Jump", "Make your character jump.") }, { "JoyRun", new ActionInfo("Run", "Sprint while moving.") }, { "JoyCrouch", new ActionInfo("Crouch", "Crouch.") }, { "JoySit", new ActionInfo("Sit", "Sit down or stand up.") }, { "JoyUse", new ActionInfo("Use", "Use or interact with nearby objects and characters.") }, { "JoyInventory", new ActionInfo("Inventory", "Open or close your inventory.") }, { "JoyHide", new ActionInfo("Hide/show weapon", "Toggle hiding or showing your currently equipped weapon or shield.") }, { "JoyAutoPickup", new ActionInfo("Toggle auto-pickup", "Toggle Valheim's automatic pickup behavior.") }, { "JoyGP", new ActionInfo("Forsaken power", "Activate the forsaken power.") }, { "JoyHotbarLeft", new ActionInfo("Prv. (Hotbar)", "Move the hotbar selection one slot to the left.") }, { "JoyHotbarRight", new ActionInfo("Next (Hotbar)", "Move the hotbar selection one slot to the right.") }, { "JoyHotbarUse", new ActionInfo("Use (Hotbar)", "Use or equip the currently selected hotbar item.") }, { "JoyCamZoomIn", new ActionInfo("Zoom in view", "Move the camera closer to your character.") }, { "JoyCamZoomOut", new ActionInfo("Zoom out view", "Move the camera farther from your character.") }, { "JoyMap", new ActionInfo("Map", "Open or close the map.") }, { "JoyPlace", new ActionInfo("Place", "Place the currently selected build piece.") }, { "JoyRemove", new ActionInfo("Deconstruct", "Remove the currently targeted build piece.") }, { "JoyRotate", new ActionInfo("Rotate", "Rotate the selected build piece.") }, { "BuildCopy", new ActionInfo("Copy", "Copy the targeted build piece. Alt is optional; when enabled, Alternate Function + Copy input form the chord.") }, { "BuildHammer", new ActionInfo("Hammer", "Select the Hammer/build tool control used by the build menu.") }, { "JoyNextSnap", new ActionInfo("Next snap point", "Cycle to the next available snap point while building.") }, { "JoyPrevSnap", new ActionInfo("Previous snap point", "Cycle to the previous available snap point while building.") }, { "JoyAltPlace", new ActionInfo("Alternative placement", "Switch to Valheim's alternative build-placement mode where supported.") }, { "JoyTabLeft", new ActionInfo("Previous tab", "Move to the previous tab in interfaces that support controller tab navigation.") }, { "JoyTabRight", new ActionInfo("Next tab", "Move to the next tab in interfaces that support controller tab navigation.") }, { "JoyToggleHUD", new ActionInfo("Toggle HUD", "Show or hide the game's HUD.") }, { "JoyChat", new ActionInfo("Chat", "Open or activate the chat interface.") }, { "JoyScrollChatUp", new ActionInfo("Scroll chat up", "Scroll the chat history upward.") }, { "JoyScrollChatDown", new ActionInfo("Scroll chat down", "Scroll the chat history downward.") }, { "JoyMenu", new ActionInfo("Menu", "Open the game's pause menu.") }, { "JoyBack", new ActionInfo("Back / Select", "Perform the controller's back or select function in supported interfaces.") }, { "GuiRepairItem", new ActionInfo("Repair Item", "Trigger Valheim's inventory repair action when repair is available.") }, { "GuiCraftUpgrade", new ActionInfo("Craft / Upgrade", "Trigger Valheim's Craft / Upgrade action for the currently selected recipe.") }, { "JoyRadial", new ActionInfo("Radial Menu", "Open or activate the radial menu.") }, { "JoyRadialInteract", new ActionInfo("Interact", "Perform the radial menu's primary selection action.") }, { "JoyRadialSecondaryInteract", new ActionInfo("Drop", "Perform the radial menu's secondary interaction action.") }, { "JoyRadialBack", new ActionInfo("Back", "Go back from the radial-menu selection.") }, { "JoyRadialClose", new ActionInfo("Close", "Close the radial menu.") }, { "JoyAltKeys", new ActionInfo("Alternate function", "Hold this input to access Valheim's alternate controller functions.") }, { "JoyStart", new ActionInfo("Start", "Perform the controller's Start function in supported interfaces.") }, { "JoyButtonA", new ActionInfo("A Button", "Represent the controller's A button.") }, { "JoyButtonB", new ActionInfo("B Button", "Represent the controller's B button.") }, { "JoyButtonX", new ActionInfo("X Button", "Represent the controller's X button.") }, { "JoyButtonY", new ActionInfo("Y Button", "Represent the controller's Y button.") }, { "JoyLBumper", new ActionInfo("Left Bumper", "Represent the controller's left bumper.") }, { "JoyRBumper", new ActionInfo("Right Bumper", "Represent the controller's right bumper.") }, { "JoyLTrigger", new ActionInfo("Left Trigger", "Represent the controller's left trigger.") }, { "JoyRTrigger", new ActionInfo("Right Trigger", "Represent the controller's right trigger.") }, { "JoyDPadUp", new ActionInfo("D-pad Up", "Represent the controller's D-pad up direction.") }, { "JoyDPadDown", new ActionInfo("D-pad Down", "Represent the controller's D-pad down direction.") }, { "JoyDPadLeft", new ActionInfo("D-pad Left", "Represent the controller's D-pad left direction.") }, { "JoyDPadRight", new ActionInfo("D-pad Right", "Represent the controller's D-pad right direction.") }, { "JoyLStickUp", new ActionInfo("Left Stick Up", "Represent the controller's left stick up direction.") }, { "JoyLStickDown", new ActionInfo("Left Stick Down", "Represent the controller's left stick down direction.") }, { "JoyLStickLeft", new ActionInfo("Left Stick Left", "Represent the controller's left stick left direction.") }, { "JoyLStickRight", new ActionInfo("Left Stick Right", "Represent the controller's left stick right direction.") }, { "JoyRStickUp", new ActionInfo("Right Stick Up", "Represent the controller's right stick up direction.") }, { "JoyRStickDown", new ActionInfo("Right Stick Down", "Represent the controller's right stick down direction.") }, { "JoyRStickLeft", new ActionInfo("Right Stick Left", "Represent the controller's right stick left direction.") }, { "JoyRStickRight", new ActionInfo("Right Stick Right", "Represent the controller's right stick right direction.") }, { "JoyMiniMapZoomIn", new ActionInfo("Minimap Zoom In", "Increase the minimap's zoom level.") }, { "JoyMiniMapZoomOut", new ActionInfo("Minimap Zoom Out", "Decrease the minimap's zoom level.") } }; private static readonly Dictionary<string, string> ControlNames = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase) { { "<Gamepad>/buttonSouth", "A" }, { "<Gamepad>/buttonEast", "B" }, { "<Gamepad>/buttonWest", "X" }, { "<Gamepad>/buttonNorth", "Y" }, { "<Gamepad>/leftShoulder", "LB" }, { "<Gamepad>/rightShoulder", "RB" }, { "<Gamepad>/leftTrigger", "LT" }, { "<Gamepad>/rightTrigger", "RT" }, { "<Gamepad>/leftStickPress", "L3" }, { "<Gamepad>/rightStickPress", "R3" }, { "<Gamepad>/dpad/up", "D-pad Up" }, { "<Gamepad>/dpad/down", "D-pad Down" }, { "<Gamepad>/dpad/left", "D-pad Left" }, { "<Gamepad>/dpad/right", "D-pad Right" }, { "<Gamepad>/start", "Start" }, { "<Gamepad>/select", "Select" }, { "<Gamepad>/leftStick/up", "Left Stick Up" }, { "<Gamepad>/leftStick/down", "Left Stick Down" }, { "<Gamepad>/leftStick/left", "Left Stick Left" }, { "<Gamepad>/leftStick/right", "Left Stick Right" }, { "<Gamepad>/rightStick/up", "Right Stick Up" }, { "<Gamepad>/rightStick/down", "Right Stick Down" }, { "<Gamepad>/rightStick/left", "Right Stick Left" }, { "<Gamepad>/rightStick/right", "Right Stick Right" } }; private static readonly Dictionary<string, string> ControlPropertyAliases = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase) { { "leftStickPress", "leftStickButton" }, { "rightStickPress", "rightStickButton" }, { "start", "startButton" }, { "select", "selectButton" } }; private static readonly string[] CaptureOrder = new string[24] { "<Gamepad>/buttonSouth", "<Gamepad>/buttonEast", "<Gamepad>/buttonWest", "<Gamepad>/buttonNorth", "<Gamepad>/leftShoulder", "<Gamepad>/rightShoulder", "<Gamepad>/leftTrigger", "<Gamepad>/rightTrigger", "<Gamepad>/leftStickPress", "<Gamepad>/rightStickPress", "<Gamepad>/dpad/up", "<Gamepad>/dpad/down", "<Gamepad>/dpad/left", "<Gamepad>/dpad/right", "<Gamepad>/start", "<Gamepad>/select", "<Gamepad>/leftStick/up", "<Gamepad>/leftStick/down", "<Gamepad>/leftStick/left", "<Gamepad>/leftStick/right", "<Gamepad>/rightStick/up", "<Gamepad>/rightStick/down", "<Gamepad>/rightStick/left", "<Gamepad>/rightStick/right" }; private bool showEditorWindow; private static readonly string[] GamepadUiOptions = new string[8] { "Xbox", "Steam Deck (Xbox)", "PlayStation 5 / DualSense", "PlayStation 4 / DualShock", "Steam Deck (PlayStation)", "Switch Pro", "Switch Joy-Con", "Switch 2 Joy-Con / Mouse" }; private static readonly Dictionary<string, string> GamepadUiInstanceFields = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase) { { "Xbox", "xboxMapInstance" }, { "Steam Deck (Xbox)", "steamDeckXboxMapInstance" }, { "PlayStation 5 / DualSense", "dualSenseMapInstance" }, { "PlayStation 4 / DualShock", "dualShockMapInstance" }, { "Steam Deck (PlayStation)", "steamDeckPSMapInstance" }, { "Switch Pro", "switchProMapInstance" }, { "Switch Joy-Con", "switchJoyConMapInstance" }, { "Switch 2 Joy-Con / Mouse", "switchJoyCon2MouseMapInstance" } }; private static readonly Dictionary<string, string> GamepadUiPrefabFields = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase) { { "Xbox", "xboxMapPrefab" }, { "Steam Deck (Xbox)", "steamDeckXboxMapPrefab" }, { "PlayStation 5 / DualSense", "ps5MapPrefab" }, { "PlayStation 4 / DualShock", "oldPsMapPrefab" }, { "Steam Deck (PlayStation)", "steamDeckPSMapPrefab" }, { "Switch Pro", "switchProMapPrefab" }, { "Switch Joy-Con", "switchJoyConMapPrefab" }, { "Switch 2 Joy-Con / Mouse", "switchJoyCon2MouseMapPrefab" } }; private readonly HashSet<int> manuallyCreatedGamepadMapIds = new HashSet<int>(); private static readonly Dictionary<string, string> GamepadMapFieldByPath = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase) { { "<Gamepad>/buttonSouth", "joyButton0" }, { "<Gamepad>/buttonEast", "joyButton1" }, { "<Gamepad>/buttonWest", "joyButton2" }, { "<Gamepad>/buttonNorth", "joyButton3" }, { "<Gamepad>/leftShoulder", "joyButton4" }, { "<Gamepad>/rightShoulder", "joyButton5" }, { "<Gamepad>/select", "joyButton6" }, { "<Gamepad>/start", "joyButton7" }, { "<Gamepad>/leftTrigger", "joyAxis9" }, { "<Gamepad>/rightTrigger", "joyAxis10" }, { "<Gamepad>/leftStickPress", "joyButton8" }, { "<Gamepad>/rightStickPress", "joyButton9" }, { "<Gamepad>/dpad/up", "joyAxis7Up" }, { "<Gamepad>/dpad/down", "joyAxis7Down" }, { "<Gamepad>/dpad/left", "joyAxis6Left" }, { "<Gamepad>/dpad/right", "joyAxis6Right" } }; private static readonly string[] LeftStickDirectionPaths = new string[4] { "<Gamepad>/leftStick/up", "<Gamepad>/leftStick/down", "<Gamepad>/leftStick/left", "<Gamepad>/leftStick/right" }; private static readonly string[] RightStickDirectionPaths = new string[4] { "<Gamepad>/rightStick/up", "<Gamepad>/rightStick/down", "<Gamepad>/rightStick/left", "<Gamepad>/rightStick/right" }; private static readonly Dictionary<string, Type> SuccessfulTypeLookupCache = new Dictionary<string, Type>(StringComparer.Ordinal); internal static bool ProximityInteractionFeatureEnabled { get { if ((Object)(object)Instance != (Object)null) { return Instance.IsProximityInteractionEnabled(); } return false; } } internal static bool ProximityInteractionDebugLogging { get { if ((Object)(object)Instance != (Object)null && Instance.debug != null) { return Instance.debug.Value; } return false; } } private static bool DefaultRequiresAlternateFunction(string action) { if (string.Equals(action, "JoyDodge", StringComparison.OrdinalIgnoreCase)) { return false; } if (!NativeAlternateFunctionDefaults.Contains(action) && !string.Equals(action, "JoySit", StringComparison.OrdinalIgnoreCase)) { return string.Equals(action, "JoyHide", StringComparison.OrdinalIgnoreCase); } return true; } private static bool IsForcedInternalUnbound(string action) { if (!string.IsNullOrEmpty(action)) { return ForcedUnboundInternalActions.Contains(action); } return false; } private static bool IsNativeOwnedFullMapAction(string action) { if (!string.IsNullOrEmpty(action)) { return NativeOwnedFullMapActions.Contains(action); } return false; } private static bool IsNativeOwnedReadOnlyAction(string action) { if (!string.IsNullOrEmpty(action)) { return NativeOwnedReadOnlyActions.Contains(action); } return false; } private void LoadGlobalSettingsFile() { loadedGlobalSettings.Clear(); globalSettingsFileExisted = !string.IsNullOrWhiteSpace(globalSettingsFilePath) && File.Exists(globalSettingsFilePath); if (!globalSettingsFileExisted) { return; } try { string[] array = File.ReadAllLines(globalSettingsFilePath); for (int i = 0; i < array.Length; i++) { string text = (array[i] ?? string.Empty).Trim(); if (text.Length == 0 || text.StartsWith("#", StringComparison.Ordinal) || text.StartsWith(";", StringComparison.Ordinal)) { continue; } int num = text.IndexOf('='); if (num > 0) { string text2 = text.Substring(0, num).Trim(); string value = text.Substring(num + 1).Trim(); if (text2.Length > 0) { loadedGlobalSettings[text2] = value; } } } } catch (Exception ex) { Log.LogWarning((object)("Could not read mod settings file '" + globalSettingsFilePath + "': " + ex.Message)); } if (!loadedGlobalSettings.TryGetValue("SettingsFormatVersion", out var value2) || !string.Equals(value2, "2", StringComparison.Ordinal)) { loadedGlobalSettings.Clear(); globalSettingsFileExisted = false; } } private string GetInitialGlobalString(string settingsKey, string defaultValue) { if (!loadedGlobalSettings.TryGetValue(settingsKey, out var value)) { return defaultValue; } return value ?? defaultValue; } private bool GetInitialGlobalBool(string settingsKey, bool defaultValue) { if (!bool.TryParse(GetInitialGlobalString(settingsKey, defaultValue.ToString()), out var result)) { return defaultValue; } return result; } private unsafe KeyCode GetInitialGlobalKeyCode(string settingsKey, KeyCode defaultValue) { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) if (!Enum.TryParse<KeyCode>(GetInitialGlobalString(settingsKey, ((object)(*(KeyCode*)(&defaultValue))/*cast due to .constrained prefix*/).ToString()), ignoreCase: true, out KeyCode result)) { return defaultValue; } return result; } private void InitializeRuntimeSettings() { //IL_0029: Unknown result type (might be due to invalid IL or missing references) LoadGlobalSettingsFile(); debug = new RuntimeSetting<bool>(GetInitialGlobalBool("DebugLogging", defaultValue: false)); editorKey = new RuntimeSetting<KeyCode>(GetInitialGlobalKeyCode("OpenEditorKey", (KeyCode)289)); showEditor = new RuntimeSetting<bool>(GetInitialGlobalBool("ShowEditor", defaultValue: true)); proximityInteractionEnabled = new RuntimeSetting<bool>(initialValue: false); string text = GetInitialGlobalString("GamepadUI", "Xbox"); if (string.Equals(text, "Auto / Game default", StringComparison.OrdinalIgnoreCase)) { text = "Xbox"; } gamepadUiSelection = new RuntimeSetting<string>(string.IsNullOrWhiteSpace(text) ? "Xbox" : text); nintendoFaceButtonNormalization = new RuntimeSetting<bool>(GetInitialGlobalBool("NintendoFaceButtonNormalization", defaultValue: true)); crouchInput = new RuntimeSetting<string>(string.Empty); crouchUseChord = new RuntimeSetting<bool>(initialValue: false); blockJumpToDodge = new RuntimeSetting<bool>(initialValue: true); moveWhileContainerGuiOpen = new RuntimeSetting<bool>(initialValue: false); buildCopyInput = new RuntimeSetting<string>(string.Empty); buildCopyRequiresAlt = new RuntimeSetting<bool>(initialValue: true); buildHammerInput = new RuntimeSetting<string>(string.Empty); } private string BuildGlobalSettingsText() { //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine("# Controller / Gamepad Remapping mod settings"); stringBuilder.AppendLine("# Controller layouts, bindings, and layout-owned behaviors are stored only in *.preset files."); stringBuilder.AppendLine("# This is the mod-level config. Layout data belongs in individual *.preset files."); stringBuilder.AppendLine("SettingsFormatVersion=2"); stringBuilder.AppendLine("DebugLogging=" + (debug != null && debug.Value)); stringBuilder.AppendLine("OpenEditorKey=" + ((editorKey != null) ? ((object)editorKey.Value/*cast due to .constrained prefix*/).ToString() : ((object)(KeyCode)289/*cast due to .constrained prefix*/).ToString())); stringBuilder.AppendLine("ShowEditor=" + (showEditor == null || showEditor.Value)); stringBuilder.AppendLine("GamepadUI=" + ((gamepadUiSelection != null) ? (gamepadUiSelection.Value ?? "Xbox") : "Xbox")); stringBuilder.AppendLine("NintendoFaceButtonNormalization=" + (nintendoFaceButtonNormalization == null || nintendoFaceButtonNormalization.Value)); return stringBuilder.ToString(); } private void SaveGlobalSettingsIfChanged() { if (string.IsNullOrWhiteSpace(globalSettingsFilePath) || debug == null || editorKey == null || showEditor == null || gamepadUiSelection == null || nintendoFaceButtonNormalization == null) { return; } string text = BuildGlobalSettingsText(); if (string.Equals(text, lastSavedGlobalSettingsSignature, StringComparison.Ordinal)) { return; } try { Directory.CreateDirectory(Path.GetDirectoryName(globalSettingsFilePath) ?? Paths.ConfigPath); string text2 = globalSettingsFilePath + ".tmp"; File.WriteAllText(text2, text, new UTF8Encoding(encoderShouldEmitUTF8Identifier: false)); File.Copy(text2, globalSettingsFilePath, overwrite: true); File.Delete(text2); lastSavedGlobalSettingsSignature = text; } catch (Exception ex) { Log.LogWarning((object)("Could not save mod settings file '" + globalSettingsFilePath + "': " + ex.Message)); } } private void FinalizeGlobalSettingsInitialization() { EventHandler value = delegate { SaveGlobalSettingsIfChanged(); }; debug.SettingChanged += value; editorKey.SettingChanged += value; showEditor.SettingChanged += value; gamepadUiSelection.SettingChanged += value; nintendoFaceButtonNormalization.SettingChanged += value; string text = BuildGlobalSettingsText(); HashSet<string> canonicalKeys = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "SettingsFormatVersion", "DebugLogging", "OpenEditorKey", "ShowEditor", "GamepadUI", "NintendoFaceButtonNormalization" }; if (globalSettingsFileExisted && !loadedGlobalSettings.Keys.Any((string key) => !canonicalKeys.Contains(key)) && loadedGlobalSettings.ContainsKey("SettingsFormatVersion") && loadedGlobalSettings.ContainsKey("DebugLogging") && loadedGlobalSettings.ContainsKey("OpenEditorKey") && loadedGlobalSettings.ContainsKey("ShowEditor") && loadedGlobalSettings.ContainsKey("GamepadUI") && loadedGlobalSettings.ContainsKey("NintendoFaceButtonNormalization")) { lastSavedGlobalSettingsSignature = text; } else { SaveGlobalSettingsIfChanged(); } } private void Awake() { //IL_0245: Unknown result type (might be due to invalid IL or missing references) //IL_024f: Expected O, but got Unknown Instance = this; Log = ((BaseUnityPlugin)this).Logger; string path = Paths.ConfigPath ?? string.Empty; customPresetDirectoryPath = Path.Combine(path, "ControllerPresets"); customPresetStateFilePath = Path.Combine(customPresetDirectoryPath, "_ControllerPresets.state"); globalSettingsFilePath = Path.Combine(customPresetDirectoryPath, "_ControllerGamepadRemapping.settings"); try { Directory.CreateDirectory(customPresetDirectoryPath); } catch (Exception ex) { Log.LogWarning((object)("Could not create custom preset directory '" + customPresetDirectoryPath + "': " + ex.Message)); } InitializeRuntimeSettings(); if (LoadSavedCustomPresets() == CustomPresetLoadSource.PerPresetFilesWithoutState) { SaveNamedPresetStore(); } SavedCustomPreset savedCustomPreset = FindSavedCustomPreset(activeCustomPresetName); if (savedCustomPreset != null) { SetWorkingPresetBehaviors(savedCustomPreset.Behaviors, markDirty: false); } else if (savedCustomPresets.Count > 0) { SetWorkingPresetBehaviors(CreateNativePresetBehaviorDefaults(), markDirty: false); } crouchInput.SettingChanged += delegate { if (!suppressPresetRuntimeStateEvents) { ScheduleActionApply("JoyCrouch", 0.05f, "Crouch binding changed", waitForPhysicalNeutral: true); } }; crouchUseChord.SettingChanged += delegate { if (!suppressPresetRuntimeStateEvents) { SetWorkingCrouchChord(crouchUseChord.Value); } }; blockJumpToDodge.SettingChanged += delegate { if (!suppressPresetRuntimeStateEvents) { SetWorkingBlockJumpToDodge(blockJumpToDodge.Value); } }; moveWhileContainerGuiOpen.SettingChanged += delegate { if (!suppressPresetRuntimeStateEvents) { SetWorkingMoveWhileGui(moveWhileContainerGuiOpen.Value); } }; proximityInteractionEnabled.SettingChanged += delegate { if (!suppressPresetRuntimeStateEvents) { SetWorkingProximityInteraction(proximityInteractionEnabled.Value); } }; buildCopyInput.SettingChanged += OnBuildCopyBindingChanged; buildCopyRequiresAlt.SettingChanged += OnBuildCopyBindingChanged; buildHammerInput.SettingChanged += OnBuildHammerBindingChanged; string[] digitalActions = DigitalActions; foreach (string action in digitalActions) { if (!IsForcedInternalUnbound(action) && !IsNativeOwnedReadOnlyAction(action)) { EnsureBindingEntry(action); } } digitalActions = DigitalActions; foreach (string action2 in digitalActions) { if (!IsForcedInternalUnbound(action2) && !IsNativeOwnedReadOnlyAction(action2) && IsAlternateFunctionEligible(action2)) { EnsureAlternateFunctionEntry(action2); } } digitalActions = ContextualGuiActions; foreach (string action3 in digitalActions) { EnsureGuiBindingEntry(action3); } FinalizeGlobalSettingsInitialization(); harmony = new Harmony("nussbacker.valheim.controllergamepadremapping"); harmony.PatchAll(typeof(Plugin).Assembly); TryPrepareZInputReflection(); TryPrepareInputSystemReflection(); TryPatchZInput(); TryPatchEditorExclusiveCursorOwnership(); TryPatchBuildRotateHintSource(); TryPatchUniversalUnboundHintSources(); TryPatchRadialUnboundHintSources(); TryPatchContextualGuiActions(); TryPatchNativeCraftQuantityContext(); TryPatchMovementGuiLifecycleEvents(); TryPatchControllerPresetLifecycle(); TryPatchKeyboardMouseSettingsRollback(); TryPatchTabSoundTrace(); TryPatchBuildUiSelectableNavigation(); TryPatchPlayerSetControls(); TryPatchPlayerPlaceModeLifecycle(); TryPatchBuildCopyPlayerUpdate(); TryPatchNativeAltGuardPaths(); TryPatchAuthoritativeGamepadMap(); Log.LogInfo((object)"Controller / Gamepad Remapping 3.0.2 by nussbacker loaded. General controller remapping enabled. Optional behaviors: Crouch Chord, Block & Jump to Dodge, Move while in GUI, Proximity Interaction 1.0.7."); if (debug.Value) { Log.LogInfo((object)"Native-first controller engine enabled: Valheim ButtonDefs own normal remaps, Unbound and Alternate Function; custom runtime code is limited to contextual features."); } } private void OnBuildCopyBindingChanged(object sender, EventArgs e) { InvalidateResolvedControlCache(); if (buildCopyInput == null || !string.Equals(buildCopyInput.Value, "__UNBOUND__", StringComparison.OrdinalIgnoreCase)) { RestoreHiddenUnboundKeyHintsForAction("BuildCopy"); } buildCopyChordWasHeld = false; buildCopyClearQuarantineAfterPlayerUpdate = false; buildCopyQuarantinedPaths.Clear(); cachedBuildCopyHintTextObject = null; cachedBuildCopyHintTextObjects.Clear(); keyHintPresentationCacheInitialized = false; buildCopyHintPresentationLogged = false; keyHintPresentationCacheValidationFrame = -1; if (!suppressPresetRuntimeStateEvents) { UpdateWorkingPresetContextualActionFromRuntimeState("BuildCopy"); if (debug != null && debug.Value) { Log.LogInfo((object)("Copy binding presentation invalidated -> " + ((GetBuildCopyInputPath() != null) ? PrettyPath(GetBuildCopyInputPath()) : "Unbound") + (BuildCopyUsesAlternateFunction() ? " + Alternate Function" : ""))); } if (showEditorWindow) { editorDeferredPresentationRefresh = true; } else { RefreshKeyHints(); } } } private void OnBuildHammerBindingChanged(object sender, EventArgs e) { InvalidateResolvedControlCache(); cachedBuildUiHammerTextObject = null; cachedBuildUiHammerOriginalGlyph = null; if (!suppressPresetRuntimeStateEvents) { UpdateWorkingPresetContextualActionFromRuntimeState("BuildHammer"); if (movementGuiBuildSelectionOpen && cachedBuildUiHammerOwner != null) { TryRewriteBuildHammerPresentation(cachedBuildUiHammerOwner); } } } private void RefreshControllerPresentationViews() { RefreshKeyHints(); RefreshGamepadGlyphEvents(); RefreshVisibleGamepadMapLabelsOnly(); } private void EnsureBindingEntry(string action) { if (bindings.ContainsKey(action)) { return; } RuntimeSetting<string> runtimeSetting = new RuntimeSetting<string>(string.Empty); bindings[action] = runtimeSetting; string trackedAction = action; runtimeSetting.SettingChanged += delegate { InvalidateResolvedControlCache(); bool flag = string.Equals(trackedAction, "JoyAltKeys", StringComparison.OrdinalIgnoreCase); if (flag) { InvalidateAlternateFunctionGlyphCache(); } if (!IsExplicitlyUnbound(trackedAction)) { RestoreHiddenUnboundKeyHintsForAction(trackedAction); } if (!suppressPresetRuntimeStateEvents) { ScheduleActionApply(trackedAction, 0.05f, "binding runtime state changed", waitForPhysicalNeutral: true); if (flag) { ResetRoutedLogicalStateForConfigurationChange("Alternate Function input changed"); } } }; } private void EnsureGuiBindingEntry(string action) { if (!IsContextualGuiAction(action) || guiBindings.ContainsKey(action)) { return; } RuntimeSetting<string> runtimeSetting = new RuntimeSetting<string>(string.Empty); guiBindings[action] = runtimeSetting; RefreshContextualGuiCustomBindingFlag(); runtimeSetting.SettingChanged += delegate { InvalidateResolvedControlCache(); RefreshContextualGuiCustomBindingFlag(); if (contextualGuiCustomBindingActive && harmony != null && !patchedInventoryGuiShortcutUpdate) { TryPatchContextualGuiActions(); } if (!suppressPresetRuntimeStateEvents) { UpdateWorkingPresetContextualActionFromRuntimeState(action); } }; } private void RefreshContextualGuiCustomBindingFlag() { contextualGuiCustomBindingActive = guiBindings.Values.Any((RuntimeSetting<string> entry) => entry != null && !string.IsNullOrWhiteSpace(entry.Value) && !string.Equals(entry.Value, "__UNBOUND__", StringComparison.OrdinalIgnoreCase)); } private static bool IsContextualGuiAction(string action) { if (!string.IsNullOrEmpty(action)) { return ContextualGuiActions.Any((string a) => string.Equals(a, action, StringComparison.OrdinalIgnoreCase)); } return false; } private static bool IsAlternateFunctionEligible(string action) { if (string.IsNullOrEmpty(action) || IsForcedInternalUnbound(action) || IsNativeOwnedReadOnlyAction(action) || IsContextualGuiAction(action)) { return false; } if (!string.Equals(action, "JoyAltKeys", StringComparison.OrdinalIgnoreCase)) { return !string.Equals(action, "JoyAltPlace", StringComparison.OrdinalIgnoreCase); } return false; } private static bool IsMenuNavigationAction(string action) { if (!string.Equals(action, "JoyTabLeft", StringComparison.OrdinalIgnoreCase) && !string.Equals(action, "JoyTabRight", StringComparison.OrdinalIgnoreCase) && !string.Equals(action, "JoyMenu", StringComparison.OrdinalIgnoreCase) && !string.Equals(action, "JoyBack", StringComparison.OrdinalIgnoreCase) && !string.Equals(action, "JoyChat", StringComparison.OrdinalIgnoreCase) && !string.Equals(action, "JoyScrollChatUp", StringComparison.OrdinalIgnoreCase)) { return string.Equals(action, "JoyScrollChatDown", StringComparison.OrdinalIgnoreCase); } return true; } private void EnsureAlternateFunctionEntry(string action) { if (!IsAlternateFunctionEligible(action) || alternateFunctionRequirements.ContainsKey(action)) { return; } bool initialValue = DefaultRequiresAlternateFunction(action); RuntimeSetting<bool> entry = new RuntimeSetting<bool>(initialValue); alternateFunctionRequirements[action] = entry; string trackedAction = action; entry.SettingChanged += delegate { if (!suppressPresetRuntimeStateEvents) { if (string.Equals(trackedAction, "JoyCrouch", StringComparison.OrdinalIgnoreCase) && entry.Value && IsCrouchChordEnabled()) { SetWorkingCrouchChord(enabled: false); previousNativeCrouchHeld = false; } ScheduleActionApply(trackedAction, 0.01f, "Alternate Function requirement changed"); RefreshControllerPresentationViews(); } }; } private bool RequiresAlternateFunction(string action) { if (string.Equals(action, "BuildCopy", StringComparison.OrdinalIgnoreCase)) { return BuildCopyUsesAlternateFunction(); } if (IsNativeOwnedReadOnlyAction(action)) { if (originalBindings.TryGetValue(action, out var value) && value != null) { return value.AltKey; } return NativeAlternateFunctionDefaults.Contains(action); } if (!IsAlternateFunctionEligible(action)) { return false; } if (TryGetWorkingLayoutBinding(action, out var binding)) { return binding.AlternateFunction; } if (alternateFunctionRequirements.TryGetValue(action, out var value2) && value2 != null) { return value2.Value; } return false; } private bool IsValheimNativeAlternateAction(string action) { if (string.IsNullOrEmpty(action)) { return false; } if (originalBindings.TryGetValue(action, out var value) && value != null) { return value.AltKey; } return false; } private bool IsModOwnedAlternateAction(string action) { if (!string.IsNullOrEmpty(action) && IsAlternateFunctionEligible(action) && !IsExplicitlyUnbound(action) && RequiresAlternateFunction(action)) { return !IsValheimNativeAlternateAction(action); } return false; } private void RefreshActiveNativeAlternateActions() { activeNativeAlternateActions.Clear(); activeModOwnedAlternateActions.Clear(); string[] digitalActions = DigitalActions; foreach (string text in digitalActions) { if (IsAlternateFunctionEligible(text) && !IsExplicitlyUnbound(text) && RequiresAlternateFunction(text)) { activeNativeAlternateActions.Add(text); if (IsModOwnedAlternateAction(text)) { activeModOwnedAlternateActions.Add(text); } } } nativePlayerModifierOwnershipPossible = IsNormalActionOnAlternateModifierPath("JoyAttack") || IsNormalActionOnAlternateModifierPath("JoySecondaryAttack") || IsNormalActionOnAlternateModifierPath("JoyBlock") || IsNormalActionOnAlternateModifierPath("JoyJump") || IsNormalActionOnAlternateModifierPath("JoyRun") || IsNormalActionOnAlternateModifierPath("JoyCrouch") || IsNormalActionOnAlternateModifierPath("JoyDodge"); RefreshModOwnedAlternateOwnershipCache(); RefreshModOwnedAlternateConsumerPatches(); } private void RefreshModOwnedAlternateOwnershipCache() { activeModOwnedAlternatePaths.Clear(); activeModOwnedAlternateShadowedActions.Clear(); foreach (string activeModOwnedAlternateAction in activeModOwnedAlternateActions) { string text = NormalizeBindingPath(GetConfiguredRuntimePath(activeModOwnedAlternateAction)); if (text != null) { activeModOwnedAlternatePaths.Add(text); } } if (activeModOwnedAlternatePaths.Count == 0) { modOwnedAlternateOwnedPathsUntilRelease.Clear(); modOwnedAlternateOwnedPathLatchFrame.Clear(); return; } string[] digitalActions = DigitalActions; foreach (string text2 in digitalActions) { if (!string.IsNullOrEmpty(text2) && !activeModOwnedAlternateActions.Contains(text2) && !IsExplicitlyUnbound(text2) && !RequiresAlternateFunction(text2) && !IsMenuNavigationAction(text2) && !string.Equals(text2, "JoyAltKeys", StringComparison.OrdinalIgnoreCase) && !string.Equals(text2, "JoyAltPlace", StringComparison.OrdinalIgnoreCase)) { string path = NormalizeBindingPath(GetConfiguredRuntimePath(text2)); if (path != null && activeModOwnedAlternatePaths.Contains(path) && (!string.Equals(text2, "JoyRun", StringComparison.OrdinalIgnoreCase) || activeModOwnedAlternateActions.Any((string owner) => (GetConflictContexts(owner) & ConflictContext.Building) == 0 && string.Equals(NormalizeBindingPath(GetConfiguredRuntimePath(owner)), path, StringComparison.OrdinalIgnoreCase)))) { activeModOwnedAlternateShadowedActions.Add(text2); } } } modOwnedAlternateOwnedPathsUntilRelease.RemoveWhere((string item) => !activeModOwnedAlternatePaths.Contains(item)); digitalActions = modOwnedAlternateOwnedPathLatchFrame.Keys.ToArray(); foreach (string text3 in digitalActions) { if (!activeModOwnedAlternatePaths.Contains(text3)) { modOwnedAlternateOwnedPathLatchFrame.Remove(text3); } } } private bool IsAnyNativeAlternateChordHeld() { string text = NormalizeBindingPath(GetConfiguredRuntimePath("JoyAltKeys")); if (text == null || !IsCurrentPhysicalPathPressed(text)) { return false; } foreach (string activeNativeAlternateAction in activeNativeAlternateActions) { if (activeModOwnedAlternateActions.Contains(activeNativeAlternateAction)) { string text2 = NormalizeBindingPath(GetConfiguredRuntimePath(activeNativeAlternateAction)); if (text2 != null && !string.Equals(text2, text, StringComparison.OrdinalIgnoreCase) && IsCurrentPhysicalPathPressed(text2)) { return true; } } else if (GetNativeActionState(activeNativeAlternateAction, 0)) { return true; } } return false; } private bool IsAnyNonBuildingAlternateChordHeld() { string text = NormalizeBindingPath(GetConfiguredRuntimePath("JoyAltKeys")); if (text == null || !IsCurrentPhysicalPathPressed(text)) { return false; } foreach (string activeNativeAlternateAction in activeNativeAlternateActions) { if ((GetConflictContexts(activeNativeAlternateAction) & ConflictContext.Building) != ConflictContext.None) { continue; } if (activeModOwnedAlternateActions.Contains(activeNativeAlternateAction)) { string text2 = NormalizeBindingPath(GetConfiguredRuntimePath(activeNativeAlternateAction)); if (text2 != null && !string.Equals(text2, text, StringComparison.OrdinalIgnoreCase) && IsCurrentPhysicalPathPressed(text2)) { return true; } } else if (GetNativeActionState(activeNativeAlternateAction, 0)) { return true; } } return false; } private bool IsGameplayBlockingUiVisible() { if (!movementGuiMenuOpen && !movementGuiSettingsOpen && !movementGuiInventoryOpen && !movementGuiStoreOpen && !movementGuiBuildSelectionOpen && !movementGuiTextInputOpen) { return movementGuiFullMapOpen; } return true; } private bool IsContainerGuiMovementActive() { if (EditorInputBlocked || !IsMoveWhileGuiEnabled()) { return false; } if (!movementGuiInventoryOpen && !movementGuiStoreOpen && !movementGuiBuildSelectionOpen) { return movementGuiFullMapOpen; } return true; } private string GetContainerGuiNavigationSource() { if (movementGuiFullMapOpen) { return "Right Stick"; } if (movementGuiInventoryOpen && !movementGuiSplitDialogOpen) { return "Right Stick"; } return "Both"; } private static string GetPhysicalVectorPathPrefix(string source) { if (string.Equals(source, "D-pad", StringComparison.OrdinalIgnoreCase)) { return "<Gamepad>/dpad/"; } if (string.Equals(source, "Right Stick", StringComparison.OrdinalIgnoreCase)) { return "<Gamepad>/rightStick/"; } if (string.Equals(source, "Left Stick", StringComparison.OrdinalIgnoreCase)) { return "<Gamepad>/leftStick/"; } return null; } private bool TryReadNativeZInputRightStick(out Vector2 value) { //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) //IL_00e2: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: 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) //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: Unknown result type (might be due to invalid IL or missing references) value = Vector2.zero; if (zInputGetJoyRightStickX == null || zInputGetJoyRightStickY == null) { return false; } try { object obj = (zInputGetJoyRightStickX.IsStatic ? null : GetZInputInstance()); object obj2 = (zInputGetJoyRightStickY.IsStatic ? null : GetZInputInstance()); if ((!zInputGetJoyRightStickX.IsStatic && obj == null) || (!zInputGetJoyRightStickY.IsStatic && obj2 == null)) { return false; } float num = Convert.ToSingle(zInputGetJoyRightStickX.Invoke(obj, null)); float num2 = Convert.ToSingle(zInputGetJoyRightStickY.Invoke(obj2, null)); value = new Vector2(num, num2); if (((Vector2)(ref value)).sqrMagnitude < 0.01f) { value = Vector2.zero; } if (((Vector2)(ref value)).sqrMagnitude > 1f) { ((Vector2)(ref value)).Normalize(); } return true; } catch { value = Vector2.zero; return false; } } private bool TryReadPhysicalGamepadVector(string source, out Vector2 value) { //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) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_0104: Unknown result type (might be due to invalid IL or missing references) //IL_0109: Unknown result type (might be due to invalid IL or missing references) //IL_01bd: Unknown result type (might be due to invalid IL or missing references) //IL_01c2: Unknown result type (might be due to invalid IL or missing references) value = Vector2.zero; if (string.Equals(source, "Both", StringComparison.OrdinalIgnoreCase)) { Vector2 value2; bool num = TryReadPhysicalGamepadVector("D-pad", out value2); Vector2 value3; bool flag = TryReadPhysicalGamepadVector("Right Stick", out value3); if (!num && !flag) { return false; } value = value2 + value3; if (((Vector2)(ref value)).sqrMagnitude < 0.01f) { value = Vector2.zero; } if (((Vector2)(ref value)).sqrMagnitude > 1f) { ((Vector2)(ref value)).Normalize(); } return true; } if (string.Equals(source, "Right Stick", StringComparison.OrdinalIgnoreCase) && TryReadNativeZInputRightStick(out value)) { return true; } try { Gamepad current = Gamepad.current; if (current != null) { if (string.Equals(source, "D-pad", StringComparison.OrdinalIgnoreCase)) { value = ((InputControl<Vector2>)(object)current.dpad).ReadValue(); } else if (string.Equals(source, "Right Stick", StringComparison.OrdinalIgnoreCase)) { value = ((InputControl<Vector2>)(object)current.rightStick).ReadValue(); } else { value = ((InputControl<Vector2>)(object)current.leftStick).ReadValue(); } if (((Vector2)(ref value)).sqrMagnitude < 0.01f) { value = Vector2.zero; } if (((Vector2)(ref value)).sqrMagnitude > 1f) { ((Vector2)(ref value)).Normalize(); } return true; } } catch { } string physicalVectorPathPrefix = GetPhysicalVectorPathPrefix(source); if (physicalVectorPathPrefix == null) { return false; } float num2 = (IsCurrentPhysicalPathPressed(physicalVectorPathPrefix + "right") ? 1f : 0f) - (IsCurrentPhysicalPathPressed(physicalVectorPathPrefix + "left") ? 1f : 0f); float num3 = (IsCurrentPhysicalPathPressed(physicalVectorPathPrefix + "up") ? 1f : 0f) - (IsCurrentPhysicalPathPressed(physicalVectorPathPrefix + "down") ? 1f : 0f); value = new Vector2(num2, num3); if (((Vector2)(ref value)).sqrMagnitude > 1f) { ((Vector2)(ref value)).Normalize(); } return true; } private bool TryGetContainerGuiNavigationAxis(MethodBase originalMethod, out float axis) { //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) axis = 0f; bool flag = originalMethod != null && zInputGetJoyLeftStickX != null && originalMethod == zInputGetJoyLeftStickX; bool flag2 = originalMethod != null && zInputGetJoyLeftStickY != null && originalMethod == zInputGetJoyLeftStickY; if (!flag && !flag2) { return false; } if (!IsContainerGuiMovementActive()) { return false; } string containerGuiNavigationSource = GetContainerGuiNavigationSource(); TryReadPhysicalGamepadVector(containerGuiNavigationSource, out var value); axis = (flag ? value.x : value.y); return true; } private bool TryGetContainerGuiNavigationVector(MethodBase originalMethod, out Vector2 vector) { //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) vector = Vector2.zero; if (originalMethod == null || zInputGetJoyLeftStick == null || originalMethod != zInputGetJoyLeftStick) { return false; } if (!IsContainerGuiMovementActive()) { return false; } string containerGuiNavigationSource = GetContainerGuiNavigationSource(); return TryReadPhysicalGamepadVector(containerGuiNavigationSource, out vector); } private static bool TryGetGuiDirectionFromActionName(string action, out string direction) { direction = null; if (string.IsNullOrEmpty(action)) { return false; } if (string.Equals(action, "JoyDPadUp", StringComparison.OrdinalIgnoreCase) || string.Equals(action, "JoyLStickUp", StringComparison.OrdinalIgnoreCase) || string.Equals(action, "JoyRStickUp", StringComparison.OrdinalIgnoreCase)) { direction = "up"; } else if (string.Equals(action, "JoyDPadDown", StringComparison.OrdinalIgnoreCase) || string.Equals(action, "JoyLStickDown", StringComparison.OrdinalIgnoreCase) || string.Equals(action, "JoyRStickDown", StringComparison.OrdinalIgnoreCase)) { direction = "down"; } else if (string.Equals(action, "JoyDPadLeft", StringComparison.OrdinalIgnoreCase) || string.Equals(action, "JoyLStickLeft", StringComparison.OrdinalIgnoreCase) || string.Equals(action, "JoyRStickLeft", StringComparison.OrdinalIgnoreCase)) { direction = "left"; } else if (string.Equals(action, "JoyDPadRight", StringComparison.OrdinalIgnoreCase) || string.Equals(action, "JoyLStickRight", StringComparison.OrdinalIgnoreCase) || string.Equals(action, "JoyRStickRight", StringComparison.OrdinalIgnoreCase)) { direction = "right"; } return direction != null; } private bool TryReadPhysicalPathState(string path, int stateKind, out bool result) { result = false; object controlForPath = GetControlForPath(path); if (controlForPath == null) { return false; } switch (stateKind) { case 1: return TryGetControlBoolProperty(controlForPath, "wasPressedThisFrame", out result); case 2: return TryGetControlBoolProperty(controlForPath, "wasReleasedThisFrame", out result); default: result = IsControlPressed(controlForPath); return true; } } private bool TryGetPhysicalGuiDirectionState(string source, string direction, int stateKind, out bool result) { //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) result = false; string physicalVectorPathPrefix = GetPhysicalVectorPathPrefix(source); if (physicalVectorPathPrefix != null && TryReadPhysicalPathState(physicalVectorPathPrefix + direction, stateKind, out result)) { return true; } if (!TryReadPhysicalGamepadVector(source, out var value)) { return false; } switch (direction) { case "up": result = value.y > 0.5f; break; case "down": result = value.y < -0.5f; break; case "left": result = value.x < -0.5f; break; case "right": result = value.x > 0.5f; break; } return true; } private static string GetNativeGuiDirectionAction(string source, string direction) { if (string.IsNullOrEmpty(direction)) { return null; } string text; switch (direction) { case "up": text = "Up"; break; case "down": text = "Down"; break; case "left": text = "Left"; break; case "right": text = "Right"; break; default: return null; } if (string.Equals(source, "D-pad", StringComparison.OrdinalIgnoreCase)) { return "JoyDPad" + text; } if (string.Equals(source, "Right Stick", StringComparison.OrdinalIgnoreCase)) { return "JoyRStick" + text; } return null; } private bool TryInvokeNativeGuiDirectionDown(string source, string direction, out bool result) { result = false; if (zInputGetButtonDown == null) { return false; } if (string.Equals(source, "Both", StringComparison.OrdinalIgnoreCase)) { bool result2; bool num = TryInvokeNativeGuiDirectionDown("D-pad", direction, out result2); bool result3; bool flag = TryInvokeNativeGuiDirectionDown("Right Stick", direction, out result3); if (!num && !flag) { return false; } result = result2 || result3; return true; } string nativeGuiDirectionAction = GetNativeGuiDirectionAction(source, direction); if (string.IsNullOrEmpty(nativeGuiDirectionAction)) { return false; } try { object obj = (zInputGetButtonDown.IsStatic ? null : GetZInputInstance()); if (!zInputGetButtonDown.IsStatic && obj == null) { return false; } nativeGuiDirectionQueryInProgress = true; result = Convert.ToBoolean(zInputGetButtonDown.Invoke(obj, new object[1] { nativeGuiDirectionAction })); return true; } catch { result = false; return false; } finally { nativeGuiDirectionQueryInProgress = false; } } private bool TryGetContainerGuiDirectionalActionState(string action, int stateKind, out bool result) { result = false; if (!TryGetGuiDirectionFromActionName(action, out var direction)) { return false; } if (!IsContainerGuiMovementActive()) { return false; } string containerGuiNavigationSource = GetContainerGuiNavigationSource(); if (movementGuiFullMapOpen && action.StartsWith("JoyDPad", StringComparison.OrdinalIgnoreCase)) { return false; } if (stateKind == 1 && movementGuiInventoryOpen && !movementGuiSplitDialogOpen && TryInvokeNativeGuiDirectionDown(containerGuiNavigationSource, direction, out result)) { return true; } if (!string.Equals(containerGuiNavigationSource, "Both", StringComparison.OrdinalIgnoreCase)) { return TryGetPhysicalGuiDirectionState(containerGuiNavigationSource, direction, stateKind, out result); } bool result2; bool num = TryGetPhysicalGuiDirectionState("D-pad", direction, stateKind, out result2); bool result3; bool flag = TryGetPhysicalGuiDirectionState("Right Stick", direction, stateKind, out result3); if (!num && !flag) { return false; } if (stateKind == 0) { result = result2 || result3; return true; } TryGetPhysicalGuiDirectionState("D-pad", direction, 0, out var result4); TryGetPhysicalGuiDirectionState("Right Stick", direction, 0, out var result5); if (stateKind == 1) { result = (result2 && (!result5 || result3)) || (result3 && (!result4 || result2)); } else { result = (result2 && !result5) || (result3 && !result4); } return true; } private bool IsBuildUiManualNavigationActive() { if (!EditorInputBlocked && IsMoveWhileGuiEnabled()) { return movementGuiBuildSelectionOpen; } return false; } private static bool BuildUiSelectableOnMovePrefix(object __instance) { Plugin instance = Instance; if ((Object)(object)instance == (Object)null || manualUnityUiMoveDispatchInProgress) { return true; } if (instance.IsBuildUiManualNavigationActive()) { return false; } if (instance.IsSplitDialogManualNavigationActive() && instance.IsSplitDialogMoveTarget(__instance)) { return false; } return true; } private void TryPatchBuildUiSelectableNavigation() { //IL_012f: Unknown result type (might be due to invalid IL or missing references) //IL_013d: Expected O, but got Unknown if (patchedBuildUiSelectableNavigation || harmony == null) { return; } try { buildUiSelectableType = buildUiSelectableType ?? FindType("UnityEngine.UI.Selectable") ?? FindType("Selectable"); buildUiEventSystemType = buildUiEventSystemType ?? FindType("UnityEngine.EventSystems.EventSystem") ?? FindType("EventSystem"); if (buildUiSelectableType == null || buildUiEventSystemType == null) { return; } bool flag = false; string[] array = new string[4] { "UnityEngine.UI.Selectable", "UnityEngine.UI.Slider", "BuildUiPieceButton", "BuildUiTagButton" }; for (int i = 0; i < array.Length; i++) { Type type = FindType(array[i]); if (type == null) { continue; } foreach (MethodInfo item in from m in type.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) where string.Equals(m.Name, "OnMove", StringComparison.Ordinal) && m.GetParameters().Length == 1 select m) { flag = true; if (!patchedBuildUiMoveMethods.Contains(item)) { harmony.Patch((MethodBase)item, new HarmonyMethod(typeof(Plugin), "BuildUiSelectableOnMovePrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); patchedBuildUiMoveMethods.Add(item); } } } if (flag) { buildUiEventSystemCurrentProperty = buildUiEventSystemType.GetProperty("current", BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); buildUiCurrentSelectedGameObjectProperty = buildUiEventSystemType.GetProperty("currentSelectedGameObject", BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); buildUiSelectableFindUp = FindMethodByArityAndName(buildUiSelectableType, "FindSelectableOnUp", 0); buildUiSelectableFindDown = FindMethodByArityAndName(buildUiSelectableType, "FindSelectableOnDown", 0); buildUiSelectableFindLeft = FindMethodByArityAndName(buildUiSelectableType, "FindSelectableOnLeft", 0); buildUiSelectableFindRight = FindMethodByArityAndName(buildUiSelectableType, "FindSelectableOnRight", 0); buildUiSelectableSelect = FindMethodByArityAndName(buildUiSelectableType, "Select", 0); patchedBuildUiSelectableNavigation = buildUiEventSystemCurrentProperty != null && buildUiCurrentSelectedGameObjectProperty != null && buildUiSelectableFindUp != null && buildUiSelectableFindDown != null && buildUiSelectableFindLeft != null && buildUiSelectableFindRight != null && buildUiSelectableSelect != null; if (patchedBuildUiSelectableNavigation && debug != null && debug.Value) { Log.LogInfo((object)"Patched Unity Selectable.OnMove for BuildUi and split-stack automatic GUI Navigation source control."); } } } catch (Exception ex) { if (debug != null && debug.Value) { Log.LogWarning((object)("Could not patch BuildUi Selectable navigation: " + ex.GetBaseException().Message)); } } } private void TryPatchBuildRotateHintSource() { //IL_0233: Unknown result type (might be due to invalid IL or missing references) //IL_023a: Expected O, but got Unknown //IL_0250: Unknown result type (might be due to invalid IL or missing references) //IL_02ab: Unknown result type (might be due to invalid IL or missing references) //IL_0289: Unknown result type (might be due to invalid IL or missing references) //IL_0272: Unknown result type (might be due to invalid IL or missing references) //IL_0290: Expected O, but got Unknown if (harmony == null) { return; } List<MethodInfo> list = new List<MethodInfo>(); Type type = FindType("KeyHints"); if (type != null) { if (cachedKeyHintsRefreshMethod == null) { cachedKeyHintsRefreshMethod = FindMethodByArityAndName(type, "UpdateGamepadHints", 0) ?? FindMethodByArityAndName(type, "UpdateHints", 0); } try { MethodInfo[] methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); foreach (MethodInfo methodInfo in methods) { if (!(methodInfo == null) && !(methodInfo.DeclaringType != type) && !methodInfo.ContainsGenericParameters && (string.Equals(methodInfo.Name, "UpdateHints", StringComparison.Ordinal) || string.Equals(methodInfo.Name, "UpdateGamepadHints", StringComparison.Ordinal))) { list.Add(methodInfo); } } } catch { } } Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly assembly in assemblies) { Type[] array = null; try { array = assembly.GetTypes(); } catch (ReflectionTypeLoadException ex) { array = ex.Types; } catch { continue; } if (array == null) { continue; } Type[] array2 = array; foreach (Type type2 in array2) { if (type2 == null) { continue; } MethodInfo[] methods2; try { methods2 = type2.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); } catch { continue; } MethodInfo[] methods = methods2; foreach (MethodInfo methodInfo2 in methods) { if (!(methodInfo2 == null) && !methodInfo2.ContainsGenericParameters && string.Equals(methodInfo2.Name, "SetGamepadRotateText", StringComparison.Ordinal)) { list.Add(methodInfo2); } } } } int num = 0; foreach (MethodInfo item in list.Distinct()) { if (item == null || patchedKeyHintsRotateSourceMethods.Contains(item)) { continue; } try { bool num2 = type != null && item.DeclaringType == type && (string.Equals(item.Name, "UpdateHints", StringComparison.Ordinal) || string.Equals(item.Name, "UpdateGamepadHints", StringComparison.Ordinal)); HarmonyMethod val = new HarmonyMethod(typeof(Plugin), "BuildRotateHintActionTranspiler", (Type[])null); HarmonyMethod val2 = ((!num2) ? ((HarmonyMethod)null) : new HarmonyMethod(typeof(Plugin), "KeyHintsGenerationPrefix", (Type[])null)); HarmonyMethod val3 = ((!num2) ? (item.IsStatic ? ((HarmonyMethod)null) : new HarmonyMethod(typeof(Plugin), "BuildRotateHintPresentationPostfix", (Type[])null)) : (item.IsStatic ? new HarmonyMethod(typeof(Plugin), "KeyHintsGenerationStaticPostfix", (Type[])null) : new HarmonyMethod(typeof(Plugin), "KeyHintsUpdatePostfix", (Type[])null))); harmony.Patch((MethodBase)item, val2, val3, val, (HarmonyMethod)null, (HarmonyMethod)null); patchedKeyHintsRotateSourceMethods.Add(item); num++; if (debug != null && debug.Value) { Log.LogInfo((object)("Patched build Rotate hint source method: " + ((item.DeclaringType != null) ? item.DeclaringType.FullName : "<unknown>") + "." + item.Name + ".")); } } catch (Exception ex2) { if (debug != null && debug.Value) { Log.LogWarning((object)("Could not patch build Rotate hint source method " + item.Name + ": " + ex2.GetBaseException().Message)); } } } if (num == 0 && debug != null && debug.Value) { Log.LogWarning((object)"No build Rotate hint source methods were patched."); } } private bool MethodCallsBoundKeyString(MethodInfo method) { if (method == null || zInputGetBoundKeyString == null) { return false; } try { byte[] array = method.GetMethodBody()?.GetILAsByteArray(); if (array == null || array.Length < 5) { return false; } Module module = method.Module; for (int i = 0; i <= array.Length - 5; i++) { if (array[i] != 40 && array[i] != 111) { continue; } int metadataToken = array[i + 1] | (array[i + 2] << 8) | (array[i + 3] << 16) | (array[i + 4] << 24); MethodBase methodBase; try { methodBase = module.ResolveMethod(metadataToken); } catch { continue; } if (!(methodBase == null) && !(methodBase.DeclaringType != zInputGetBoundKeyString.DeclaringType) && string.Equals(methodBase.Name, zInputGetBoundKeyString.Name, StringComparison.Ordinal)) { ParameterInfo[] parameters; try { parameters = methodBase.GetParameters(); } catch { continue; } if (parameters.Length == 2 && parameters[0].ParameterType == typeof(string) && parameters[1].ParameterType == typeof(bool)) { return true; } } } } catch { } return false; } private void TryPatchUniversalUnboundHintSources() { //IL_0166: Unknown result type (might be due to invalid IL or missing references) //IL_017b: Unknown result type (might be due to invalid IL or missing references) //IL_0188: Expected O, but got Unknown //IL_0188: Expected O, but got Unknown if (universalUnboundHintDiscoveryComplete || harmony == null || zInputGetBoundKeyString == null) { return; } universalUnboundHintDiscoveryComplete = true; int num = 0; Ass