diff --git a/GlobalUsings.cs b/GlobalUsings.cs index e4c8818..5580321 100644 --- a/GlobalUsings.cs +++ b/GlobalUsings.cs @@ -19,6 +19,7 @@ global using ItemSorter = GClass2772; global using ItemWithLocation = GClass2521; global using SearchableGrid = GClass2516; global using CursorManager = GClass3034; +global using Helmet = GClass2651; // State machine states global using FirearmReadyState = EFT.Player.FirearmController.GClass1619; diff --git a/Patches/AimToggleHoldPatches.cs b/Patches/AimToggleHoldPatches.cs index 10cda72..039d8ed 100644 --- a/Patches/AimToggleHoldPatches.cs +++ b/Patches/AimToggleHoldPatches.cs @@ -74,15 +74,7 @@ public static class AimToggleHoldPatches [PatchPostfix] public static void Postfix(ToggleKeyCombination __instance, EGameKey gameKey, ECommand command, KeyCombination.KeyCombinationState[] ___keyCombinationState_1) { - bool useToggleHold = gameKey switch - { - EGameKey.Tactical => Settings.ToggleOrHoldTactical.Value, - EGameKey.ToggleGoggles => Settings.ToggleOrHoldGoggles.Value, - EGameKey.ToggleHeadLight => Settings.ToggleOrHoldHeadlight.Value, - _ => false - }; - - if (!useToggleHold) + if (!UseToggleHold(gameKey)) { return; } @@ -108,23 +100,44 @@ public static class AimToggleHoldPatches [PatchPostfix] public static void Postfix(KeyCombination __instance) { - bool useToggleHold = __instance.GameKey switch - { - EGameKey.Aim => Settings.ToggleOrHoldAim.Value, - EGameKey.Tactical => Settings.ToggleOrHoldTactical.Value, - EGameKey.ToggleGoggles => Settings.ToggleOrHoldGoggles.Value, - EGameKey.ToggleHeadLight => Settings.ToggleOrHoldHeadlight.Value, - EGameKey.Sprint => Settings.ToggleOrHoldSprint.Value, - _ => false - }; - - if (useToggleHold) + if (UseToggleHold(__instance.GameKey)) { __instance.method_0((KeyCombination.EKeyState)ToggleHoldState.Idle); } } } + private static bool UseToggleHold(EGameKey gameKey) + { + return gameKey switch + { + EGameKey.Aim => Settings.ToggleOrHoldAim.Value, + EGameKey.Tactical => Settings.ToggleOrHoldTactical.Value, + EGameKey.ToggleGoggles => Settings.ToggleOrHoldGoggles.Value, + EGameKey.ToggleHeadLight => Settings.ToggleOrHoldHeadlight.Value, + EGameKey.Sprint => Settings.ToggleOrHoldSprint.Value, + EGameKey.Slot4 => UseToggleHoldQuickBind(EGameKey.Slot4), + EGameKey.Slot5 => UseToggleHoldQuickBind(EGameKey.Slot5), + EGameKey.Slot6 => UseToggleHoldQuickBind(EGameKey.Slot6), + EGameKey.Slot7 => UseToggleHoldQuickBind(EGameKey.Slot7), + EGameKey.Slot8 => UseToggleHoldQuickBind(EGameKey.Slot8), + EGameKey.Slot9 => UseToggleHoldQuickBind(EGameKey.Slot9), + EGameKey.Slot0 => UseToggleHoldQuickBind(EGameKey.Slot0), + _ => false + }; + } + + private static bool UseToggleHoldQuickBind(EGameKey gameKey) + { + return Quickbind.GetType(gameKey) switch + { + Quickbind.ItemType.Tactical => Settings.ToggleOrHoldTactical.Value, + Quickbind.ItemType.Headlight => Settings.ToggleOrHoldHeadlight.Value, + Quickbind.ItemType.NightVision => Settings.ToggleOrHoldGoggles.Value, + _ => false, + }; + } + private static void OnSettingChanged(object sender, EventArgs args) { // Will "save" control settings, running GClass1911.UpdateInput, which will set (or unset) toggle/hold behavior diff --git a/Patches/TacticalBindsPatches.cs b/Patches/TacticalBindsPatches.cs index 7365281..c1ccf1a 100644 --- a/Patches/TacticalBindsPatches.cs +++ b/Patches/TacticalBindsPatches.cs @@ -1,9 +1,13 @@ using Comfort.Common; using EFT; +using EFT.InputSystem; using EFT.InventoryLogic; using HarmonyLib; using SPT.Reflection.Patching; +using System.Collections.Generic; +using System.Linq; using System.Reflection; +using System.Threading.Tasks; using UnityEngine; namespace UIFixes; @@ -15,6 +19,10 @@ public static class TacticalBindsPatches new BindableTacticalPatch().Enable(); new ReachableTacticalPatch().Enable(); new UseTacticalPatch().Enable(); + + new BindTacticalPatch().Enable(); + new UnbindTacticalPatch().Enable(); + new InitQuickBindsPatch().Enable(); } public class BindableTacticalPatch : ModulePatch @@ -72,20 +80,33 @@ public static class TacticalBindsPatches } LightComponent lightComponent = boundItem.GetItemComponent(); - if (lightComponent == null) - { - return true; - } - - // Don't return true past this point; if the default handler tries to use a tactical device, very bad things happen - - if (__instance.HandsController is not Player.FirearmController firearmController || - firearmController.Item != boundItem.GetRootItem()) + if (lightComponent != null) { + ToggleLight(__instance, boundItem, lightComponent); callback(null); return false; } + NightVisionComponent nightVisionComponent = boundItem.GetItemComponent(); + if (nightVisionComponent != null) + { + Item rootItem = boundItem.GetRootItemNotEquipment(); + if (rootItem is Helmet helmet && + __instance.Inventory.Equipment.GetSlot(EquipmentSlot.Headwear).ContainedItem == helmet) + { + __instance.InventoryControllerClass.TryRunNetworkTransaction( + nightVisionComponent.Togglable.Set(!nightVisionComponent.Togglable.On, true, false)); + } + + callback(null); + return false; + } + + return true; + } + + private static void ToggleLight(Player player, Item boundItem, LightComponent lightComponent) + { FirearmLightStateStruct lightState = new() { Id = lightComponent.Item.Id, @@ -102,24 +123,96 @@ public static class TacticalBindsPatches lightState.IsActive = !lightState.IsActive; } - firearmController.SetLightsState([lightState], false); + Item rootItem = boundItem.GetRootItemNotEquipment(); + if (rootItem is Weapon weapon && + player.HandsController is Player.FirearmController firearmController && + firearmController.Item == weapon) + { + firearmController.SetLightsState([lightState], false); + } - callback(null); - return false; + if (rootItem is Helmet helmet && + player.Inventory.Equipment.GetSlot(EquipmentSlot.Headwear).ContainedItem == helmet) + { + lightComponent.SetLightState(lightState); + player.SendHeadlightsPacket(false); + player.SwitchHeadLightsAnimation(); + } + } + } + + public class InitQuickBindsPatch : ModulePatch + { + protected override MethodBase GetTargetMethod() + { + return AccessTools.Method(typeof(MainMenuController), nameof(MainMenuController.method_5)); + } + + [PatchPostfix] + public static async void Postfix(MainMenuController __instance, Task __result) + { + await __result; + + for (EBoundItem index = EBoundItem.Item4; index <= EBoundItem.Item10; index++) + { + if (__instance.InventoryController.Inventory.FastAccess.BoundItems.ContainsKey(index)) + { + UpdateQuickbindType(__instance.InventoryController.Inventory.FastAccess.BoundItems[index], index); + } + } + + // Will "save" control settings, running GClass1911.UpdateInput, which will set (or unset) toggle/hold behavior + Singleton.Instance.Control.Controller.method_3(); + } + } + + public class BindTacticalPatch : ModulePatch + { + protected override MethodBase GetTargetMethod() + { + return AccessTools.Method(typeof(GClass2818), nameof(GClass2818.Run)); + } + + [PatchPostfix] + public static void Postfix(InventoryControllerClass controller, Item item, EBoundItem index) + { + UpdateQuickbindType(item, index); + + // Will "save" control settings, running GClass1911.UpdateInput, which will set (or unset) toggle/hold behavior + Singleton.Instance.Control.Controller.method_3(); + } + } + + public class UnbindTacticalPatch : ModulePatch + { + protected override MethodBase GetTargetMethod() + { + return AccessTools.Method(typeof(GClass2819), nameof(GClass2819.Run)); + } + + [PatchPostfix] + public static void Postfix(InventoryControllerClass controller, Item item, EBoundItem index) + { + Quickbind.SetType(index, Quickbind.ItemType.Other); + + // Will "save" control settings, running GClass1911.UpdateInput, which will set (or unset) toggle/hold behavior + Singleton.Instance.Control.Controller.method_3(); } } private static bool IsEquippedTacticalDevice(InventoryControllerClass inventoryController, Item item) { LightComponent lightComponent = item.GetItemComponent(); - if (lightComponent == null) + NightVisionComponent nightVisionComponent = item.GetItemComponent(); + if (lightComponent == null && nightVisionComponent == null) { return false; } - if (item.GetRootItem() is Weapon weapon) + Item rootItem = item.GetRootItemNotEquipment(); + if (rootItem is Weapon || rootItem is Helmet) { - return inventoryController.Inventory.Equipment.Contains(weapon); + return inventoryController.Inventory.Equipment.Contains(rootItem); } return false; @@ -135,4 +228,75 @@ public static class TacticalBindsPatches _ => false, }; } + + private static void UpdateQuickbindType(Item item, EBoundItem index) + { + if (item == null) + { + Quickbind.SetType(index, Quickbind.ItemType.Other); + return; + } + + LightComponent lightComponent = item.GetItemComponent(); + if (lightComponent != null) + { + Item rootItem = item.GetRootItemNotEquipment(); + if (rootItem is Weapon) + { + Quickbind.SetType(index, Quickbind.ItemType.Tactical); + return; + } + + if (rootItem is Helmet) + { + Quickbind.SetType(index, Quickbind.ItemType.Headlight); + return; + } + } + + NightVisionComponent nvComponent = item.GetItemComponent(); + if (nvComponent != null) + { + Quickbind.SetType(index, Quickbind.ItemType.NightVision); + return; + } + + Quickbind.SetType(index, Quickbind.ItemType.Other); + } + + private static Item GetRootItemNotEquipment(this Item item) + { + return item.GetAllParentItemsAndSelf(true).LastOrDefault(i => i is not EquipmentClass) ?? item; + } +} + +public static class Quickbind +{ + public enum ItemType + { + Other, + Tactical, + Headlight, + NightVision + } + + private static readonly Dictionary TacticalQuickbinds = new() + { + { EBoundItem.Item4, ItemType.Other }, + { EBoundItem.Item5, ItemType.Other }, + { EBoundItem.Item6, ItemType.Other }, + { EBoundItem.Item7, ItemType.Other }, + { EBoundItem.Item8, ItemType.Other }, + { EBoundItem.Item9, ItemType.Other }, + { EBoundItem.Item10, ItemType.Other }, + }; + + public static ItemType GetType(EBoundItem index) => TacticalQuickbinds[index]; + public static void SetType(EBoundItem index, ItemType type) => TacticalQuickbinds[index] = type; + + public static ItemType GetType(EGameKey gameKey) + { + int offset = gameKey - EGameKey.Slot4; + return GetType(EBoundItem.Item4 + offset); + } } \ No newline at end of file