diff --git a/BepInEx/plugins/Kat.FastSellInFlea.dll b/BepInEx/plugins/Kat.FastSellInFlea.dll new file mode 100644 index 0000000..589a38c Binary files /dev/null and b/BepInEx/plugins/Kat.FastSellInFlea.dll differ diff --git a/BepInEx/plugins/Tyfon.UIFixes.Net.dll b/BepInEx/plugins/Tyfon.UIFixes.Net.dll new file mode 100644 index 0000000..8393634 Binary files /dev/null and b/BepInEx/plugins/Tyfon.UIFixes.Net.dll differ diff --git a/BepInEx/plugins/Tyfon.UIFixes.dll b/BepInEx/plugins/Tyfon.UIFixes.dll new file mode 100644 index 0000000..14105a8 Binary files /dev/null and b/BepInEx/plugins/Tyfon.UIFixes.dll differ diff --git a/user/mods/redlaser42-Increase Climb Height/config ReadMe.txt b/user/mods/redlaser42-Increase Climb Height/config ReadMe.txt new file mode 100644 index 0000000..5983341 --- /dev/null +++ b/user/mods/redlaser42-Increase Climb Height/config ReadMe.txt @@ -0,0 +1,42 @@ +Here are the game's defaults with my experience tweaking them: + + "InteractMaxHeight": 1.31, + This one setting is 99% of the mod. It's the height a climb will be attempted. You could bump this up to make things feel more grippy, but how high your character moves up won't change. + + "InteractMaxLength": 10, + This is how close in front of you a wall needs to be to attempt a climb. I left this at default. It didn't help get to more ledges because where you climb up to does not go further out. + + "MinDistantToInteract": 0.5, + IDK what this does but was with the other two. Didn't notice any effect. + + + "BaseJumpPenalty": 0.3, + Inertia penalty for jumping. I set this to 0 for off. + + "MaxOneHandAnimationHeight": 2.3, + How high a the one handed climb animation transitions into a two handed climb. One hands are slightly faster, so this could be boosted, but wouldn't above 3.5. I left this at default. + + "MaxWithoutHandHeight": 0.65, + How high you can do a no-handed step up. I left this at default. Looks wonky past 2 + + + "VaultingInputTime": 0.5, + I didn't notice an effect lowering this to 0. I could still vault as quick as I could press a key. + + "AnimationVerticalSpeed": 1.1, + "AnimationForwardSpeed": 0.8, + How fast you climb. Left at default. Adjust .1 at a time. + + + "GridOffsetX": 0, + "GridOffsetY": 0, + "GridOffsetZ": 0, + "GridSizeX": 0, + "GridSizeY": 1.8, + "GridSizeZ": 2, + "OffsetFactor": 0.1, + "SteppingLengthX": 0.1, + "SteppingLengthY": 0.1, + "SteppingLengthZ": 0.1 + Everything above is under "Grid Settings". I tweaked these settings, but it was hard to tell what was happening. + I didn't notice any effect except making the grid too small or large prevents you from climbing. Left all at defaults. diff --git a/user/mods/redlaser42-Increase Climb Height/config.json b/user/mods/redlaser42-Increase Climb Height/config.json new file mode 100644 index 0000000..72b8328 --- /dev/null +++ b/user/mods/redlaser42-Increase Climb Height/config.json @@ -0,0 +1,26 @@ +{ + "InteractMaxHeight": 1.72, + "InteractMaxLength": 10, + "MinDistantToInteract": 0.5, + + "BaseJumpPenalty": 0, + + "MaxOneHandAnimationHeight": 2.3, + "MaxWithoutHandHeight": 0.65, + + "VaultingInputTime": 0.5, + + "AnimationVerticalSpeed": 1.1, + "AnimationForwardSpeed": 0.8, + + "GridOffsetX": 0, + "GridOffsetY": 0, + "GridOffsetZ": 0, + "GridSizeX": 0, + "GridSizeY": 1.8, + "GridSizeZ": 2, + "OffsetFactor": 0.1, + "SteppingLengthX": 0.1, + "SteppingLengthY": 0.1, + "SteppingLengthZ": 0.1 +} diff --git a/user/mods/redlaser42-Increase Climb Height/package.json b/user/mods/redlaser42-Increase Climb Height/package.json new file mode 100644 index 0000000..496e374 --- /dev/null +++ b/user/mods/redlaser42-Increase Climb Height/package.json @@ -0,0 +1,32 @@ +{ + "name": "Increase Climb Height", + "version": "1.2.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" + }, + "author": "redlaser42", + "contributors": [], + "license": "MIT", + "dependencies": { + } +} diff --git a/user/mods/redlaser42-Increase Climb Height/src/mod.js b/user/mods/redlaser42-Increase Climb Height/src/mod.js new file mode 100644 index 0000000..7265cc1 --- /dev/null +++ b/user/mods/redlaser42-Increase Climb Height/src/mod.js @@ -0,0 +1,58 @@ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.mod = void 0; +const config_json_1 = __importDefault(require("./../config.json")); +const cfMaxLength = config_json_1.default.InteractMaxLength; +const cfMaxHeight = config_json_1.default.InteractMaxHeight; +const cfMinDistantToInteract = config_json_1.default.MinDistantToInteract; +const cfMaxOneHandHeight = config_json_1.default.MaxOneHandAnimationHeight; +const cfMaxWithoutHandHeight = config_json_1.default.MaxWithoutHandHeight; +const cfBaseJumpPenalty = config_json_1.default.BaseJumpPenalty; +const cfVaultingInputTime = config_json_1.default.VaultingInputTime; +const cfSpeedRangeX = config_json_1.default.AnimationForwardSpeed; +const cfSpeedRangeY = config_json_1.default.AnimationVerticalSpeed; +const cfGridOffsetX = config_json_1.default.GridOffsetX; +const cfGridOffsetY = config_json_1.default.GridOffsetY; +const cfGridOffsetZ = config_json_1.default.GridOffsetZ; +const cfGridSizeX = config_json_1.default.GridSizeX; +const cfGridSizeY = config_json_1.default.GridSizeY; +const cfGridSizeZ = config_json_1.default.GridSizeZ; +const cfOffsetFactor = config_json_1.default.OffsetFactor; +const cfSteppingLengthX = config_json_1.default.SteppingLengthX; +const cfSteppingLengthY = config_json_1.default.SteppingLengthY; +const cfSteppingLengthZ = config_json_1.default.SteppingLengthZ; +class Mod { + postDBLoad(container) { + // get database from server + const databaseServer = container.resolve("DatabaseServer"); + // Get all the in-memory json found in /assets/database + const tables = databaseServer.getTables(); + tables.globals.config.VaultingSettings.MovesSettings.ClimbSettings.MoveRestrictions.MaxLength = cfMaxLength; + tables.globals.config.VaultingSettings.MovesSettings.ClimbSettings.MoveRestrictions.MaxHeight = cfMaxHeight; + tables.globals.config.VaultingSettings.MovesSettings.ClimbSettings.MoveRestrictions.MinDistantToInteract = cfMinDistantToInteract; + tables.globals.config.VaultingSettings.MovesSettings.ClimbSettings.AutoMoveRestrictions.MaxLength = cfMaxLength; + tables.globals.config.VaultingSettings.MovesSettings.ClimbSettings.AutoMoveRestrictions.MaxHeight = cfMaxHeight; + tables.globals.config.VaultingSettings.MovesSettings.ClimbSettings.AutoMoveRestrictions.MinDistantToInteract = cfMinDistantToInteract; + tables.globals.config.VaultingSettings.MovesSettings.ClimbSettings.SpeedRange.x = cfSpeedRangeX; + tables.globals.config.VaultingSettings.MovesSettings.ClimbSettings.SpeedRange.y = cfSpeedRangeY; + tables.globals.config.VaultingSettings.MovesSettings.ClimbSettings.MaxOneHandHeight = cfMaxOneHandHeight; + tables.globals.config.VaultingSettings.MovesSettings.ClimbSettings.MaxWithoutHandHeight = cfMaxWithoutHandHeight; + tables.globals.config.Inertia.BaseJumpPenalty = cfBaseJumpPenalty; + tables.globals.config.VaultingSettings.GridSettings.SteppingLengthY = cfSteppingLengthY; + tables.globals.config.VaultingSettings.GridSettings.SteppingLengthX = cfSteppingLengthX; + tables.globals.config.VaultingSettings.GridSettings.SteppingLengthZ = cfSteppingLengthZ; + tables.globals.config.VaultingSettings.GridSettings.GridOffsetY = cfGridOffsetY; + tables.globals.config.VaultingSettings.GridSettings.GridOffsetX = cfGridOffsetX; + tables.globals.config.VaultingSettings.GridSettings.GridOffsetZ = cfGridOffsetZ; + tables.globals.config.VaultingSettings.GridSettings.GridSizeY = cfGridSizeY; + tables.globals.config.VaultingSettings.GridSettings.GridSizeX = cfGridSizeX; + tables.globals.config.VaultingSettings.GridSettings.GridSizeZ = cfGridSizeZ; + tables.globals.config.VaultingSettings.GridSettings.OffsetFactor = cfOffsetFactor; + tables.globals.config.VaultingSettings.VaultingInputTime = cfVaultingInputTime; + } +} +exports.mod = new Mod(); +//# sourceMappingURL=mod.js.map \ No newline at end of file diff --git a/user/mods/redlaser42-Increase Climb Height/src/mod.js.map b/user/mods/redlaser42-Increase Climb Height/src/mod.js.map new file mode 100644 index 0000000..331640f --- /dev/null +++ b/user/mods/redlaser42-Increase Climb Height/src/mod.js.map @@ -0,0 +1,10 @@ +{ + "version": 3, + "file": "mod.js", + "sourceRoot": "", + "sources": [ + "mod.ts" + ], + "names": [], + "mappings": "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" +} \ No newline at end of file diff --git a/user/mods/redlaser42-Increase Climb Height/src/mod.ts b/user/mods/redlaser42-Increase Climb Height/src/mod.ts new file mode 100644 index 0000000..813e3ed --- /dev/null +++ b/user/mods/redlaser42-Increase Climb Height/src/mod.ts @@ -0,0 +1,68 @@ +import { DependencyContainer } from "tsyringe"; + +import { IPostDBLoadMod } from "@spt/models/external/IPostDBLoadMod"; +import { DatabaseServer } from "@spt/servers/DatabaseServer"; +import { IDatabaseTables } from "@spt/models/spt/server/IDatabaseTables"; + +import config from "./../config.json"; + +const cfMaxLength = config.InteractMaxLength; +const cfMaxHeight = config.InteractMaxHeight; +const cfMinDistantToInteract = config.MinDistantToInteract; +const cfMaxOneHandHeight = config.MaxOneHandAnimationHeight; +const cfMaxWithoutHandHeight = config.MaxWithoutHandHeight; +const cfBaseJumpPenalty = config.BaseJumpPenalty; +const cfVaultingInputTime = config.VaultingInputTime; +const cfSpeedRangeX = config.AnimationForwardSpeed; +const cfSpeedRangeY = config.AnimationVerticalSpeed; + +const cfGridOffsetX = config.GridOffsetX; +const cfGridOffsetY = config.GridOffsetY; +const cfGridOffsetZ = config.GridOffsetZ; +const cfGridSizeX = config.GridSizeX; +const cfGridSizeY = config.GridSizeY; +const cfGridSizeZ = config.GridSizeZ; +const cfOffsetFactor = config.OffsetFactor; +const cfSteppingLengthX = config.SteppingLengthX; +const cfSteppingLengthY = config.SteppingLengthY; +const cfSteppingLengthZ = config.SteppingLengthZ; + +class Mod implements IPostDBLoadMod { + public postDBLoad(container: DependencyContainer): void { + // get database from server + const databaseServer = container.resolve("DatabaseServer"); + + // Get all the in-memory json found in /assets/database + const tables: IDatabaseTables = databaseServer.getTables(); + + tables.globals.config.VaultingSettings.MovesSettings.ClimbSettings.MoveRestrictions.MaxLength = cfMaxLength; + tables.globals.config.VaultingSettings.MovesSettings.ClimbSettings.MoveRestrictions.MaxHeight = cfMaxHeight; + tables.globals.config.VaultingSettings.MovesSettings.ClimbSettings.MoveRestrictions.MinDistantToInteract = cfMinDistantToInteract; + tables.globals.config.VaultingSettings.MovesSettings.ClimbSettings.AutoMoveRestrictions.MaxLength = cfMaxLength; + tables.globals.config.VaultingSettings.MovesSettings.ClimbSettings.AutoMoveRestrictions.MaxHeight = cfMaxHeight; + tables.globals.config.VaultingSettings.MovesSettings.ClimbSettings.AutoMoveRestrictions.MinDistantToInteract = cfMinDistantToInteract; + + tables.globals.config.VaultingSettings.MovesSettings.ClimbSettings.SpeedRange.x = cfSpeedRangeX; + tables.globals.config.VaultingSettings.MovesSettings.ClimbSettings.SpeedRange.y = cfSpeedRangeY; + + tables.globals.config.VaultingSettings.MovesSettings.ClimbSettings.MaxOneHandHeight = cfMaxOneHandHeight; + tables.globals.config.VaultingSettings.MovesSettings.ClimbSettings.MaxWithoutHandHeight = cfMaxWithoutHandHeight; + + tables.globals.config.Inertia.BaseJumpPenalty = cfBaseJumpPenalty; + + tables.globals.config.VaultingSettings.GridSettings.SteppingLengthY = cfSteppingLengthY; + tables.globals.config.VaultingSettings.GridSettings.SteppingLengthX = cfSteppingLengthX; + tables.globals.config.VaultingSettings.GridSettings.SteppingLengthZ = cfSteppingLengthZ; + tables.globals.config.VaultingSettings.GridSettings.GridOffsetY = cfGridOffsetY; + tables.globals.config.VaultingSettings.GridSettings.GridOffsetX = cfGridOffsetX; + tables.globals.config.VaultingSettings.GridSettings.GridOffsetZ = cfGridOffsetZ; + tables.globals.config.VaultingSettings.GridSettings.GridSizeY = cfGridSizeY; + tables.globals.config.VaultingSettings.GridSettings.GridSizeX = cfGridSizeX; + tables.globals.config.VaultingSettings.GridSettings.GridSizeZ = cfGridSizeZ; + tables.globals.config.VaultingSettings.GridSettings.OffsetFactor = cfOffsetFactor; + + tables.globals.config.VaultingSettings.VaultingInputTime = cfVaultingInputTime; + } +} + +export const mod = new Mod(); \ No newline at end of file diff --git a/user/mods/tyfon-uifixes/config/config.json b/user/mods/tyfon-uifixes/config/config.json new file mode 100644 index 0000000..b5f1467 --- /dev/null +++ b/user/mods/tyfon-uifixes/config/config.json @@ -0,0 +1,3 @@ +{ + "putToolsBack": true +} diff --git a/user/mods/tyfon-uifixes/package.json b/user/mods/tyfon-uifixes/package.json new file mode 100644 index 0000000..8e428f2 --- /dev/null +++ b/user/mods/tyfon-uifixes/package.json @@ -0,0 +1,32 @@ +{ + "name": "uifixes", + "version": "4.2.1", + "main": "src/mod.js", + "license": "MIT", + "author": "Tyfon", + "sptVersion": "~3.11", + "loadBefore": [], + "loadAfter": [], + "incompatibilities": [], + "contributors": [], + "isBundleMod": false, + "scripts": { + "setup": "npm i", + "build": "node ./build.mjs", + "buildinfo": "node ./build.mjs --verbose" + }, + "devDependencies": { + "@types/node": "22.12.0", + "@typescript-eslint/eslint-plugin": "7.2", + "@typescript-eslint/parser": "7.2", + "archiver": "^6.0", + "eslint": "8.57", + "eslint-config-prettier": "^9.1.0", + "eslint-plugin-prettier": "^5.1.3", + "fs-extra": "11.2", + "ignore": "^5.2", + "tsyringe": "4.8.0", + "typescript": "5.8.2", + "winston": "3.12" + } +} diff --git a/user/mods/tyfon-uifixes/src/assortUnlocks.ts b/user/mods/tyfon-uifixes/src/assortUnlocks.ts new file mode 100644 index 0000000..faa7a2d --- /dev/null +++ b/user/mods/tyfon-uifixes/src/assortUnlocks.ts @@ -0,0 +1,57 @@ +import type { DependencyContainer } from "tsyringe"; + +import type { ILogger } from "@spt/models/spt/utils/ILogger"; +import type { DatabaseService } from "@spt/services/DatabaseService"; +import type { StaticRouterModService } from "@spt/services/mod/staticRouter/StaticRouterModService"; + +export const assortUnlocks = (container: DependencyContainer) => { + const logger = container.resolve("PrimaryLogger"); + const staticRouterModService = container.resolve("StaticRouterModService"); + const databaseService = container.resolve("DatabaseService"); + + const loadAssortmentUnlocks = () => { + const traders = databaseService.getTraders(); + const quests = databaseService.getQuests(); + const result: Record = {}; + + for (const traderId in traders) { + const trader = traders[traderId]; + if (trader.questassort) { + for (const questStatus in trader.questassort) { + // Explicitly check that quest status is an expected value - some mods accidently import in such a way that adds a "default" value + if (!["started", "success", "fail"].includes(questStatus)) { + continue; + } + + for (const assortId in trader.questassort[questStatus]) { + const questId = trader.questassort[questStatus][assortId]; + + if (!quests[questId]) { + logger.warning( + `UIFixes: Trader ${traderId} questassort references unknown quest ${JSON.stringify(questId)}! Check that whatever mod added that trader and/or quest is installed correctly.` + ); + continue; + } + + result[assortId] = quests[questId].name; + } + } + } + } + + return result; + }; + + staticRouterModService.registerStaticRouter( + "UIFixesRoutes", + [ + { + url: "/uifixes/assortUnlocks", + action: async (url, info, sessionId, output) => { + return JSON.stringify(loadAssortmentUnlocks()); + } + } + ], + "custom-static-ui-fixes" + ); +}; diff --git a/user/mods/tyfon-uifixes/src/keepQuickBinds.ts b/user/mods/tyfon-uifixes/src/keepQuickBinds.ts new file mode 100644 index 0000000..1f02aaf --- /dev/null +++ b/user/mods/tyfon-uifixes/src/keepQuickBinds.ts @@ -0,0 +1,39 @@ +import type { DependencyContainer } from "tsyringe"; + +import type { InRaidHelper } from "@spt/helpers/InRaidHelper"; +import type { ILogger } from "@spt/models/spt/utils/ILogger"; +import type { ICloner } from "@spt/utils/cloners/ICloner"; + +export const keepQuickBinds = (container: DependencyContainer) => { + const logger = container.resolve("PrimaryLogger"); + const cloner = container.resolve("RecursiveCloner"); + + container.afterResolution( + "InRaidHelper", + (_, inRaidHelper: InRaidHelper) => { + const original = inRaidHelper.deleteInventory; + + inRaidHelper.deleteInventory = (pmcData, sessionId) => { + // Copy the existing quickbinds + const fastPanel = cloner.clone(pmcData.Inventory.fastPanel); + + // Nukes the inventory and the fastpanel + const result = original.call(inRaidHelper, pmcData, sessionId); + + // Restore the quickbinds for items that still exist + try { + for (const index in fastPanel) { + if (pmcData.Inventory.items.find(i => i._id == fastPanel[index])) { + pmcData.Inventory.fastPanel[index] = fastPanel[index]; + } + } + } catch (error) { + logger.error(`UIFixes: Failed to restore quickbinds\n ${error}`); + } + + return result; + }; + }, + { frequency: "Always" } + ); +}; diff --git a/user/mods/tyfon-uifixes/src/linkedSlotSearch.ts b/user/mods/tyfon-uifixes/src/linkedSlotSearch.ts new file mode 100644 index 0000000..9888323 --- /dev/null +++ b/user/mods/tyfon-uifixes/src/linkedSlotSearch.ts @@ -0,0 +1,61 @@ +import type { DependencyContainer } from "tsyringe"; + +import type { ItemHelper } from "@spt/helpers/ItemHelper"; +import type { ITemplateItem } from "@spt/models/eft/common/tables/ITemplateItem"; +import type { ILogger } from "@spt/models/spt/utils/ILogger"; +import type { RagfairLinkedItemService } from "@spt/services/RagfairLinkedItemService"; +import type { DatabaseService } from "@spt/services/DatabaseService"; + +export const linkedSlotSearch = (container: DependencyContainer) => { + const logger = container.resolve("PrimaryLogger"); + const itemHelper = container.resolve("ItemHelper"); + const databaseService = container.resolve("DatabaseService"); + + container.afterResolution( + "RagfairLinkedItemService", + (_, linkedItemService: RagfairLinkedItemService) => { + const original = linkedItemService.getLinkedItems; + + linkedItemService.getLinkedItems = linkedSearchId => { + const [tpl, slotName] = linkedSearchId.split(":", 2); + + if (slotName) { + logger.info(`UIFixes: Finding items for specific slot ${tpl}:${slotName}`); + const allItems = databaseService.getItems(); + const resultSet = getSpecificFilter(allItems[tpl], slotName); + + // Default Inventory, for equipment slots + if (tpl === "55d7217a4bdc2d86028b456d") { + const categories = [...resultSet]; + const items = Object.keys(allItems).filter(tpl => itemHelper.isOfBaseclasses(tpl, categories)); + + // Send the categories along too, some of them might actually be items + return new Set(items.concat(categories)); + } + + return resultSet; + } + + return original.call(linkedItemService, tpl); + }; + }, + { frequency: "Always" } + ); +}; + +const getSpecificFilter = (item: ITemplateItem, slotName: string): Set => { + const results = new Set(); + + // For whatever reason, all chamber slots have the name "patron_in_weapon" + const groupName = slotName === "patron_in_weapon" ? "Chambers" : "Slots"; + const group = item._props[groupName] ?? []; + + const sub = group.find(slot => slot._name === slotName); + for (const filter of sub?._props?.filters ?? []) { + for (const f of filter.Filter) { + results.add(f); + } + } + + return results; +}; diff --git a/user/mods/tyfon-uifixes/src/mod.ts b/user/mods/tyfon-uifixes/src/mod.ts new file mode 100644 index 0000000..fc2417c --- /dev/null +++ b/user/mods/tyfon-uifixes/src/mod.ts @@ -0,0 +1,30 @@ +import type { DependencyContainer } from "tsyringe"; + +import type { IPreSptLoadMod } from "@spt/models/external/IPreSptLoadMod"; + +import { assortUnlocks } from "./assortUnlocks"; +import { keepQuickBinds } from "./keepQuickBinds"; +import { linkedSlotSearch } from "./linkedSlotSearch"; +import { putToolsBack } from "./putToolsBack"; + +import config from "../config/config.json"; + +class UIFixes implements IPreSptLoadMod { + public preSptLoad(container: DependencyContainer): void { + // Keep quickbinds for items that aren't actually lost on death + keepQuickBinds(container); + + // Better tool return - starting production + if (config.putToolsBack) { + putToolsBack(container); + } + + // Slot-specific linked search + linkedSlotSearch(container); + + // Show unlocking quest on locked offers + assortUnlocks(container); + } +} + +export const mod = new UIFixes(); diff --git a/user/mods/tyfon-uifixes/src/putToolsBack.ts b/user/mods/tyfon-uifixes/src/putToolsBack.ts new file mode 100644 index 0000000..9bf504d --- /dev/null +++ b/user/mods/tyfon-uifixes/src/putToolsBack.ts @@ -0,0 +1,166 @@ +import type { DependencyContainer } from "tsyringe"; + +import type { HideoutHelper } from "@spt/helpers/HideoutHelper"; +import type { InventoryHelper } from "@spt/helpers/InventoryHelper"; +import type { ItemHelper } from "@spt/helpers/ItemHelper"; +import type { IPmcData } from "@spt/models/eft/common/IPmcData"; +import type { IItem } from "@spt/models/eft/common/tables/IItem"; +import type { IHideoutSingleProductionStartRequestData } from "@spt/models/eft/hideout/IHideoutSingleProductionStartRequestData"; +import type { ILogger } from "@spt/models/spt/utils/ILogger"; +import type { ICloner } from "@spt/utils/cloners/ICloner"; + +const returnToProperty = "uifixes.returnTo"; + +export const putToolsBack = (container: DependencyContainer) => { + const logger = container.resolve("PrimaryLogger"); + const cloner = container.resolve("RecursiveCloner"); + const itemHelper = container.resolve("ItemHelper"); + + 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 + try { + 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); + + // If the tool is in the stash itself, skip it. Same check as InventoryHelper.isItemInStash + if ( + originalTool.parentId === pmcData.Inventory.stash && + originalTool.slotId === "hideout" + ) { + continue; + } + + tools[i][returnToProperty] = [originalTool.parentId, originalTool.slotId]; + } + } + } catch (error) { + logger.error(`UIFixes: Failed to save tool origin\n ${error}`); + } + + 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 = cloner.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[returnToProperty]) { + try { + const [containerId, slotId] = tool[returnToProperty]; + + // Clean the item + delete tool[returnToProperty]; + + const [foundContainerFS2D, foundSlotId] = findGridFS2DForItems( + inventoryHelper, + containerId, + slotId, + itemWithModsToAddClone, + pmcData + ); + + if (foundContainerFS2D) { + // At this point everything should succeed + inventoryHelper.placeItemInContainer( + foundContainerFS2D, + itemWithModsToAddClone, + containerId, + foundSlotId + ); + + // 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); + + logger.debug( + `Added ${itemWithModsToAddClone[0].upd?.StackObjectsCount ?? 1} item: ${ + itemWithModsToAddClone[0]._tpl + } with: ${itemWithModsToAddClone.length - 1} mods to ${containerId}` + ); + + return; + } + } catch (error) { + logger.error(`UIFixes: Encounted an error trying to put tool back.\n ${error}`); + } + + logger.info("UIFixes: Unable to put tool back in its original container, returning it to stash."); + } + + return original.call(inventoryHelper, sessionId, request, pmcData, output); + }; + }, + { frequency: "Always" } + ); + + function findGridFS2DForItems( + inventoryHelper: InventoryHelper, + containerId: string, + startingGrid: string, + items: IItem[], + pmcData: IPmcData + ): [number[][], string] | undefined { + const container = pmcData.Inventory.items.find(x => x._id === containerId); + if (!container) { + return; + } + + const [foundTemplate, containerTemplate] = itemHelper.getItem(container._tpl); + if (!foundTemplate || !containerTemplate) { + return; + } + + let originalGridIndex = containerTemplate._props.Grids.findIndex(g => g._name === startingGrid); + if (originalGridIndex < 0) { + originalGridIndex = 0; + } + + // Loop through grids, starting from the original grid + for ( + let gridIndex = originalGridIndex; + gridIndex < containerTemplate._props.Grids.length + originalGridIndex; + gridIndex++ + ) { + const grid = containerTemplate._props.Grids[gridIndex % containerTemplate._props.Grids.length]; + const gridItems = pmcData.Inventory.items.filter( + x => x.parentId === containerId && x.slotId === grid._name + ); + + const containerFS2D = inventoryHelper.getContainerMap( + grid._props.cellsH, + grid._props.cellsV, + gridItems, + containerId + ); + + // will change the array so clone it + if (inventoryHelper.canPlaceItemInContainer(cloner.clone(containerFS2D), items)) { + return [containerFS2D, grid._name]; + } + } + } +};