From 41f21421faba12e9e6e7345d9561714baaf83229 Mon Sep 17 00:00:00 2001 From: GetParanoid Date: Fri, 18 Jul 2025 17:37:04 -0500 Subject: [PATCH] feat(merge consumables) --- BepInEx/plugins/MergeConsumables.dll | Bin 0 -> 11264 bytes user/mods/lacyway-mergeconsumables/biome.json | 43 ++++++++ .../lacyway-mergeconsumables/package.json | 31 ++++++ .../src/CustomItemEventRouter.ts | 103 ++++++++++++++++++ user/mods/lacyway-mergeconsumables/src/mod.ts | 17 +++ 5 files changed, 194 insertions(+) create mode 100644 BepInEx/plugins/MergeConsumables.dll create mode 100644 user/mods/lacyway-mergeconsumables/biome.json create mode 100644 user/mods/lacyway-mergeconsumables/package.json create mode 100644 user/mods/lacyway-mergeconsumables/src/CustomItemEventRouter.ts create mode 100644 user/mods/lacyway-mergeconsumables/src/mod.ts diff --git a/BepInEx/plugins/MergeConsumables.dll b/BepInEx/plugins/MergeConsumables.dll new file mode 100644 index 0000000000000000000000000000000000000000..6fab10ed9dae508139e5046df17ac220042bc240 GIT binary patch literal 11264 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 { + 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();