diff --git a/GlobalUsings.cs b/GlobalUsings.cs index e4bc427..fd44de7 100644 --- a/GlobalUsings.cs +++ b/GlobalUsings.cs @@ -12,9 +12,23 @@ global using Stackable = GClass2751; global using RagfairSearch = GClass3219; global using CurrencyInfo = GClass2531; global using Scheme = GClass1939; +global using ItemFilterExtensions = GClass2524; +global using QuickBindCommandMap = GClass3032; +global using DiscardResult = GClass2799; +global using BindOperation = GClass2818; + +// State machine states +global using FirearmReadyState = EFT.Player.FirearmController.GClass1619; +global using FirearmAddingModState = EFT.Player.FirearmController.Class1039; + +// JSON +global using LocationJsonParser = GClass1496; +global using JsonItem = GClass1198; // Errors global using DestroyError = GClass3344; +global using GridNoRoomError = StashGridClass.GClass3310; +global using GridSpaceTakenError = StashGridClass.GClass3311; global using GridModificationsUnavailableError = StashGridClass.GClass3315; global using NoRoomError = GClass3316; global using NoPossibleActionsError = GClass3317; diff --git a/Patches/AimToggleHoldPatches.cs b/Patches/AimToggleHoldPatches.cs index 9d6d6b5..d8cf42b 100644 --- a/Patches/AimToggleHoldPatches.cs +++ b/Patches/AimToggleHoldPatches.cs @@ -3,6 +3,7 @@ using EFT.InputSystem; using HarmonyLib; using SPT.Reflection.Patching; using System.Collections.Generic; +using System.Linq; using System.Reflection; namespace UIFixes @@ -28,7 +29,7 @@ namespace UIFixes protected override MethodBase GetTargetMethod() { StateMachineArray = AccessTools.Field(typeof(KeyCombination), "keyCombinationState_1"); - return AccessTools.Constructor(typeof(ToggleKeyCombination), [typeof(EGameKey), typeof(ECommand), typeof(ECommand), typeof(int)]); + return AccessTools.GetDeclaredConstructors(typeof(ToggleKeyCombination)).Single(); } [PatchPostfix] diff --git a/Patches/ContextMenuPatches.cs b/Patches/ContextMenuPatches.cs index a9abe1d..4f3b5b5 100644 --- a/Patches/ContextMenuPatches.cs +++ b/Patches/ContextMenuPatches.cs @@ -4,7 +4,6 @@ using EFT.UI; using EFT.UI.DragAndDrop; using HarmonyLib; using SPT.Reflection.Patching; -using SPT.Reflection.Utils; using System; using System.Collections.Generic; using System.Linq; @@ -17,56 +16,12 @@ namespace UIFixes { public static class ContextMenuPatches { - private static Type InventoryRootInteractionsType; - private static Type TradingRootInteractionsType; - private static FieldInfo TradingRootInteractionsItemField; - private static InsuranceInteractions CurrentInsuranceInteractions = null; private static RepairInteractions CurrentRepairInteractions = null; private static string CreatedButtonInteractionId = null; - private static readonly HashSet TradingRootInteractions = - [ - EItemInfoButton.Inspect, - EItemInfoButton.Uninstall, - EItemInfoButton.Examine, - EItemInfoButton.Open, - EItemInfoButton.Insure, - EItemInfoButton.Repair, - EItemInfoButton.Modding, - EItemInfoButton.EditBuild, - EItemInfoButton.FilterSearch, - EItemInfoButton.LinkedSearch, - EItemInfoButton.NeededSearch, - EItemInfoButton.Tag, - EItemInfoButton.ResetTag, - EItemInfoButton.TurnOn, - EItemInfoButton.TurnOff, - EItemInfoButton.Fold, - EItemInfoButton.Unfold, - EItemInfoButton.Disassemble, - EItemInfoButton.Discard - ]; - public static void Enable() { - // The context menus in the inventory and the trading screen inventory are *completely different code* - InventoryRootInteractionsType = PatchConstants.EftTypes.Single(t => t.GetField("HIDEOUT_WEAPON_MODIFICATION_REQUIRED") != null); // GClass3045 - - // GClass3054 - this is nuts to find, have to inspect a static enum array - TradingRootInteractionsType = PatchConstants.EftTypes.Single(t => - { - var enumerableField = t.GetField("ienumerable_2", BindingFlags.NonPublic | BindingFlags.Static); - if (enumerableField != null) - { - var enumerable = (IEnumerable)enumerableField.GetValue(null); - return TradingRootInteractions.SetEquals(enumerable); - } - - return false; - }); - TradingRootInteractionsItemField = AccessTools.Field(TradingRootInteractionsType, "item_0"); - new ContextMenuNamesPatch().Enable(); new PositionSubMenuPatch().Enable(); new PositionInsuranceSubMenuPatch().Enable(); @@ -170,7 +125,7 @@ namespace UIFixes { protected override MethodBase GetTargetMethod() { - return AccessTools.Method(InventoryRootInteractionsType, "get_SubInteractions"); + return AccessTools.Method(R.InventoryInteractions.Type, "get_SubInteractions"); } [PatchPostfix] @@ -186,7 +141,7 @@ namespace UIFixes protected override MethodBase GetTargetMethod() { - return AccessTools.Method(InventoryRootInteractionsType, "CreateSubInteractions"); + return AccessTools.Method(R.InventoryInteractions.Type, "CreateSubInteractions"); } [PatchPrefix] @@ -236,7 +191,7 @@ namespace UIFixes { protected override MethodBase GetTargetMethod() { - return AccessTools.Method(TradingRootInteractionsType, "get_SubInteractions"); + return AccessTools.Method(R.TradingInteractions.Type, "get_SubInteractions"); } [PatchPostfix] @@ -252,7 +207,7 @@ namespace UIFixes protected override MethodBase GetTargetMethod() { - return AccessTools.Method(TradingRootInteractionsType, "CreateSubInteractions"); + return AccessTools.Method(R.TradingInteractions.Type, "CreateSubInteractions"); } [PatchPrefix] @@ -261,12 +216,14 @@ namespace UIFixes // Clear this, since something else should be active (even a different mouseover of the insurance button) LoadingInsuranceActions = false; + var wrappedInstance = new R.TradingInteractions(__instance); + if (parentInteraction == EItemInfoButton.Insure) { int playerRubles = GetPlayerRubles(___itemUiContext_0); - // CreateSubInteractions is only on the base class here, which doesn't have an Item. But __instance is actually a GClass3032 - Item item = (Item)TradingRootInteractionsItemField.GetValue(__instance); + // CreateSubInteractions is only on the base class here, which doesn't have an Item. But __instance is actually a GClass3054 + Item item = wrappedInstance.Item; CurrentInsuranceInteractions = new(item, ___itemUiContext_0, playerRubles); CurrentInsuranceInteractions = MultiSelect.Active ? @@ -292,8 +249,8 @@ namespace UIFixes { int playerRubles = GetPlayerRubles(___itemUiContext_0); - // CreateSubInteractions is only on the base class here, which doesn't have an Item. But __instance is actually a GClass3032 - Item item = (Item)TradingRootInteractionsItemField.GetValue(__instance); + // CreateSubInteractions is only on the base class here, which doesn't have an Item. But __instance is actually a GClass3054 + Item item = wrappedInstance.Item; CurrentRepairInteractions = new(item, ___itemUiContext_0, playerRubles); subInteractionsWrapper.SetSubInteractions(CurrentRepairInteractions); @@ -469,7 +426,7 @@ namespace UIFixes { protected override MethodBase GetTargetMethod() { - return AccessTools.Method(InventoryRootInteractionsType, "CreateSubInteractions"); + return AccessTools.Method(R.InventoryInteractions.Type, "CreateSubInteractions"); } // Existing logic tries to place it on the right, moving to the left if necessary. They didn't do it correctly, so it always goes on the left. diff --git a/Patches/LoadMultipleMagazinesPatches.cs b/Patches/LoadMultipleMagazinesPatches.cs index 3512103..ce40aab 100644 --- a/Patches/LoadMultipleMagazinesPatches.cs +++ b/Patches/LoadMultipleMagazinesPatches.cs @@ -2,6 +2,8 @@ using EFT.UI; using HarmonyLib; using SPT.Reflection.Patching; +using SPT.Reflection.Utils; +using System; using System.Linq; using System.Reflection; using System.Threading.Tasks; @@ -53,7 +55,7 @@ namespace UIFixes { protected override MethodBase GetTargetMethod() { - return AccessTools.Method(typeof(GClass2524), nameof(GClass2524.CheckItemFilter)); + return AccessTools.Method(typeof(ItemFilterExtensions), nameof(ItemFilterExtensions.CheckItemFilter)); } [PatchPrefix] @@ -72,7 +74,8 @@ namespace UIFixes { protected override MethodBase GetTargetMethod() { - return AccessTools.Method(typeof(GClass3065), nameof(GClass3065.method_6)); + Type type = PatchConstants.EftTypes.Single(t => t.GetNestedType("EMagInteraction") != null); + return AccessTools.Method(type, "method_6"); } [PatchPrefix] @@ -92,7 +95,8 @@ namespace UIFixes { protected override MethodBase GetTargetMethod() { - return AccessTools.Method(typeof(GClass3066), nameof(GClass3066.method_7)); + Type type = PatchConstants.EftTypes.Single(t => t.GetNestedType("EMagPresetInteraction") != null); + return AccessTools.Method(type, "method_7"); } [PatchPrefix] @@ -119,7 +123,8 @@ namespace UIFixes { protected override MethodBase GetTargetMethod() { - return AccessTools.Method(typeof(GClass3066), nameof(GClass3066.method_6)); + Type type = PatchConstants.EftTypes.Single(t => t.GetNestedType("EMagPresetInteraction") != null); + return AccessTools.Method(type, "method_6"); } [PatchPrefix] diff --git a/Patches/PutToolsBackPatch.cs b/Patches/PutToolsBackPatch.cs index 02ee686..ccbc0c8 100644 --- a/Patches/PutToolsBackPatch.cs +++ b/Patches/PutToolsBackPatch.cs @@ -13,14 +13,14 @@ namespace UIFixes { protected override MethodBase GetTargetMethod() { - return AccessTools.Method(typeof(GClass1855), nameof(GClass1855.method_9)); + return AccessTools.Method(R.ItemReceiver.Type, "method_9"); // GClass1855 } // The patched method can't handle new items that aren't in stash root. // Find items that are in subcontainers and handle them first - the patched method will ignore items that have a CurrentAddress // This is a subset of the original method - doesn't handle slots, equipment containers, etc. [PatchPrefix] - public static void Prefix(ref GClass1198[] newItems, Profile ___profile_0, ItemFactory ___itemFactory, GClass2780 ___gclass2780_0) + public static void Prefix(object __instance, ref JsonItem[] newItems, Profile ___profile_0, ItemFactory ___itemFactory) { Inventory inventory = ___profile_0.Inventory; StashClass stash = inventory.Stash; @@ -40,10 +40,12 @@ namespace UIFixes List stashItems = stash.GetNotMergedItems().ToList(); - ItemFactory.GStruct135 tree = ___itemFactory.FlatItemsToTree(unhandledItems.ToArray(), true, null); + InventoryControllerClass inventoryController = new R.ItemReceiver(__instance).InventoryController; + + var tree = ___itemFactory.FlatItemsToTree(unhandledItems.ToArray(), true, null); foreach (Item item in tree.Items.Values.Where(i => i.CurrentAddress == null)) { - GClass1198 newItem = unhandledItems.First(i => i._id == item.Id); + var newItem = unhandledItems.First(i => i._id == item.Id); if (newItem.parentId != null || newItem.slotId != null) { // Assuming here that unhandled items are trying to go into containers in the stash - find that container @@ -52,14 +54,14 @@ namespace UIFixes { if (containerCollection.GetContainer(newItem.slotId) is StashGridClass grid) { - LocationInGrid location = GClass1496.CreateItemLocation(newItem.location); + LocationInGrid location = LocationJsonParser.CreateItemLocation(newItem.location); ItemAddress itemAddress = new GridItemAddress(grid, location); - GStruct414 operation = InteractionsHandlerClass.Add(item, itemAddress, ___gclass2780_0, false); + var operation = InteractionsHandlerClass.Add(item, itemAddress, inventoryController, false); if (operation.Succeeded) { - operation.Value.RaiseEvents(___gclass2780_0, CommandStatus.Begin); - operation.Value.RaiseEvents(___gclass2780_0, CommandStatus.Succeed); + operation.Value.RaiseEvents(inventoryController, CommandStatus.Begin); + operation.Value.RaiseEvents(inventoryController, CommandStatus.Succeed); } } } diff --git a/Patches/QuickAccessPanelPatches.cs b/Patches/QuickAccessPanelPatches.cs index e5ade8e..4276637 100644 --- a/Patches/QuickAccessPanelPatches.cs +++ b/Patches/QuickAccessPanelPatches.cs @@ -92,7 +92,7 @@ namespace UIFixes [PatchPrefix] public static void Prefix(ECommand command) { - FixVisibilityPatch.Ignorable = GClass3032.SlotBySelectCommandDictionary.ContainsKey(command); + FixVisibilityPatch.Ignorable = QuickBindCommandMap.SlotBySelectCommandDictionary.ContainsKey(command); } [PatchPostfix] diff --git a/Patches/RebindGrenadesPatch.cs b/Patches/RebindGrenadesPatch.cs index 7262243..fdd2c58 100644 --- a/Patches/RebindGrenadesPatch.cs +++ b/Patches/RebindGrenadesPatch.cs @@ -21,7 +21,7 @@ namespace UIFixes // This is a grenade specific event emitter that has all the info needed to do this [PatchPostfix] - public static void Postfix(CommandStatus status, GClass2799 ___DiscardResult) + public static void Postfix(CommandStatus status, DiscardResult ___DiscardResult) { if (status != CommandStatus.Succeed) { @@ -40,7 +40,7 @@ namespace UIFixes var nextGrenade = matchingGrenades.FirstOrDefault(g => controller.IsAtBindablePlace(g)); if (nextGrenade != null) { - controller.TryRunNetworkTransaction(GClass2818.Run(controller, nextGrenade, index, true), null); + controller.TryRunNetworkTransaction(BindOperation.Run(controller, nextGrenade, index, true), null); } } } diff --git a/Patches/SwapPatches.cs b/Patches/SwapPatches.cs index 6f8f1bc..c114639 100644 --- a/Patches/SwapPatches.cs +++ b/Patches/SwapPatches.cs @@ -532,7 +532,7 @@ namespace UIFixes [PatchPrefix] public static bool Prefix(SimpleTooltip __instance, InventoryError error) { - if (error is StashGridClass.GClass3310 || error is StashGridClass.GClass3311) + if (error is GridNoRoomError || error is GridSpaceTakenError) { __instance.Close(); return false; @@ -546,15 +546,15 @@ namespace UIFixes { protected override MethodBase GetTargetMethod() { - return AccessTools.Method(typeof(Player.FirearmController.Class1039), nameof(Player.FirearmController.Class1039.OnModChanged)); + return AccessTools.Method(typeof(FirearmAddingModState), nameof(FirearmAddingModState.OnModChanged)); } - // The firearm state machine state Class1015 is the "adding mod" state - // Unpatched, it fires off the success callback before returning to ready state (GClass1608) + // This is the state machine's "adding mod" state + // Unpatched, it fires off the success callback before returning to ready state // Patched to not be that stupid [PatchPrefix] public static bool Prefix( - Player.FirearmController.Class1039 __instance, + FirearmAddingModState __instance, bool ___bool_0, FirearmsAnimator ___firearmsAnimator_0, Item ___item_0, @@ -577,7 +577,7 @@ namespace UIFixes __instance.State = Player.EOperationState.Finished; // Begin change (moved from bottom) - ___firearmController_0.InitiateOperation().Start(null); + ___firearmController_0.InitiateOperation().Start(null); __instance.method_5(gameObject); // End change diff --git a/Patches/UnloadAmmoPatches.cs b/Patches/UnloadAmmoPatches.cs index afd5ce9..12f3134 100644 --- a/Patches/UnloadAmmoPatches.cs +++ b/Patches/UnloadAmmoPatches.cs @@ -1,9 +1,9 @@ -using SPT.Reflection.Patching; -using Comfort.Common; -using EFT.HealthSystem; +using Comfort.Common; using EFT.InventoryLogic; using EFT.UI; using HarmonyLib; +using SPT.Reflection.Patching; +using System; using System.Collections.Generic; using System.Linq; using System.Reflection; @@ -25,7 +25,7 @@ namespace UIFixes { protected override MethodBase GetTargetMethod() { - return AccessTools.DeclaredProperty(typeof(GClass3054), nameof(GClass3054.AvailableInteractions)).GetMethod; + return AccessTools.DeclaredProperty(R.TradingInteractions.Type, "AvailableInteractions").GetMethod; } [PatchPostfix] @@ -41,7 +41,7 @@ namespace UIFixes { protected override MethodBase GetTargetMethod() { - return AccessTools.DeclaredProperty(typeof(GClass3057), nameof(GClass3057.AvailableInteractions)).GetMethod; + return AccessTools.DeclaredProperty(R.TransferInteractions.Type, "AvailableInteractions").GetMethod; } [PatchPostfix] @@ -88,11 +88,12 @@ namespace UIFixes { protected override MethodBase GetTargetMethod() { - return AccessTools.Constructor(typeof(ScavengerInventoryScreen.GClass3156), [typeof(GClass2780), typeof(GClass2780), typeof(IHealthController), typeof(StashClass), typeof(ISession)]); + Type type = typeof(ScavengerInventoryScreen).GetNestedTypes().Single(t => t.GetField("ScavController") != null); // ScavengerInventoryScreen.GClass3156 + return AccessTools.GetDeclaredConstructors(type).Single(); } [PatchPrefix] - public static void Prefix(GClass2780 scavController) + public static void Prefix(InventoryContainerClass scavController) { scavController.Inventory.Stash = null; } diff --git a/R.cs b/R.cs index dbc3691..9c5b12e 100644 --- a/R.cs +++ b/R.cs @@ -59,6 +59,10 @@ namespace UIFixes ContextMenuHelper.InitTypes(); RagfairNewOfferItemView.InitTypes(); TradingTableGridView.InitTypes(); + ItemReceiver.InitTypes(); + InventoryInteractions.InitTypes(); + TradingInteractions.InitTypes(); + TransferInteractions.InitTypes(); } public abstract class Wrapper(object value) @@ -142,7 +146,7 @@ namespace UIFixes { Type = typeof(EFT.Hideout.ProductionPanel); SearchInputFieldField = AccessTools.Field(Type, "_searchInputField"); - ProductionBuildsField = AccessTools.GetDeclaredFields(Type).Single(t => t.FieldType.GetElementType() == typeof(ProductionBuildAbstractClass)); + ProductionBuildsField = AccessTools.GetDeclaredFields(Type).Single(f => f.FieldType.GetElementType() == typeof(ProductionBuildAbstractClass)); } public ValidationInputField SeachInputField { get { return (ValidationInputField)SearchInputFieldField.GetValue(Value); } } @@ -703,6 +707,116 @@ namespace UIFixes public TraderAssortmentControllerClass TraderAssortmentController { get { return (TraderAssortmentControllerClass)TraderAssortmentControllerField.GetValue(Value); } } } + + public class ItemReceiver(object value) : Wrapper(value) // GClass1855 + { + public static Type Type { get; private set; } + private static FieldInfo InventoryControllerField; + + public static void InitTypes() + { + Type = PatchConstants.EftTypes.Single(t => + { + FieldInfo field = t.GetField("localQuestControllerClass", BindingFlags.NonPublic | BindingFlags.Instance); + return field != null && field.IsInitOnly; + }); + InventoryControllerField = Type.GetFields(BindingFlags.NonPublic | BindingFlags.Instance).Single(f => typeof(InventoryControllerClass).IsAssignableFrom(f.FieldType)); + } + + public InventoryControllerClass InventoryController { get { return (InventoryControllerClass)InventoryControllerField.GetValue(Value); } } + } + + public class InventoryInteractions(object value) : Wrapper(value) + { + public static Type Type { get; private set; } + + public static void InitTypes() + { + Type = PatchConstants.EftTypes.Single(t => t.GetField("HIDEOUT_WEAPON_MODIFICATION_REQUIRED") != null); // GClass3045 + } + } + + public class TradingInteractions(object value) : Wrapper(value) + { + public static Type Type { get; private set; } + private static FieldInfo ItemField; + + private static readonly HashSet Interactions = + [ + EItemInfoButton.Inspect, + EItemInfoButton.Uninstall, + EItemInfoButton.Examine, + EItemInfoButton.Open, + EItemInfoButton.Insure, + EItemInfoButton.Repair, + EItemInfoButton.Modding, + EItemInfoButton.EditBuild, + EItemInfoButton.FilterSearch, + EItemInfoButton.LinkedSearch, + EItemInfoButton.NeededSearch, + EItemInfoButton.Tag, + EItemInfoButton.ResetTag, + EItemInfoButton.TurnOn, + EItemInfoButton.TurnOff, + EItemInfoButton.Fold, + EItemInfoButton.Unfold, + EItemInfoButton.Disassemble, + EItemInfoButton.Discard + ]; + + public static void InitTypes() + { + // GClass3054 + Type = PatchConstants.EftTypes.Single(t => + { + var enumerableField = t.GetField("ienumerable_2", BindingFlags.NonPublic | BindingFlags.Static); + if (enumerableField != null) + { + var enumerable = (IEnumerable)enumerableField.GetValue(null); + return Interactions.SetEquals(enumerable); + } + + return false; + }); + ItemField = AccessTools.Field(Type, "item_0"); + } + + public Item Item { get { return (Item) ItemField.GetValue(Value); } } + } + + public class TransferInteractions(object value) : Wrapper(value) + { + public static Type Type { get; private set; } + + private static readonly HashSet Interactions = + [ + EItemInfoButton.Inspect, + EItemInfoButton.Uninstall, + EItemInfoButton.Examine, + EItemInfoButton.Equip, + EItemInfoButton.Open, + EItemInfoButton.Fold, + EItemInfoButton.Unfold, + EItemInfoButton.Disassemble, + EItemInfoButton.Discard + ]; + + public static void InitTypes() + { + // GClass3057 + Type = PatchConstants.EftTypes.Single(t => + { + var enumerableField = t.GetField("ienumerable_2", BindingFlags.NonPublic | BindingFlags.Static); + if (enumerableField != null) + { + var enumerable = (IEnumerable)enumerableField.GetValue(null); + return Interactions.SetEquals(enumerable); + } + + return false; + }); + } + } } public static class RExtentensions