Major reflection refactor into R helper class

This commit is contained in:
Tyfon
2024-05-20 15:05:55 -07:00
parent a3c85da54e
commit b8f85c06c6
10 changed files with 387 additions and 159 deletions

View File

@@ -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;
}
}

View File

@@ -1,7 +1,6 @@
using Aki.Reflection.Patching;
using EFT.UI.Chat;
using HarmonyLib;
using System;
using System.Reflection;
namespace UIFixes

View File

@@ -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;
}
}

View File

@@ -12,16 +12,10 @@ namespace UIFixes
{
public class HideoutSearchPatches
{
private static FieldInfo ProductionPanelSearch;
private static FieldInfo SubstrateContentLayoutField;
private static readonly Dictionary<string, string> 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<AreaScreenSubstrate>();
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<GClass1923> __result, ProductionPanel __instance, GClass1922[] ___gclass1922_0, ValidationInputField ____searchInputField)
{
@@ -143,7 +132,7 @@ namespace UIFixes
// Reset the default behavior
AreaScreenSubstrate areaScreenSubstrate = __instance.GetComponentInParent<AreaScreenSubstrate>();
LayoutElement layoutElement = SubstrateContentLayoutField.GetValue(areaScreenSubstrate) as LayoutElement;
LayoutElement layoutElement = new R.AreaScreenSubstrate(areaScreenSubstrate).ContentLayout;
layoutElement.minHeight = -1f;
}
}

View File

@@ -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<KeyValuePair<ItemAttributeClass, CompactCharacteristicPanel>>).IsAssignableFrom(f.FieldType));
AttributeCompactDropdownDictionaryField = AccessTools.GetDeclaredFields(typeof(ItemSpecificationPanel)).First(f => typeof(IEnumerable<KeyValuePair<ItemAttributeClass, CompactCharacteristicDropdownPanel>>).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<KeyValuePair<ItemAttributeClass, CompactCharacteristicPanel>>;
(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<KeyValuePair<ItemAttributeClass, CompactCharacteristicDropdownPanel>>;
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<Sprite>("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+)");

View File

@@ -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);

View File

@@ -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<GClass2797>
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<GClass2797>
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<int>();
@@ -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
// 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<ItemSpecificationPanel>();
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);
}

View File

@@ -3,7 +3,6 @@ using EFT.InventoryLogic;
using EFT.UI;
using EFT.UI.WeaponModding;
using HarmonyLib;
using System;
using System.Reflection;
using UnityEngine.EventSystems;

View File

@@ -9,6 +9,8 @@ namespace UIFixes
{
Settings.Init(Config);
R.Init();
new ConfirmationDialogKeysPatch().Enable();
new FixMailRecieveAllPatch().Enable();
new FixTooltipPatch().Enable();

311
R.cs Normal file
View File

@@ -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<KeyValuePair<ItemAttributeClass, EFT.UI.CompactCharacteristicPanel>>).IsAssignableFrom(f.FieldType));
CompactCharacteristicDropdownsField = AccessTools.GetDeclaredFields(Type).First(f => typeof(IEnumerable<KeyValuePair<ItemAttributeClass, EFT.UI.CompactCharacteristicDropdownPanel>>).IsAssignableFrom(f.FieldType));
RefreshMethod = AccessTools.Method(Type, nameof(EFT.UI.ItemSpecificationPanel.smethod_1), null, [typeof(EFT.UI.CompactCharacteristicPanel)]);
}
public IEnumerable<KeyValuePair<ItemAttributeClass, EFT.UI.CompactCharacteristicPanel>> CompactCharacteristicPanels
{
get { return (IEnumerable<KeyValuePair<ItemAttributeClass, EFT.UI.CompactCharacteristicPanel>>)CompactCharacteristicPanelsField.GetValue(Value); }
set { CompactCharacteristicPanelsField.SetValue(Value, value); }
}
public IEnumerable<KeyValuePair<ItemAttributeClass, EFT.UI.CompactCharacteristicDropdownPanel>> CompactCharacteristicDropdowns
{
get { return (IEnumerable<KeyValuePair<ItemAttributeClass, EFT.UI.CompactCharacteristicDropdownPanel>>)CompactCharacteristicDropdownsField.GetValue(Value); }
set { CompactCharacteristicDropdownsField.SetValue(Value, value); }
}
public static IEnumerable<KeyValuePair<ItemAttributeClass, EFT.UI.CompactCharacteristicPanel>> CreateCompactCharacteristicPanels(
IEnumerable<ItemAttributeClass> items,
EFT.UI.CompactCharacteristicPanel template,
Transform transform,
Action<ItemAttributeClass, EFT.UI.CompactCharacteristicPanel> showAction)
{
return (IEnumerable<KeyValuePair<ItemAttributeClass, EFT.UI.CompactCharacteristicPanel>>)Activator.CreateInstance(CompactCharacteristicPanelsField.FieldType, [items, template, transform, showAction]);
}
public static void Refresh(IEnumerable<KeyValuePair<ItemAttributeClass, EFT.UI.CompactCharacteristicPanel>> viewList, IReadOnlyCollection<ItemAttributeClass> 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<GClass2797>
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); } }
}
}
}