reload in place

This commit is contained in:
Tyfon
2024-07-18 14:42:18 -07:00
parent cf23b39353
commit b748eb96ba
5 changed files with 169 additions and 2 deletions

View File

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

View File

@@ -65,6 +65,7 @@ public class Plugin : BaseUnityPlugin
MoveSortingTablePatches.Enable();
FilterOutOfStockPatches.Enable();
SortPatches.Enable();
ReloadInPlacePatches.Enable();
}
public static bool InRaid()

View File

@@ -81,6 +81,7 @@ internal class Settings
public static ConfigEntry<MultiSelectStrategy> MultiSelectStrat { get; set; }
public static ConfigEntry<bool> ShowMultiSelectDebug { get; set; } // Advanced
public static ConfigEntry<bool> SwapItems { get; set; }
public static ConfigEntry<bool> SwapMags { get; set; }
public static ConfigEntry<bool> SwapImpossibleContainers { get; set; }
public static ConfigEntry<bool> ReorderGrids { get; set; }
public static ConfigEntry<bool> 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",

View File

@@ -4,7 +4,7 @@
<TargetFramework>net471</TargetFramework>
<AssemblyName>Tyfon.UIFixes</AssemblyName>
<Description>SPT UI Fixes</Description>
<Version>2.2.2</Version>
<Version>2.3.0</Version>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<LangVersion>latest</LangVersion>
<Configurations>Debug;Release</Configurations>

View File

@@ -1,6 +1,6 @@
{
"name": "uifixes",
"version": "2.2.2",
"version": "2.3.0",
"main": "src/mod.js",
"license": "MIT",
"author": "Tyfon",