diff --git a/Patches/ConfirmationDialogKeysPatch.cs b/Patches/ConfirmationDialogKeysPatch.cs index 09a3f4b..b4dc2b5 100644 --- a/Patches/ConfirmationDialogKeysPatch.cs +++ b/Patches/ConfirmationDialogKeysPatch.cs @@ -1,7 +1,5 @@ using Aki.Reflection.Patching; -using EFT.UI; using HarmonyLib; -using System; using System.Reflection; using UnityEngine; @@ -9,19 +7,16 @@ namespace UIFixes { public class ConfirmationDialogKeysPatch : ModulePatch { - private static MethodInfo AcceptMethod; - protected override MethodBase GetTargetMethod() { - Type dialogWindowType = typeof(MessageWindow).BaseType; - AcceptMethod = AccessTools.Method(dialogWindowType, "Accept"); - - return AccessTools.Method(dialogWindowType, "Update"); + return AccessTools.Method(R.DialogWindow.Type, "Update"); } [PatchPostfix] public static void Postfix(object __instance, bool ___bool_0) { + var instance = new R.DialogWindow(__instance); + if (!___bool_0) { return; @@ -29,7 +24,7 @@ namespace UIFixes if (Input.GetKeyDown(KeyCode.Return) || Input.GetKeyDown(KeyCode.KeypadEnter) || Input.GetKeyDown(KeyCode.Space)) { - AcceptMethod.Invoke(__instance, []); + instance.Accept(); return; } } diff --git a/Patches/FixMailRecieveAllPatch.cs b/Patches/FixMailRecieveAllPatch.cs index 0d946e5..23dab60 100644 --- a/Patches/FixMailRecieveAllPatch.cs +++ b/Patches/FixMailRecieveAllPatch.cs @@ -1,7 +1,6 @@ using Aki.Reflection.Patching; using EFT.UI.Chat; using HarmonyLib; -using System; using System.Reflection; namespace UIFixes diff --git a/Patches/FixWeaponBindsDisplayPatch.cs b/Patches/FixWeaponBindsDisplayPatch.cs index 9520db0..a61668a 100644 --- a/Patches/FixWeaponBindsDisplayPatch.cs +++ b/Patches/FixWeaponBindsDisplayPatch.cs @@ -1,39 +1,32 @@ using Aki.Reflection.Patching; -using Aki.Reflection.Utils; using EFT.InputSystem; using EFT.InventoryLogic; using HarmonyLib; -using System; -using System.Linq; using System.Reflection; namespace UIFixes { public class FixWeaponBindsDisplayPatch : ModulePatch { - private static Type ControlSettingsClass; - private static MethodInfo GetKeyNameMethod; - protected override MethodBase GetTargetMethod() { - ControlSettingsClass = PatchConstants.EftTypes.Single(x => x.GetMethod("GetBoundItemNames") != null); // GClass960 - GetKeyNameMethod = AccessTools.Method(ControlSettingsClass, "GetKeyName"); - return AccessTools.Method(ControlSettingsClass, "GetBoundItemNames"); + return AccessTools.Method(R.ControlSettings.Type, "GetBoundItemNames"); } [PatchPostfix] public static void Postfix(object __instance, EBoundItem boundItem, ref string __result) { + var instance = new R.ControlSettings(__instance); switch(boundItem) { case EBoundItem.Item1: - __result = GetKeyNameMethod.Invoke(__instance, [EGameKey.SecondaryWeapon]) as string; + __result = instance.GetKeyName(EGameKey.SecondaryWeapon); break; case EBoundItem.Item2: - __result = GetKeyNameMethod.Invoke(__instance, [EGameKey.PrimaryWeaponFirst]) as string; + __result = instance.GetKeyName(EGameKey.PrimaryWeaponFirst); break; case EBoundItem.Item3: - __result = GetKeyNameMethod.Invoke(__instance, [EGameKey.PrimaryWeaponSecond]) as string; + __result = instance.GetKeyName(EGameKey.PrimaryWeaponSecond); break; } } diff --git a/Patches/HideoutSearchPatches.cs b/Patches/HideoutSearchPatches.cs index 81b7717..a5275e6 100644 --- a/Patches/HideoutSearchPatches.cs +++ b/Patches/HideoutSearchPatches.cs @@ -12,16 +12,10 @@ namespace UIFixes { public class HideoutSearchPatches { - private static FieldInfo ProductionPanelSearch; - private static FieldInfo SubstrateContentLayoutField; - private static readonly Dictionary LastSearches = []; public static void Enable() { - ProductionPanelSearch = AccessTools.Field(typeof(ProductionPanel), "_searchInputField"); - SubstrateContentLayoutField = AccessTools.Field(typeof(AreaScreenSubstrate), "_contentLayout"); - new FixHideoutSearchPatch().Enable(); new RestoreHideoutSearchPatch().Enable(); new SaveHideoutSearchPatch().Enable(); @@ -35,20 +29,17 @@ namespace UIFixes { protected override MethodBase GetTargetMethod() { - Type type = typeof(ProductionPanel).GetNestedTypes().First(t => - { - MethodInfo method = t.GetMethod("method_6"); - return method != null && method.GetParameters().Length == 2 && method.GetParameters()[1].ParameterType == typeof(ProduceView); - }); - - return AccessTools.Method(type, "method_6"); + return AccessTools.Method(R.ProductionPanelShowSubclass.Type, "method_6"); } [PatchPostfix] - public static void Postfix(ProductionPanel.Class1631 __instance, GClass1923 scheme, ProduceView view) + public static void Postfix(object __instance, GClass1923 scheme, ProduceView view) { - var searchField = ProductionPanelSearch.GetValue(__instance.productionPanel_0) as ValidationInputField; - if (searchField.text.Length > 0 && scheme.endProduct.LocalizedName().IndexOf(searchField.text, StringComparison.InvariantCultureIgnoreCase) < 0) + var instance = new R.ProductionPanelShowSubclass(__instance); + var productScheme = new R.Scheme(scheme); + + ValidationInputField searchField = new R.ProductionPanel(instance.ProductionPanel).SeachInputField; + if (searchField.text.Length > 0 && productScheme.EndProduct.LocalizedName().IndexOf(searchField.text, StringComparison.InvariantCultureIgnoreCase) < 0) { view.GameObject.SetActive(false); } @@ -58,7 +49,6 @@ namespace UIFixes // Populate the search box, and force the window to render public class RestoreHideoutSearchPatch : ModulePatch { - protected override MethodBase GetTargetMethod() { return AccessTools.Method(typeof(ProductionPanel), nameof(ProductionPanel.ShowContents)); @@ -80,7 +70,7 @@ namespace UIFixes if (__instance.method_4().Count() > 2) { AreaScreenSubstrate areaScreenSubstrate = __instance.GetComponentInParent(); - LayoutElement layoutElement = SubstrateContentLayoutField.GetValue(areaScreenSubstrate) as LayoutElement; + LayoutElement layoutElement = new R.AreaScreenSubstrate(areaScreenSubstrate).ContentLayout; layoutElement.minHeight = 750f; // aka areaScreenSubstrate._maxHeight areaScreenSubstrate.method_8(); } @@ -98,8 +88,7 @@ namespace UIFixes return AccessTools.Method(typeof(ProductionPanel), nameof(ProductionPanel.method_4)); } - // Working with GClasses directly here, because this would be a nightmare with reflection - // Copied directly from method_4 + // Copied directly from method_4. Working with GClasses directly here, because this would be a nightmare with reflection [PatchPrefix] public static bool Prefix(ref IEnumerable __result, ProductionPanel __instance, GClass1922[] ___gclass1922_0, ValidationInputField ____searchInputField) { @@ -143,7 +132,7 @@ namespace UIFixes // Reset the default behavior AreaScreenSubstrate areaScreenSubstrate = __instance.GetComponentInParent(); - LayoutElement layoutElement = SubstrateContentLayoutField.GetValue(areaScreenSubstrate) as LayoutElement; + LayoutElement layoutElement = new R.AreaScreenSubstrate(areaScreenSubstrate).ContentLayout; layoutElement.minHeight = -1f; } } diff --git a/Patches/InspectWindowStatsPatches.cs b/Patches/InspectWindowStatsPatches.cs index c64a40d..8457d21 100644 --- a/Patches/InspectWindowStatsPatches.cs +++ b/Patches/InspectWindowStatsPatches.cs @@ -16,20 +16,8 @@ namespace UIFixes { public class InspectWindowStatsPatches { - private static FieldInfo AttributeCompactPanelDictionaryField; - private static FieldInfo AttributeCompactDropdownDictionaryField; - - private static FieldInfo CompactCharacteristicPanelItemAttributeField; - private static FieldInfo CompactCharacteristicPanelCompareItemAttributeField; - public static void Enable() { - AttributeCompactPanelDictionaryField = AccessTools.GetDeclaredFields(typeof(ItemSpecificationPanel)).First(f => typeof(IEnumerable>).IsAssignableFrom(f.FieldType)); - AttributeCompactDropdownDictionaryField = AccessTools.GetDeclaredFields(typeof(ItemSpecificationPanel)).First(f => typeof(IEnumerable>).IsAssignableFrom(f.FieldType)); - - CompactCharacteristicPanelItemAttributeField = AccessTools.Field(typeof(CompactCharacteristicPanel), "ItemAttribute"); - CompactCharacteristicPanelCompareItemAttributeField = AccessTools.Field(typeof(CompactCharacteristicPanel), "CompareItemAttribute"); - new AddShowHideModStatsButtonPatch().Enable(); new CalculateModStatsPatch().Enable(); new CompareModStatsPatch().Enable(); @@ -52,6 +40,8 @@ namespace UIFixes Transform ____compactPanel, SimpleTooltip ___simpleTooltip_0) { + var instance = new R.ItemSpecificationPanel(__instance); + if (!Settings.ShowModStats.Value || ___item_0 is not Mod) { return; @@ -64,28 +54,25 @@ namespace UIFixes } // Clean up existing one - if (AttributeCompactPanelDictionaryField.GetValue(__instance) is IDisposable compactPanels) + if (instance.CompactCharacteristicPanels is IDisposable compactPanels) { compactPanels.Dispose(); } - var newCompactPanels = Activator.CreateInstance(AttributeCompactPanelDictionaryField.FieldType, - [ + var newCompactPanels = R.ItemSpecificationPanel.CreateCompactCharacteristicPanels( deepAttributes, ____compactCharTemplate, ____compactPanel, - (ItemAttributeClass attribute, CompactCharacteristicPanel viewer) => viewer.Show(attribute, ___simpleTooltip_0, __instance.Boolean_0, 100) - ]) as IEnumerable>; + (attribute, viewer) => viewer.Show(attribute, ___simpleTooltip_0, __instance.Boolean_0, 100)); - AttributeCompactPanelDictionaryField.SetValue(__instance, newCompactPanels); + instance.CompactCharacteristicPanels = newCompactPanels; if (newCompactPanels.Any()) { newCompactPanels.Last().Value.OnTextWidthCalculated += __instance.method_3; int siblingIndex = newCompactPanels.Last().Value.Transform.GetSiblingIndex(); - var compactDropdownPanels = AttributeCompactDropdownDictionaryField.GetValue(__instance) as IEnumerable>; - foreach (var item in compactDropdownPanels) + foreach (var item in instance.CompactCharacteristicDropdowns) { item.Value.Transform.SetSiblingIndex(++siblingIndex); } @@ -104,12 +91,8 @@ namespace UIFixes // So I have to forcably call the refresh values method private class CompareModStatsPatch : ModulePatch { - private static MethodInfo RefreshStaticMethod; - protected override MethodBase GetTargetMethod() { - RefreshStaticMethod = AccessTools.Method(typeof(ItemSpecificationPanel), nameof(ItemSpecificationPanel.smethod_1), null, [typeof(CompactCharacteristicPanel)]); - return AccessTools.Method(typeof(ItemSpecificationPanel), nameof(ItemSpecificationPanel.method_6)); } @@ -153,31 +136,15 @@ namespace UIFixes return; } - var compactPanels = AttributeCompactPanelDictionaryField.GetValue(__instance); - RefreshStaticMethod.Invoke(null, [compactPanels, deepAttributes]); + var compactPanels = new R.ItemSpecificationPanel(__instance).CompactCharacteristicPanels; + R.ItemSpecificationPanel.Refresh(compactPanels, deepAttributes); } } private class AddShowHideModStatsButtonPatch : ModulePatch { - private static FieldInfo InteractionsButtonsContainerButtonTemplateField; - private static FieldInfo InteractionsButtonsContainerContainerField; - - private static FieldInfo InteractionsButtonContainerUIField; - private static MethodInfo InteractionsButtonContainerUIAddDisposableMethod; - - private static FieldInfo SimpleContextMenuButtonTextField; - protected override MethodBase GetTargetMethod() { - InteractionsButtonsContainerButtonTemplateField = AccessTools.Field(typeof(InteractionButtonsContainer), "_buttonTemplate"); - InteractionsButtonsContainerContainerField = AccessTools.Field(typeof(InteractionButtonsContainer), "_buttonsContainer"); - - InteractionsButtonContainerUIField = AccessTools.Field(typeof(InteractionButtonsContainer), "UI"); - InteractionsButtonContainerUIAddDisposableMethod = AccessTools.Method(InteractionsButtonContainerUIField.FieldType, "AddDisposable", [typeof(Action)]); - - SimpleContextMenuButtonTextField = AccessTools.Field(typeof(ContextMenuButton), "_text"); - return AccessTools.Method(typeof(ItemSpecificationPanel), nameof(ItemSpecificationPanel.method_4)); } @@ -194,15 +161,14 @@ namespace UIFixes return; } - SimpleContextMenuButton template = InteractionsButtonsContainerButtonTemplateField.GetValue(____interactionButtonsContainer) as SimpleContextMenuButton; - Transform transform = InteractionsButtonsContainerContainerField.GetValue(____interactionButtonsContainer) as Transform; + var buttonsContainer = new R.InteractionButtonsContainer(____interactionButtonsContainer); SimpleContextMenuButton toggleButton = null; // Listen to the setting and the work there to handle multiple windows open at once void onSettingChanged(object sender, EventArgs args) { - var text = SimpleContextMenuButtonTextField.GetValue(toggleButton) as TextMeshProUGUI; + var text = new R.ContextMenuButton(toggleButton).Text; text.text = GetLabel(); __instance.method_5(); // rebuild stat panels @@ -217,7 +183,7 @@ namespace UIFixes void createButton() { Sprite sprite = CacheResourcesPopAbstractClass.Pop("Characteristics/Icons/Modding"); - toggleButton = UnityEngine.Object.Instantiate(template, transform, false); + toggleButton = UnityEngine.Object.Instantiate(buttonsContainer.ButtonTemplate, buttonsContainer.Container, false); toggleButton.Show(GetLabel(), null, sprite, onClick, null); ____interactionButtonsContainer.method_5(toggleButton); // add to disposable list } @@ -226,11 +192,11 @@ namespace UIFixes contextInteractions.OnRedrawRequired += createButton; // And unsubscribe when the window goes away - InteractionsButtonContainerUIAddDisposableMethod.Invoke(InteractionsButtonContainerUIField.GetValue(____interactionButtonsContainer), [() => + buttonsContainer.AddDisposable(() => { contextInteractions.OnRedrawRequired -= createButton; Settings.ShowModStats.SettingChanged -= onSettingChanged; - }]); + }); createButton(); } @@ -346,14 +312,16 @@ namespace UIFixes const string DecreasingColorHex = "#C40000"; string text = textMesh.text; - ItemAttributeClass attribute = CompactCharacteristicPanelItemAttributeField.GetValue(panel) as ItemAttributeClass; + var wrappedPanel = new R.CompactCharacteristicPanel(panel); + ItemAttributeClass attribute = wrappedPanel.ItemAttribute; // Holy shit did they mess up MOA. Half of the calculation is done in the StringValue() method, so calculating delta from Base() loses all that // Plus, they round the difference to the nearest integer (!?) // Completely redo it if ((EItemAttributeId)attribute.Id == EItemAttributeId.CenterOfImpact) { - if (CompactCharacteristicPanelCompareItemAttributeField.GetValue(panel) is ItemAttributeClass compareAttribute) + var compareAttribute = wrappedPanel.CompareItemAttribute; + if (compareAttribute != null) { string currentStringValue = attribute.StringValue(); var moaMatch = Regex.Match(currentStringValue, @"^(\S+)"); diff --git a/Patches/RemoveDoorActionsPatch.cs b/Patches/RemoveDoorActionsPatch.cs index 39eb04d..262c541 100644 --- a/Patches/RemoveDoorActionsPatch.cs +++ b/Patches/RemoveDoorActionsPatch.cs @@ -27,7 +27,6 @@ namespace UIFixes { for (int i = __result.Actions.Count - 1; i >= 0; i--) { - // if (__result.Actions[i].Disabled) if (UnimplementedActions.Contains(__result.Actions[i].Name)) { __result.Actions.RemoveAt(i); diff --git a/Patches/SwapPatches.cs b/Patches/SwapPatches.cs index e1f6692..1acf8a7 100644 --- a/Patches/SwapPatches.cs +++ b/Patches/SwapPatches.cs @@ -17,24 +17,8 @@ namespace UIFixes { public class SwapPatches { - // Types needed - private static Type GridItemAddressType; // GClass2769 - private static FieldInfo GridItemAddressLocationInGridField; - private static PropertyInfo GridItemAddressGridProperty; - - private static Type SlotItemAddressType; // GClass2767 - private static FieldInfo SlotItemAddressSlotField; - - private static Type CanAcceptOperationType; // GStruct413 - private static PropertyInfo CanAcceptOperationSucceededProperty; - private static PropertyInfo CanAcceptOperationErrorProperty; - - private static Type SwapOperationType; // GStruct414 - private static MethodInfo SwapOperationToCanAcceptOperationOperator; - // Source container for the drag - we have to grab this early to check it private static IContainer SourceContainer; - private static FieldInfo GridViewNonInteractableField; // Whether we're being called from the "check every slot" loop private static bool InHighlight = false; @@ -50,22 +34,6 @@ namespace UIFixes public static void Enable() { - GridItemAddressType = PatchConstants.EftTypes.First(t => typeof(ItemAddress).IsAssignableFrom(t) && t.GetProperty("Grid") != null); // GClass2769 - GridItemAddressLocationInGridField = AccessTools.Field(GridItemAddressType, "LocationInGrid"); - GridItemAddressGridProperty = AccessTools.Property(GridItemAddressType, "Grid"); - - SlotItemAddressType = PatchConstants.EftTypes.First(t => typeof(ItemAddress).IsAssignableFrom(t) && t.GetField("Slot") != null); // GClass2767 - SlotItemAddressSlotField = AccessTools.Field(SlotItemAddressType, "Slot"); - - CanAcceptOperationType = AccessTools.Method(typeof(GridView), "CanAccept").GetParameters()[2].ParameterType.GetElementType(); // GStruct413, parameter is a ref type, get underlying type - CanAcceptOperationSucceededProperty = AccessTools.Property(CanAcceptOperationType, "Succeeded"); - CanAcceptOperationErrorProperty = AccessTools.Property(CanAcceptOperationType, "Error"); - - SwapOperationType = AccessTools.Method(typeof(InteractionsHandlerClass), nameof(InteractionsHandlerClass.Swap)).ReturnType; // GStruct414 - SwapOperationToCanAcceptOperationOperator = SwapOperationType.GetMethods().First(m => m.Name == "op_Implicit" && m.ReturnType == CanAcceptOperationType); - - GridViewNonInteractableField = AccessTools.Field(typeof(GridView), "_nonInteractable"); - new DetectSwapSourceContainerPatch().Enable(); new GridViewCanAcceptSwapPatch().Enable(); new DetectGridHighlightPrecheckPatch().Enable(); @@ -89,7 +57,9 @@ namespace UIFixes return false; } - if (InHighlight || itemContext == null || targetItemContext == null || (bool)CanAcceptOperationSucceededProperty.GetValue(operation) == true) + var wrappedOperation = new R.GridViewCanAcceptOperation(operation); + + if (InHighlight || itemContext == null || targetItemContext == null || wrappedOperation.Succeeded) { return false; } @@ -114,12 +84,12 @@ namespace UIFixes } // Check if the source container is a non-interactable GridView. Specifically for StashSearch, but may exist in other scenarios? - if (SourceContainer != null && SourceContainer is GridView && (bool)GridViewNonInteractableField.GetValue(SourceContainer)) + if (SourceContainer != null && SourceContainer is GridView && new R.GridView(SourceContainer).NonInteractable) { return false; } - string error = CanAcceptOperationErrorProperty.GetValue(operation).ToString(); + string error = wrappedOperation.Error.ToString(); if (Settings.SwapImpossibleContainers.Value && !InRaid() && error.StartsWith("No free room")) { // Check if it isn't allowed in that container, if so try to swap @@ -182,11 +152,8 @@ namespace UIFixes public class GridViewCanAcceptSwapPatch : ModulePatch { - private static FieldInfo GridViewTraderControllerClassField; - protected override MethodBase GetTargetMethod() { - GridViewTraderControllerClassField = AccessTools.GetDeclaredFields(typeof(GridView)).First(f => f.FieldType == typeof(TraderControllerClass)); return AccessTools.Method(typeof(GridView), nameof(GridView.CanAccept)); } @@ -199,11 +166,14 @@ namespace UIFixes return false; } - if (GridItemAddressType.IsInstanceOfType(itemAddressA) && GridItemAddressType.IsInstanceOfType(itemAddressB)) + if (R.GridItemAddress.Type.IsInstanceOfType(itemAddressA) && R.GridItemAddress.Type.IsInstanceOfType(itemAddressB)) { - LocationInGrid locationA = GridItemAddressLocationInGridField.GetValue(itemAddressA) as LocationInGrid; - LocationInGrid locationB = GridItemAddressLocationInGridField.GetValue(itemAddressB) as LocationInGrid; - StashGridClass grid = GridItemAddressGridProperty.GetValue(itemAddressA) as StashGridClass; + var gridItemAddressA = new R.GridItemAddress(itemAddressA); + var gridItemAddressB = new R.GridItemAddress(itemAddressB); + + LocationInGrid locationA = gridItemAddressA.Location; + LocationInGrid locationB = gridItemAddressB.Location; + StashGridClass grid = gridItemAddressA.Grid; var itemASize = itemA.CalculateRotatedSize(locationA.r); var itemASlots = new List(); @@ -243,9 +213,9 @@ namespace UIFixes Item item = itemContext.Item; Item targetItem = targetItemContext.Item; ItemAddress itemAddress = item.Parent; - ItemAddress targetAddress = targetItem.Parent; + ItemAddress targetItemAddress = targetItem.Parent; - if (targetAddress == null) + if (targetItemAddress == null) { return; } @@ -259,24 +229,26 @@ namespace UIFixes } } - // This is the location you're dragging it over, including rotation - LocationInGrid itemToLocation = __instance.CalculateItemLocation(itemContext); + // This is the location you're dragging it, including rotation + LocationInGrid itemToLocation = __instance.CalculateItemLocation(itemContext); - // This is a grid because we're in the GridView patch, i.e. you're dragging it over a grid - ItemAddress itemToAddress = Activator.CreateInstance(GridItemAddressType, [GridItemAddressGridProperty.GetValue(targetAddress), itemToLocation]) as ItemAddress; + // Target is a grid because we're in the GridView patch, i.e. you're dragging it over a grid + var targetGridItemAddress = new R.GridItemAddress(targetItemAddress); + ItemAddress itemToAddress = R.GridItemAddress.Create(targetGridItemAddress.Grid, itemToLocation); ItemAddress targetToAddress; - if (GridItemAddressType.IsInstanceOfType(itemAddress)) + if (R.GridItemAddress.Type.IsInstanceOfType(itemAddress)) { - LocationInGrid targetToLocation = (GridItemAddressLocationInGridField.GetValue(itemAddress) as LocationInGrid).Clone(); - targetToLocation.r = (GridItemAddressLocationInGridField.GetValue(targetAddress) as LocationInGrid).r; + var gridItemAddress = new R.GridItemAddress(itemAddress); - StashGridClass grid = GridItemAddressGridProperty.GetValue(itemAddress) as StashGridClass; - targetToAddress = Activator.CreateInstance(GridItemAddressType, [grid, targetToLocation]) as ItemAddress; + LocationInGrid targetToLocation = gridItemAddress.Location.Clone(); + targetToLocation.r = targetGridItemAddress.Location.r; + + targetToAddress = R.GridItemAddress.Create(gridItemAddress.Grid, targetToLocation); } - else if (SlotItemAddressType.IsInstanceOfType(itemAddress)) + else if (R.SlotItemAddress.Type.IsInstanceOfType(itemAddress)) { - targetToAddress = Activator.CreateInstance(SlotItemAddressType, [SlotItemAddressSlotField.GetValue(itemAddress)]) as ItemAddress; + targetToAddress = R.SlotItemAddress.Create(new R.SlotItemAddress(itemAddress).Slot); } else { @@ -284,14 +256,14 @@ namespace UIFixes } // Get the TraderControllerClass - TraderControllerClass traderControllerClass = GridViewTraderControllerClassField.GetValue(__instance) as TraderControllerClass; + TraderControllerClass traderControllerClass = new R.GridView(__instance).TraderController; // Check that the destinations won't overlap (Swap won't check this) if (!ItemsOverlap(item, itemToAddress, targetItem, targetToAddress)) { // Try original rotations var result = InteractionsHandlerClass.Swap(item, itemToAddress, targetItem, targetToAddress, traderControllerClass, true); - operation = SwapOperationToCanAcceptOperationOperator.Invoke(null, [result]); + operation = new R.SwapOperation(result).ToGridViewCanAcceptOperation(); __result = result.Succeeded; if (result.Succeeded) { @@ -300,9 +272,10 @@ namespace UIFixes } // If we're coming from a grid, try rotating the target object - if (GridItemAddressType.IsInstanceOfType(itemAddress)) + if (R.GridItemAddress.Type.IsInstanceOfType(itemAddress)) { - var targetToLocation = GridItemAddressLocationInGridField.GetValue(targetToAddress) as LocationInGrid; + var gridItemAddress = new R.GridItemAddress(itemAddress); + var targetToLocation = gridItemAddress.Location; targetToLocation.r = targetToLocation.r == ItemRotation.Horizontal ? ItemRotation.Vertical : ItemRotation.Horizontal; if (!ItemsOverlap(item, itemToAddress, targetItem, targetToAddress)) { @@ -310,7 +283,7 @@ namespace UIFixes if (result.Succeeded) { // Only save this operation result if it succeeded, otherwise we return the non-rotated result from above - operation = SwapOperationToCanAcceptOperationOperator.Invoke(null, [result]); + operation = new R.SwapOperation(result).ToGridViewCanAcceptOperation(); __result = true; return; } @@ -326,7 +299,7 @@ namespace UIFixes { protected override MethodBase GetTargetMethod() { - return AccessTools.Method(SwapOperationType.GenericTypeArguments[0], "RaiseEvents"); // GClass2787 + return AccessTools.Method(R.SwapOperation.Type.GenericTypeArguments[0], "RaiseEvents"); // GClass2797 } [PatchPostfix] @@ -404,7 +377,7 @@ namespace UIFixes var item = __instance.Item; var targetItem = slot.ContainedItem; - var itemToAddress = Activator.CreateInstance(SlotItemAddressType, [slot]) as ItemAddress; + var itemToAddress = R.SlotItemAddress.Create(slot); var targetToAddress = item.Parent; // Repair kits again @@ -415,7 +388,7 @@ namespace UIFixes } var result = InteractionsHandlerClass.Swap(item, itemToAddress, targetItem, targetToAddress, itemController, simulate); - operation = SwapOperationToCanAcceptOperationOperator.Invoke(null, [result]); + operation = new R.SwapOperation(result).ToGridViewCanAcceptOperation(); __result = result.Succeeded; } } @@ -500,7 +473,7 @@ namespace UIFixes ItemSpecificationPanel panel = sourceComponent.GetComponentInParent(); if (panel != null) { - Slot slot = SlotItemAddressSlotField.GetValue(__instance.ItemAddress) as Slot; + Slot slot = new R.SlotItemAddress(__instance.ItemAddress).Slot; ItemContextClass itemUnderCursorContext = itemUnderCursor != null ? new ItemContextClass(itemUnderCursor, ItemRotation.Horizontal) : null; panel.method_15(slot, itemUnderCursorContext); } diff --git a/Patches/WeaponZoomPatches.cs b/Patches/WeaponZoomPatches.cs index dfc3a1c..62239f4 100644 --- a/Patches/WeaponZoomPatches.cs +++ b/Patches/WeaponZoomPatches.cs @@ -3,7 +3,6 @@ using EFT.InventoryLogic; using EFT.UI; using EFT.UI.WeaponModding; using HarmonyLib; -using System; using System.Reflection; using UnityEngine.EventSystems; diff --git a/Plugin.cs b/Plugin.cs index 754a7b9..02b4efd 100644 --- a/Plugin.cs +++ b/Plugin.cs @@ -9,6 +9,8 @@ namespace UIFixes { Settings.Init(Config); + R.Init(); + new ConfirmationDialogKeysPatch().Enable(); new FixMailRecieveAllPatch().Enable(); new FixTooltipPatch().Enable(); diff --git a/R.cs b/R.cs new file mode 100644 index 0000000..adfada6 --- /dev/null +++ b/R.cs @@ -0,0 +1,311 @@ +using Aki.Reflection.Utils; +using Diz.LanguageExtensions; +using EFT.InputSystem; +using EFT.InventoryLogic; +using EFT.UI; +using HarmonyLib; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using TMPro; +using UnityEngine; +using UnityEngine.UI; + +namespace UIFixes +{ + public static class R + { + public static void Init() + { + // Order is significant, as some reference each other + DialogWindow.InitTypes(); + ControlSettings.InitTypes(); + ProductionPanel.InitTypes(); + ProductionPanelShowSubclass.InitTypes(); + Scheme.InitTypes(); + AreaScreenSubstrate.InitTypes(); + ItemSpecificationPanel.InitTypes(); + CompactCharacteristicPanel.InitTypes(); + GridItemAddress.InitTypes(); + SlotItemAddress.InitTypes(); + GridView.InitTypes(); + GridViewCanAcceptOperation.InitTypes(); + SwapOperation.InitTypes(); + InteractionButtonsContainer.InitTypes(); + ContextMenuButton.InitTypes(); + } + + public abstract class Wrapper(object value) + { + public object Value { get; protected set; } = value; + } + + public class DialogWindow(object value) : Wrapper(value) + { + public static Type Type { get; private set; } + private static MethodInfo AcceptMethod; + + public static void InitTypes() + { + Type = typeof(MessageWindow).BaseType; + AcceptMethod = AccessTools.Method(Type, "Accept"); + } + + public void Accept() => AcceptMethod.Invoke(Value, []); + } + + public class ControlSettings(object value) : Wrapper(value) + { + public static Type Type { get; private set; } + private static MethodInfo GetKeyNameMethod; + + public static void InitTypes() + { + Type = PatchConstants.EftTypes.Single(x => x.GetMethod("GetBoundItemNames") != null); // GClass960 + GetKeyNameMethod = AccessTools.Method(Type, "GetKeyName"); + } + + public string GetKeyName(EGameKey key) => (string)GetKeyNameMethod.Invoke(Value, [key]); + } + + public class ProductionPanel(object value) : Wrapper(value) + { + public static Type Type { get; private set; } + private static FieldInfo SearchInputFieldField; + + public static void InitTypes() + { + Type = typeof(EFT.Hideout.ProductionPanel); + SearchInputFieldField = AccessTools.Field(Type, "_searchInputField"); + } + + public ValidationInputField SeachInputField { get { return (ValidationInputField)SearchInputFieldField.GetValue(Value); } } + } + + public class ProductionPanelShowSubclass(object value) : Wrapper(value) + { + public static Type Type { get; private set; } + private static FieldInfo ProductionPanelField; + + public static void InitTypes() + { + Type = typeof(EFT.Hideout.ProductionPanel).GetNestedTypes().First(t => t.GetField("availableSearch") != null); // ProductionPanel.Class1631 + ProductionPanelField = AccessTools.Field(Type, "productionPanel_0"); + } + + public EFT.Hideout.ProductionPanel ProductionPanel { get { return (EFT.Hideout.ProductionPanel)ProductionPanelField.GetValue(Value); } } + } + + public class Scheme(object value) : Wrapper(value) + { + public static Type Type { get; private set; } + private static FieldInfo EndProductField; + + public static void InitTypes() + { + Type = PatchConstants.EftTypes.First(t => t.GetField("endProduct") != null); // GClass1923 + EndProductField = AccessTools.Field(Type, "endProduct"); + } + + public string EndProduct { get { return (string)EndProductField.GetValue(Value); } } + } + + public class AreaScreenSubstrate(object value) : Wrapper(value) + { + public static Type Type { get; private set; } + private static FieldInfo ContentLayoutField; + + public static void InitTypes() + { + Type = typeof(EFT.Hideout.AreaScreenSubstrate); + ContentLayoutField = AccessTools.Field(Type, "_contentLayout"); + } + + public LayoutElement ContentLayout { get { return (LayoutElement)ContentLayoutField.GetValue(Value); } } + } + + public class ItemSpecificationPanel(object value) : Wrapper(value) + { + public static Type Type { get; private set; } + private static FieldInfo CompactCharacteristicPanelsField; + private static FieldInfo CompactCharacteristicDropdownsField; + private static MethodInfo RefreshMethod; + + public static void InitTypes() + { + Type = typeof(EFT.UI.ItemSpecificationPanel); + CompactCharacteristicPanelsField = AccessTools.GetDeclaredFields(Type).First(f => typeof(IEnumerable>).IsAssignableFrom(f.FieldType)); + CompactCharacteristicDropdownsField = AccessTools.GetDeclaredFields(Type).First(f => typeof(IEnumerable>).IsAssignableFrom(f.FieldType)); + + RefreshMethod = AccessTools.Method(Type, nameof(EFT.UI.ItemSpecificationPanel.smethod_1), null, [typeof(EFT.UI.CompactCharacteristicPanel)]); + } + + public IEnumerable> CompactCharacteristicPanels + { + get { return (IEnumerable>)CompactCharacteristicPanelsField.GetValue(Value); } + set { CompactCharacteristicPanelsField.SetValue(Value, value); } + } + + public IEnumerable> CompactCharacteristicDropdowns + { + get { return (IEnumerable>)CompactCharacteristicDropdownsField.GetValue(Value); } + set { CompactCharacteristicDropdownsField.SetValue(Value, value); } + } + + public static IEnumerable> CreateCompactCharacteristicPanels( + IEnumerable items, + EFT.UI.CompactCharacteristicPanel template, + Transform transform, + Action showAction) + { + return (IEnumerable>)Activator.CreateInstance(CompactCharacteristicPanelsField.FieldType, [items, template, transform, showAction]); + } + + public static void Refresh(IEnumerable> viewList, IReadOnlyCollection changedList) => RefreshMethod.Invoke(null, [viewList, changedList]); + } + + public class CompactCharacteristicPanel(object value) : Wrapper(value) + { + public static Type Type { get; private set; } + private static FieldInfo ItemAttributeField; + private static FieldInfo CompareItemAttributeField; + + public static void InitTypes() + { + Type = typeof(EFT.UI.CompactCharacteristicPanel); + ItemAttributeField = AccessTools.Field(Type, "ItemAttribute"); + CompareItemAttributeField = AccessTools.Field(Type, "CompareItemAttribute"); + } + + public ItemAttributeClass ItemAttribute { get { return (ItemAttributeClass)ItemAttributeField.GetValue(Value); } } + public ItemAttributeClass CompareItemAttribute { get { return (ItemAttributeClass)CompareItemAttributeField.GetValue(Value); } } + } + + public class GridItemAddress(object value) : Wrapper(value) + { + public static Type Type { get; private set; } + private static FieldInfo LocationInGridField; + private static PropertyInfo GridProperty; + + public static void InitTypes() + { + Type = PatchConstants.EftTypes.First(t => typeof(ItemAddress).IsAssignableFrom(t) && t.GetProperty("Grid") != null); // GClass2769 + LocationInGridField = AccessTools.Field(Type, "LocationInGrid"); + GridProperty = AccessTools.Property(Type, "Grid"); + } + + public static ItemAddress Create(StashGridClass grid, LocationInGrid location) + { + return (ItemAddress)Activator.CreateInstance(Type, [grid, location]); + } + + public LocationInGrid Location { get { return (LocationInGrid)LocationInGridField.GetValue(Value); } } + public StashGridClass Grid { get { return (StashGridClass)GridProperty.GetValue(Value); } } + } + + public class SlotItemAddress(object value) : Wrapper(value) + { + public static Type Type { get; private set; } + private static FieldInfo SlotField; + + public static void InitTypes() + { + Type = PatchConstants.EftTypes.First(t => typeof(ItemAddress).IsAssignableFrom(t) && t.GetField("Slot") != null); // GClass2767 + SlotField = AccessTools.Field(Type, "Slot"); + } + + public static ItemAddress Create(Slot slot) + { + return (ItemAddress)Activator.CreateInstance(Type, [slot]); + } + + public Slot Slot { get { return (Slot)SlotField.GetValue(Value); } } + } + + public class GridView(object value) : Wrapper(value) + { + public static Type Type { get; private set; } + private static FieldInfo TraderControllerField; + private static FieldInfo NonInteractableField; + + public static void InitTypes() + { + Type = typeof(EFT.UI.DragAndDrop.GridView); + TraderControllerField = AccessTools.GetDeclaredFields(Type).First(f => f.FieldType == typeof(TraderControllerClass)); + NonInteractableField = AccessTools.Field(Type, "_nonInteractable"); + } + + public TraderControllerClass TraderController { get { return (TraderControllerClass)TraderControllerField.GetValue(Value); } } + public bool NonInteractable { get { return (bool)NonInteractableField.GetValue(Value); } } + } + + public class GridViewCanAcceptOperation(object value) : Wrapper(value) + { + public static Type Type { get; private set; } + private static PropertyInfo SucceededProperty; + private static PropertyInfo ErrorProperty; + + public static void InitTypes() + { + Type = AccessTools.Method(typeof(EFT.UI.DragAndDrop.GridView), "CanAccept").GetParameters()[2].ParameterType.GetElementType(); // GStruct413, parameter is a ref type, get underlying type + SucceededProperty = AccessTools.Property(Type, "Succeeded"); + ErrorProperty = AccessTools.Property(Type, "Error"); + } + + public bool Succeeded { get { return (bool)SucceededProperty.GetValue(Value); } } + public Error Error { get { return (Error)ErrorProperty.GetValue(Value); } } + } + + public class SwapOperation(object value) : Wrapper(value) + { + public static Type Type { get; private set; } + private static MethodInfo ImplicitCastToGridViewCanAcceptOperationMethod; + + public static void InitTypes() + { + Type = AccessTools.Method(typeof(InteractionsHandlerClass), nameof(InteractionsHandlerClass.Swap)).ReturnType; // GStruct414 + ImplicitCastToGridViewCanAcceptOperationMethod = Type.GetMethods().First(m => m.Name == "op_Implicit" && m.ReturnType == GridViewCanAcceptOperation.Type); + } + + public object ToGridViewCanAcceptOperation() => ImplicitCastToGridViewCanAcceptOperationMethod.Invoke(null, [Value]); + } + + public class InteractionButtonsContainer(object value) : Wrapper(value) + { + public static Type Type { get; private set; } + private static FieldInfo ButtonTemplateField; + private static FieldInfo ContainerField; + private static FieldInfo UIField; + private static MethodInfo UIAddDisposableMethod; + + public static void InitTypes() + { + Type = typeof(EFT.UI.InteractionButtonsContainer); + ButtonTemplateField = AccessTools.Field(Type, "_buttonTemplate"); + ContainerField = AccessTools.Field(Type, "_buttonsContainer"); + UIField = AccessTools.Field(Type, "UI"); // GClass767 + UIAddDisposableMethod = AccessTools.Method(UIField.FieldType, "AddDisposable", [typeof(Action)]); + } + + public SimpleContextMenuButton ButtonTemplate { get { return (SimpleContextMenuButton)ButtonTemplateField.GetValue(Value); } } + public Transform Container { get { return (Transform)ContainerField.GetValue(Value); } } + public object UI { get { return UIField.GetValue(Value); } } + public void AddDisposable(Action action) => UIAddDisposableMethod.Invoke(UI, [action]); + } + + public class ContextMenuButton(object value) : Wrapper(value) + { + public static Type Type { get; private set;} + private static FieldInfo TextField; + + public static void InitTypes() + { + Type = typeof(EFT.UI.ContextMenuButton); + TextField = AccessTools.Field(Type, "_text"); + } + + public TextMeshProUGUI Text { get { return (TextMeshProUGUI)TextField.GetValue(Value); } } + } + } +}