diff --git a/Patches/ReloadInPlacePatches.cs b/Patches/ReloadInPlacePatches.cs new file mode 100644 index 0000000..5e1e2eb --- /dev/null +++ b/Patches/ReloadInPlacePatches.cs @@ -0,0 +1,156 @@ +using EFT; +using EFT.UI; +using HarmonyLib; +using SPT.Reflection.Patching; +using System; +using System.Linq; +using System.Reflection; + +namespace UIFixes; + +public static class ReloadInPlacePatches +{ + private static bool IsReloading = false; + private static MagazineClass FoundMagazine = null; + + public static void Enable() + { + // These patch ItemUiContext.ReloadWeapon, which is called from the context menu Reload + new ReloadInPlacePatch().Enable(); + new ReloadInPlaceFindMagPatch().Enable(); + new ReloadInPlaceFindSpotPatch().Enable(); + + // This patches the firearmsController code when you hit R in raid with an external magazine class + new SwapIfNoSpacePatch().Enable(); + } + + public class ReloadInPlacePatch : ModulePatch + { + protected override MethodBase GetTargetMethod() + { + return AccessTools.Method(typeof(ItemUiContext), nameof(ItemUiContext.ReloadWeapon)); + } + + [PatchPrefix] + public static void Prefix() + { + IsReloading = Settings.SwapMags.Value; + } + + [PatchPostfix] + public static void Postfix() + { + IsReloading = false; + FoundMagazine = null; + } + } + + public class ReloadInPlaceFindMagPatch : ModulePatch + { + protected override MethodBase GetTargetMethod() + { + return AccessTools.Method(typeof(ItemUiContext), nameof(ItemUiContext.method_5)); + } + + [PatchPostfix] + public static void Postfix(MagazineClass __result) + { + if (IsReloading) + { + FoundMagazine = __result; + } + } + } + + public class ReloadInPlaceFindSpotPatch : ModulePatch + { + protected override MethodBase GetTargetMethod() + { + Type type = typeof(ItemUiContext).GetNestedTypes().Single(t => t.GetField("currentMagazine") != null); + return AccessTools.Method(type, "method_0"); + } + + [PatchPrefix] + public static void Prefix(StashGridClass grid, ref GStruct414 __state) + { + if (!Settings.SwapMags.Value) + { + return; + } + + if (grid.Contains(FoundMagazine)) + { + __state = InteractionsHandlerClass.Remove(FoundMagazine, grid.ParentItem.Owner as TraderControllerClass, false, false); + } + } + + [PatchPostfix] + public static void Postfix(GStruct414 __state) + { + if (!Settings.SwapMags.Value || __state.Value == null) + { + return; + } + + if (__state.Succeeded) + { + __state.Value.RollBack(); + } + } + } + + public class SwapIfNoSpacePatch : ModulePatch + { + protected override MethodBase GetTargetMethod() + { + return AccessTools.Method(typeof(Player.FirearmController), nameof(Player.FirearmController.ReloadMag)); + } + + // By default this method will do a series of removes and adds, but not swap, to reload + // This tied to a different animation state machine sequence than Swap(), and is faster than Swap. + // So only use Swap if *needed*, otherwise its penalizing all reload speeds + [PatchPrefix] + public static bool Prefix(Player.FirearmController __instance, MagazineClass magazine, ItemAddressClass gridItemAddress) + { + // If gridItemAddress isn't null, it already found a place for the current mag, so let it run + if (gridItemAddress != null || !Settings.SwapMags.Value) + { + return true; + } + + // Weapon doesn't currently have a magazine, let the default run (will load one) + MagazineClass currentMagazine = __instance.Weapon.GetCurrentMagazine(); + if (currentMagazine == null) + { + return true; + } + + InventoryControllerClass controller = __instance.Weapon.Owner as InventoryControllerClass; + + // Null address means it couldn't find a spot. Try to remove magazine (temporarily) and try again + var operation = InteractionsHandlerClass.Remove(magazine, controller, false, false); + if (operation.Failed) + { + return true; + } + + gridItemAddress = controller.Inventory.Equipment.GetPrioritizedGridsForUnloadedObject(false) + .Select(grid => grid.FindLocationForItem(currentMagazine)) + .Where(address => address != null) + .OrderBy(address => address.Grid.GridWidth.Value * address.Grid.GridHeight.Value) + .FirstOrDefault(); // BSG's version checks null again, but there's no nulls already. If there's no matches, the enumerable is empty + + // Put the magazine back + operation.Value.RollBack(); + + if (gridItemAddress == null) + { + // Didn't work, nowhere to put magazine. Let it run (will drop mag on ground) + return true; + } + + controller.TryRunNetworkTransaction(InteractionsHandlerClass.Swap(currentMagazine, gridItemAddress, magazine, new GClass2783(__instance.Weapon.GetMagazineSlot()), controller, true), null); + return false; + } + } +} \ No newline at end of file diff --git a/Plugin.cs b/Plugin.cs index 01d0270..6c98a25 100644 --- a/Plugin.cs +++ b/Plugin.cs @@ -65,6 +65,7 @@ public class Plugin : BaseUnityPlugin MoveSortingTablePatches.Enable(); FilterOutOfStockPatches.Enable(); SortPatches.Enable(); + ReloadInPlacePatches.Enable(); } public static bool InRaid() diff --git a/Settings.cs b/Settings.cs index f5ebf05..384dc9b 100644 --- a/Settings.cs +++ b/Settings.cs @@ -81,6 +81,7 @@ internal class Settings public static ConfigEntry MultiSelectStrat { get; set; } public static ConfigEntry ShowMultiSelectDebug { get; set; } // Advanced public static ConfigEntry SwapItems { get; set; } + public static ConfigEntry SwapMags { get; set; } public static ConfigEntry SwapImpossibleContainers { get; set; } public static ConfigEntry ReorderGrids { get; set; } public static ConfigEntry SynchronizeStashScrolling { get; set; } @@ -398,6 +399,15 @@ internal class Settings null, new ConfigurationManagerAttributes { }))); + configEntries.Add(SwapMags = config.Bind( + InventorySection, + "Reload Magazines In-Place", + true, + new ConfigDescription( + "When reloading a weapon with a magazine, swap locations with the new magazine (if possible)", + null, + new ConfigurationManagerAttributes { }))); + configEntries.Add(SwapImpossibleContainers = config.Bind( InventorySection, "Swap with Incompatible Containers", diff --git a/UIFixes.csproj b/UIFixes.csproj index 915d242..e5f21b8 100644 --- a/UIFixes.csproj +++ b/UIFixes.csproj @@ -4,7 +4,7 @@ net471 Tyfon.UIFixes SPT UI Fixes - 2.2.2 + 2.3.0 true latest Debug;Release diff --git a/server/package.json b/server/package.json index 7c1964d..0116677 100644 --- a/server/package.json +++ b/server/package.json @@ -1,6 +1,6 @@ { "name": "uifixes", - "version": "2.2.2", + "version": "2.3.0", "main": "src/mod.js", "license": "MIT", "author": "Tyfon",