diff --git a/BepInEx/plugins/MergeConsumables.dll b/BepInEx/plugins/MergeConsumables.dll new file mode 100644 index 0000000..6fab10e Binary files /dev/null and b/BepInEx/plugins/MergeConsumables.dll differ diff --git a/user/mods/lacyway-mergeconsumables/biome.json b/user/mods/lacyway-mergeconsumables/biome.json new file mode 100644 index 0000000..8ad2f86 --- /dev/null +++ b/user/mods/lacyway-mergeconsumables/biome.json @@ -0,0 +1,43 @@ +{ + "$schema": "https://biomejs.dev/schemas/1.7.1/schema.json", + "organizeImports": { + "enabled": true + }, + "formatter": { + "enabled": true, + "indentStyle": "space", + "indentWidth": 4, + "lineWidth": 320 + }, + "javascript": { + "parser": { + "unsafeParameterDecoratorsEnabled": true + } + }, + "linter": { + "enabled": true, + "rules": { + "recommended": false, + "complexity": { + "noStaticOnlyClass": "off" + }, + "correctness": { + "noUndeclaredVariables": "error", + "noUnusedVariables": "error" + }, + "performance": { + "noDelete": "warn" + }, + "style": { + "useImportType": "off", + "noNamespace": "error" + }, + "suspicious": { + "noEmptyBlockStatements": "error" + } + } + }, + "files": { + "ignore": ["**/node_modules", "dist", "types", "build.mjs", "**/package-lock.json"] + } +} diff --git a/user/mods/lacyway-mergeconsumables/package.json b/user/mods/lacyway-mergeconsumables/package.json new file mode 100644 index 0000000..7225dc9 --- /dev/null +++ b/user/mods/lacyway-mergeconsumables/package.json @@ -0,0 +1,31 @@ +{ + "name": "MergeConsumables", + "version": "1.3.0", + "sptVersion": "~3.11", + "loadBefore": [], + "loadAfter": [], + "incompatibilities": [], + "isBundleMod": false, + "main": "src/mod.js", + "scripts": { + "setup": "npm i", + "build": "node ./build.mjs", + "buildinfo": "node ./build.mjs --verbose" + }, + "devDependencies": { + "@types/node": "20.11", + "@typescript-eslint/eslint-plugin": "7.2", + "@typescript-eslint/parser": "7.2", + "archiver": "^6.0", + "eslint": "8.57", + "fs-extra": "11.2", + "ignore": "^5.2", + "tsyringe": "4.8.0", + "typescript": "5.4", + "winston": "3.12", + "@biomejs/biome": "^1.7.1" + }, + "author": "Lacyway", + "contributors": [], + "license": "CC BY-NC-ND 4.0" +} \ No newline at end of file diff --git a/user/mods/lacyway-mergeconsumables/src/CustomItemEventRouter.ts b/user/mods/lacyway-mergeconsumables/src/CustomItemEventRouter.ts new file mode 100644 index 0000000..67b8305 --- /dev/null +++ b/user/mods/lacyway-mergeconsumables/src/CustomItemEventRouter.ts @@ -0,0 +1,103 @@ +import { inject, injectable } from "tsyringe"; + +import { HandledRoute, ItemEventRouterDefinition } from "@spt/di/Router" +import { IPmcData } from "@spt/models/eft/common/IPmcData"; +import { IItemEventRouterResponse } from "@spt/models/eft/itemEvent/IItemEventRouterResponse"; +import { EventOutputHolder } from "@spt/routers/EventOutputHolder"; +import { InventoryHelper } from "@spt/helpers/InventoryHelper"; +import { ILogger } from "@spt/models/spt/utils/ILogger"; + +@injectable() +export class CustomInventoryItemEventRouter extends ItemEventRouterDefinition { + constructor( + @inject("WinstonLogger") protected logger: ILogger, + @inject("EventOutputHolder") protected eventOutputHolder: EventOutputHolder, + @inject("InventoryHelper") protected inventoryHelper: InventoryHelper, + ) { + super(); + } + + public override getHandledRoutes(): HandledRoute[] { + return [ + new HandledRoute("Combine", false) + ]; + } + + public override async handleItemEvent( + url: string, + pmcData: IPmcData, + body: any, + sessionID: string, + output: IItemEventRouterResponse, + ): Promise { + switch (url) { + case "Combine": + output = this.eventOutputHolder.getOutput(sessionID); + this.HandleCombine(pmcData, body.sourceItem, body.targetItem, body.sourceAmount, body.targetAmount, body.type, sessionID, output); + return output; + } + return output; + } + + private HandleCombine(pmcData: IPmcData, sourceItem: string, targetItem: string, sourceAmount: number, targetAmount: number, type: string, sessionID: string, output: IItemEventRouterResponse) { + const sItem = pmcData.Inventory.items.find((item) => item._id === sourceItem); + const tItem = pmcData.Inventory.items.find((item) => item._id === targetItem); + + if (!sItem || !tItem) { + this.logger.error(`Could not find source or target item! Source: ${sourceItem}, Target: ${targetItem}`); + return; + } + + switch (type) { + case "medical": + if (sItem.upd?.MedKit && tItem.upd?.MedKit) { + sItem.upd.MedKit.HpResource = sourceAmount; + tItem.upd.MedKit.HpResource = targetAmount; + } else if ((sItem._tpl === tItem._tpl)) { + + if (!sItem.upd) { + sItem.upd = {}; + } + if (!sItem.upd.MedKit) { + sItem.upd.MedKit = { HpResource: sourceAmount } + } else { + sItem.upd.MedKit.HpResource = sourceAmount; + } + if (!tItem.upd) { + tItem.upd = {}; + } + if (!tItem.upd.MedKit) { + tItem.upd.MedKit = { HpResource: targetAmount }; + } else { + tItem.upd.MedKit.HpResource = targetAmount; + } + + this.logger.warning("MedKit was missing on source or target item -- attempted resolve with item _tpl") + + } else { + this.logger.error("MedKit was missing on source or target item!"); + this.logger.error("tItem: " + JSON.stringify(tItem)); + this.logger.error("sItem: " + JSON.stringify(sItem)); + return; + } + break; + case "food": + if (sItem.upd?.FoodDrink && tItem?.upd.FoodDrink) { + sItem.upd.FoodDrink.HpPercent = sourceAmount; + tItem.upd.FoodDrink.HpPercent = targetAmount; + } + else { + this.logger.error("FoodDrink was missing on source or target item!"); + return; + } + break; + default: + this.logger.warning(`Unknown type: ${type}`); + break; + } + + if (sourceAmount == 0) { + this.inventoryHelper.removeItem(pmcData, sourceItem, sessionID, output); + } + } +} \ No newline at end of file diff --git a/user/mods/lacyway-mergeconsumables/src/mod.ts b/user/mods/lacyway-mergeconsumables/src/mod.ts new file mode 100644 index 0000000..e292fec --- /dev/null +++ b/user/mods/lacyway-mergeconsumables/src/mod.ts @@ -0,0 +1,17 @@ +import { DependencyContainer } from "tsyringe"; + +import { CustomInventoryItemEventRouter } from "./CustomItemEventRouter"; +import { IPreSptLoadMod } from "@spt/models/external/IPreSptLoadMod"; + +class Mod implements IPreSptLoadMod { + preSptLoad(container: DependencyContainer): void { + container.registerType("IERouters", "CustomInventoryItemEventRouter"); + + container.register("CustomInventoryItemEventRouter", { + useClass: CustomInventoryItemEventRouter + }) + } + +} + +export const mod = new Mod();