Returning tools to containers; server config

This commit is contained in:
Tyfon
2024-07-02 17:09:43 -07:00
parent d21d981125
commit 29599dfa69
4 changed files with 174 additions and 0 deletions

View File

@@ -0,0 +1,69 @@
using Aki.Reflection.Patching;
using EFT;
using EFT.InventoryLogic;
using HarmonyLib;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace UIFixes
{
public class PutToolsBackPatch : ModulePatch
{
protected override MethodBase GetTargetMethod()
{
return AccessTools.Method(typeof(GClass1841), nameof(GClass1841.method_8));
}
// 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 GClass1189[] newItems, Profile ___profile_0, ItemFactory ___gclass1486_0, GClass2764 ___gclass2764_0)
{
Inventory inventory = ___profile_0.Inventory;
StashClass stash = inventory.Stash;
if (inventory != null && stash != null)
{
var handledContainers = new ContainerCollection[] { inventory.Stash, inventory.Equipment, inventory.QuestRaidItems, inventory.QuestStashItems, inventory.SortingTable };
var unhandledItems = newItems.Where(i => !handledContainers.Select(c => c.Id).Contains(i.parentId)).ToArray();
if (!unhandledItems.Any())
{
return;
}
// Change the parameter to remove the items handled here
newItems = newItems.Except(unhandledItems).ToArray();
List<Item> stashItems = stash.GetNotMergedItems().ToList();
ItemFactory.GStruct134 tree = ___gclass1486_0.FlatItemsToTree(unhandledItems, true, null);
foreach (Item item in tree.Items.Values.Where(i => i.CurrentAddress == null))
{
GClass1189 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
Item parent = stashItems.FirstOrDefault(i => i.Id == newItem.parentId);
if (parent is ContainerCollection containerCollection)
{
if (containerCollection.GetContainer(newItem.slotId) is StashGridClass grid)
{
LocationInGrid location = GClass1485.CreateItemLocation<LocationInGrid>(newItem.location);
ItemAddress itemAddress = new GClass2769(grid, location);
GStruct414<GClass2782> operation = InteractionsHandlerClass.Add(item, itemAddress, ___gclass2764_0, false);
if (operation.Succeeded)
{
operation.Value.RaiseEvents(___gclass2764_0, CommandStatus.Begin);
operation.Value.RaiseEvents(___gclass2764_0, CommandStatus.Succeed);
}
}
}
}
}
}
}
}
}

View File

@@ -55,6 +55,7 @@ namespace UIFixes
UnloadAmmoPatches.Enable();
new FixTraderControllerSimulateFalsePatch().Enable();
LoadMultipleMagazinesPatches.Enable();
new PutToolsBackPatch().Enable();
}
public static bool InRaid()

View File

@@ -0,0 +1,3 @@
{
"putToolsBack": true
}

View File

@@ -1,9 +1,13 @@
import type { DependencyContainer } from "tsyringe";
import type { InventoryController } from "@spt-aki/controllers/InventoryController";
import type { HideoutHelper } from "@spt-aki/helpers/HideoutHelper";
import type { InRaidHelper } from "@spt-aki/helpers/InRaidHelper";
import type { InventoryHelper } from "@spt-aki/helpers/InventoryHelper";
import type { ItemHelper } from "@spt-aki/helpers/ItemHelper";
import type { ProfileHelper } from "@spt-aki/helpers/ProfileHelper";
import type { RagfairSortHelper } from "@spt-aki/helpers/RagfairSortHelper";
import type { IHideoutSingleProductionStartRequestData } from "@spt-aki/models/eft/hideout/IHideoutSingleProductionStartRequestData";
import type { IRagfairOffer } from "@spt-aki/models/eft/ragfair/IRagfairOffer";
import { Money } from "@spt-aki/models/enums/Money";
import type { IPreAkiLoadMod } from "@spt-aki/models/external/IPreAkiLoadMod";
@@ -12,6 +16,8 @@ import type { DatabaseServer } from "@spt-aki/servers/DatabaseServer";
import type { StaticRouterModService } from "@spt-aki/services/mod/staticRouter/StaticRouterModService";
import type { JsonUtil } from "@spt-aki/utils/JsonUtil";
import config from "../config/config.json";
class UIFixes implements IPreAkiLoadMod {
private databaseServer: DatabaseServer;
private logger: ILogger;
@@ -21,6 +27,7 @@ class UIFixes implements IPreAkiLoadMod {
this.logger = container.resolve<ILogger>("WinstonLogger");
const profileHelper = container.resolve<ProfileHelper>("ProfileHelper");
const itemHelper = container.resolve<ItemHelper>("ItemHelper");
const staticRouterModService = container.resolve<StaticRouterModService>("StaticRouterModService");
const jsonUtil = container.resolve<JsonUtil>("JsonUtil");
@@ -83,6 +90,100 @@ class UIFixes implements IPreAkiLoadMod {
{ frequency: "Always" }
);
// Better tool return - starting production
if (config.putToolsBack) {
container.afterResolution(
"HideoutHelper",
(_, hideoutHelper: HideoutHelper) => {
const original = hideoutHelper.registerProduction;
hideoutHelper.registerProduction = (pmcData, body, sessionID) => {
const result = original.call(hideoutHelper, pmcData, body, sessionID);
// The items haven't been deleted yet, augment the list with their parentId
const bodyAsSingle = body as IHideoutSingleProductionStartRequestData;
if (bodyAsSingle && bodyAsSingle.tools?.length > 0) {
const requestTools = bodyAsSingle.tools;
const tools = pmcData.Hideout.Production[body.recipeId].sptRequiredTools;
for (let i = 0; i < tools.length; i++) {
const originalTool = pmcData.Inventory.items.find(x => x._id === requestTools[i].id);
tools[i]["uifixes.returnTo"] = [originalTool.parentId, originalTool.slotId];
}
}
return result;
};
},
{ frequency: "Always" }
);
// Better tool return - returning the tool
container.afterResolution(
"InventoryHelper",
(_, inventoryHelper: InventoryHelper) => {
const original = inventoryHelper.addItemToStash;
inventoryHelper.addItemToStash = (sessionId, request, pmcData, output) => {
const itemWithModsToAddClone = jsonUtil.clone(request.itemWithModsToAdd);
// If a tool marked with uifixes is there, try to return it to its original container
const tool = itemWithModsToAddClone[0];
if (tool["uifixes.returnTo"]) {
const [containerId, slotId] = tool["uifixes.returnTo"];
const container = pmcData.Inventory.items.find(x => x._id === containerId);
if (container) {
const containerTemplate = itemHelper.getItem(container._tpl)[1];
const containerFS2D = inventoryHelper.getContainerMap(
containerTemplate._props.Grids[0]._props.cellsH,
containerTemplate._props.Grids[0]._props.cellsV,
pmcData.Inventory.items,
containerId
);
const canPlaceResult = inventoryHelper.canPlaceItemInContainer(
jsonUtil.clone(containerFS2D), // will change the array so clone it
itemWithModsToAddClone
);
// In 3.8.3 canPlaceItemInContainer is wonky and returns undefined when the answer is yes
if (canPlaceResult === undefined) {
// At this point everything should succeed
inventoryHelper.placeItemInContainer(
containerFS2D,
itemWithModsToAddClone,
containerId,
slotId
);
// protected function, bypass typescript
inventoryHelper["setFindInRaidStatusForItem"](
itemWithModsToAddClone,
request.foundInRaid
);
// Add item + mods to output and profile inventory
output.profileChanges[sessionId].items.new.push(...itemWithModsToAddClone);
pmcData.Inventory.items.push(...itemWithModsToAddClone);
this.logger.debug(
`Added ${itemWithModsToAddClone[0].upd?.StackObjectsCount ?? 1} item: ${
itemWithModsToAddClone[0]._tpl
} with: ${itemWithModsToAddClone.length - 1} mods to ${containerId}`
);
return;
}
}
}
return original.call(inventoryHelper, sessionId, request, pmcData, output);
};
},
{ frequency: "Always" }
);
}
staticRouterModService.registerStaticRouter(
"UIFixesRoutes",
[