Returning tools to containers; server config
This commit is contained in:
69
Patches/PutToolsBackPatch.cs
Normal file
69
Patches/PutToolsBackPatch.cs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -55,6 +55,7 @@ namespace UIFixes
|
|||||||
UnloadAmmoPatches.Enable();
|
UnloadAmmoPatches.Enable();
|
||||||
new FixTraderControllerSimulateFalsePatch().Enable();
|
new FixTraderControllerSimulateFalsePatch().Enable();
|
||||||
LoadMultipleMagazinesPatches.Enable();
|
LoadMultipleMagazinesPatches.Enable();
|
||||||
|
new PutToolsBackPatch().Enable();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool InRaid()
|
public static bool InRaid()
|
||||||
|
3
server/config/config.json
Normal file
3
server/config/config.json
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"putToolsBack": true
|
||||||
|
}
|
@@ -1,9 +1,13 @@
|
|||||||
import type { DependencyContainer } from "tsyringe";
|
import type { DependencyContainer } from "tsyringe";
|
||||||
|
|
||||||
import type { InventoryController } from "@spt-aki/controllers/InventoryController";
|
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 { 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 { ProfileHelper } from "@spt-aki/helpers/ProfileHelper";
|
||||||
import type { RagfairSortHelper } from "@spt-aki/helpers/RagfairSortHelper";
|
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 type { IRagfairOffer } from "@spt-aki/models/eft/ragfair/IRagfairOffer";
|
||||||
import { Money } from "@spt-aki/models/enums/Money";
|
import { Money } from "@spt-aki/models/enums/Money";
|
||||||
import type { IPreAkiLoadMod } from "@spt-aki/models/external/IPreAkiLoadMod";
|
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 { StaticRouterModService } from "@spt-aki/services/mod/staticRouter/StaticRouterModService";
|
||||||
import type { JsonUtil } from "@spt-aki/utils/JsonUtil";
|
import type { JsonUtil } from "@spt-aki/utils/JsonUtil";
|
||||||
|
|
||||||
|
import config from "../config/config.json";
|
||||||
|
|
||||||
class UIFixes implements IPreAkiLoadMod {
|
class UIFixes implements IPreAkiLoadMod {
|
||||||
private databaseServer: DatabaseServer;
|
private databaseServer: DatabaseServer;
|
||||||
private logger: ILogger;
|
private logger: ILogger;
|
||||||
@@ -21,6 +27,7 @@ class UIFixes implements IPreAkiLoadMod {
|
|||||||
this.logger = container.resolve<ILogger>("WinstonLogger");
|
this.logger = container.resolve<ILogger>("WinstonLogger");
|
||||||
|
|
||||||
const profileHelper = container.resolve<ProfileHelper>("ProfileHelper");
|
const profileHelper = container.resolve<ProfileHelper>("ProfileHelper");
|
||||||
|
const itemHelper = container.resolve<ItemHelper>("ItemHelper");
|
||||||
const staticRouterModService = container.resolve<StaticRouterModService>("StaticRouterModService");
|
const staticRouterModService = container.resolve<StaticRouterModService>("StaticRouterModService");
|
||||||
const jsonUtil = container.resolve<JsonUtil>("JsonUtil");
|
const jsonUtil = container.resolve<JsonUtil>("JsonUtil");
|
||||||
|
|
||||||
@@ -83,6 +90,100 @@ class UIFixes implements IPreAkiLoadMod {
|
|||||||
{ frequency: "Always" }
|
{ 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(
|
staticRouterModService.registerStaticRouter(
|
||||||
"UIFixesRoutes",
|
"UIFixesRoutes",
|
||||||
[
|
[
|
||||||
|
Reference in New Issue
Block a user