This commit is contained in:
Dev 2024-02-25 11:18:01 +00:00
commit 2839ce8c77
5 changed files with 115 additions and 40 deletions

View File

@ -592,24 +592,32 @@ export class HideoutController
const recipe = this.databaseServer.getTables().hideout.production.find((p) => p._id === body.recipeId); const recipe = this.databaseServer.getTables().hideout.production.find((p) => p._id === body.recipeId);
// Find the actual amount of items we need to remove because body can send weird data // Find the actual amount of items we need to remove because body can send weird data
const recipeRequirementsClone = this.jsonUtil.clone(recipe.requirements.filter((i) => i.type === "Item")); const recipeRequirementsClone = this.jsonUtil.clone(recipe.requirements.filter((i) => i.type === "Item" || i.type === "Tool"));
const output = this.eventOutputHolder.getOutput(sessionID); const output = this.eventOutputHolder.getOutput(sessionID);
const itemsToDelete = body.items.concat(body.tools);
for (const itemToDelete of body.items) for (const itemToDelete of itemsToDelete)
{ {
const itemToCheck = pmcData.Inventory.items.find((i) => i._id === itemToDelete.id); const itemToCheck = pmcData.Inventory.items.find((i) => i._id === itemToDelete.id);
const requirement = recipeRequirementsClone.find((requirement) => const requirement = recipeRequirementsClone.find((requirement) =>
requirement.templateId === itemToCheck._tpl requirement.templateId === itemToCheck._tpl
); );
if (requirement.count <= 0)
// Handle tools not having a `count`, but always only requiring 1
const requiredCount = requirement.count ?? 1;
if (requiredCount <= 0)
{ {
continue; continue;
} }
this.inventoryHelper.removeItemByCount(pmcData, itemToDelete.id, requirement.count, sessionID, output); this.inventoryHelper.removeItemByCount(pmcData, itemToDelete.id, requiredCount, sessionID, output);
// Tools don't have a count
if (requirement.type !== "Tool")
{
requirement.count -= itemToDelete.count; requirement.count -= itemToDelete.count;
} }
}
return output; return output;
} }
@ -793,6 +801,46 @@ export class HideoutController
output: IItemEventRouterResponse, output: IItemEventRouterResponse,
): void ): void
{ {
// Validate that we have a matching production
const productionDict = Object.entries(pmcData.Hideout.Production);
let prodId: string;
for (const [productionId, production] of productionDict)
{
// Skip null production objects
if (!production)
{
continue;
}
if (this.hideoutHelper.isProductionType(production))
{
// Production or ScavCase
if (production.RecipeId === request.recipeId)
{
prodId = productionId; // Set to objects key
break;
}
}
}
// If we're unable to find the production, send an error to the client
if (prodId === undefined)
{
this.logger.error(
this.localisationService.getText(
"hideout-unable_to_find_production_in_profile_by_recipie_id",
request.recipeId,
),
);
this.httpResponse.appendErrorToOutput(output, this.localisationService.getText(
"hideout-unable_to_find_production_in_profile_by_recipie_id",
request.recipeId,
));
return;
}
// Variables for managemnet of skill // Variables for managemnet of skill
let craftingExpAmount = 0; let craftingExpAmount = 0;
@ -865,40 +913,27 @@ export class HideoutController
} }
} }
// Loops over all current productions on profile - we want to find a matching production // Build an array of the tools that need to be returned to the player
const productionDict = Object.entries(pmcData.Hideout.Production); const toolsToSendToPlayer: Item[][] = [];
let prodId: string; const production = pmcData.Hideout.Production[prodId];
for (const production of productionDict) if (production.sptRequiredTools?.length > 0)
{ {
// Skip null production objects for (const tool of production.sptRequiredTools)
if (!production[1])
{ {
continue; const toolToAdd: Item = {
} _id: this.hashUtil.generate(),
_tpl: tool,
};
if (this.hideoutHelper.isProductionType(production[1])) if (this.itemHelper.isItemTplStackable(tool))
{ {
// Production or ScavCase toolToAdd.upd = {
if ((production[1] as Production).RecipeId === request.recipeId) StackObjectsCount: 1,
{
prodId = production[0]; // Set to objects key
break;
}
} }
} }
if (prodId === undefined) toolsToSendToPlayer.push([toolToAdd]);
{ }
this.logger.error(
this.localisationService.getText(
"hideout-unable_to_find_production_in_profile_by_recipie_id",
request.recipeId,
),
);
this.httpResponse.appendErrorToOutput(output);
return;
} }
// Check if the recipe is the same as the last one - get bonus when crafting same thing multiple times // Check if the recipe is the same as the last one - get bonus when crafting same thing multiple times
@ -920,15 +955,34 @@ export class HideoutController
hoursCrafting -= this.hideoutConfig.hoursForSkillCrafting * multiplierCrafting; hoursCrafting -= this.hideoutConfig.hoursForSkillCrafting * multiplierCrafting;
} }
// Create request for what we want to add to stash // Make sure we can fit both the craft result and tools in the stash
const totalResultItems = toolsToSendToPlayer.concat(itemAndChildrenToSendToPlayer);
if (!this.inventoryHelper.canPlaceItemsInInventory(sessionID, totalResultItems))
{
this.httpResponse.appendErrorToOutput(output, this.localisationService.getText("inventory-no_stash_space"));
return;
}
// Add the used tools to the stash as non-FiR
const addToolsRequest: IAddItemsDirectRequest = {
itemsWithModsToAdd: toolsToSendToPlayer,
foundInRaid: false,
useSortingTable: false,
callback: null,
};
this.inventoryHelper.addItemsToStash(sessionID, addToolsRequest, pmcData, output);
if (output.warnings.length > 0)
{
return;
}
// Add the crafting result to the stash, marked as FiR
const addItemsRequest: IAddItemsDirectRequest = { const addItemsRequest: IAddItemsDirectRequest = {
itemsWithModsToAdd: itemAndChildrenToSendToPlayer, itemsWithModsToAdd: itemAndChildrenToSendToPlayer,
foundInRaid: true, foundInRaid: true,
useSortingTable: false, useSortingTable: false,
callback: null, callback: null,
}; };
// Add FiR crafted items(s) to player inventory
this.inventoryHelper.addItemsToStash(sessionID, addItemsRequest, pmcData, output); this.inventoryHelper.addItemsToStash(sessionID, addItemsRequest, pmcData, output);
if (output.warnings.length > 0) if (output.warnings.length > 0)
{ {

View File

@ -2,12 +2,14 @@ import { inject, injectable } from "tsyringe";
import { PresetHelper } from "@spt-aki/helpers/PresetHelper"; import { PresetHelper } from "@spt-aki/helpers/PresetHelper";
import { IPreset } from "@spt-aki/models/eft/common/IGlobals"; import { IPreset } from "@spt-aki/models/eft/common/IGlobals";
import { ILogger } from "@spt-aki/models/spt/utils/ILogger";
import { DatabaseServer } from "@spt-aki/servers/DatabaseServer"; import { DatabaseServer } from "@spt-aki/servers/DatabaseServer";
@injectable() @injectable()
export class PresetController export class PresetController
{ {
constructor( constructor(
@inject("WinstonLogger") protected logger: ILogger,
@inject("PresetHelper") protected presetHelper: PresetHelper, @inject("PresetHelper") protected presetHelper: PresetHelper,
@inject("DatabaseServer") protected databaseServer: DatabaseServer, @inject("DatabaseServer") protected databaseServer: DatabaseServer,
) )
@ -15,11 +17,17 @@ export class PresetController
public initialize(): void public initialize(): void
{ {
const presets: IPreset[] = Object.values(this.databaseServer.getTables().globals.ItemPresets); const presets: [string, IPreset][] = Object.entries(this.databaseServer.getTables().globals.ItemPresets);
const reverse: Record<string, string[]> = {}; const reverse: Record<string, string[]> = {};
for (const preset of presets) for (const [id, preset] of presets)
{ {
if (id != preset._id)
{
this.logger.error(`Preset for template '${preset._items[0]._tpl}' has invalid id (${id} != ${preset._id}). Skipping`);
continue;
}
const tpl = preset._items[0]._tpl; const tpl = preset._items[0]._tpl;
if (!(tpl in reverse)) if (!(tpl in reverse))

View File

@ -95,11 +95,20 @@ export class HideoutHelper
modifiedProductionTime = 40; modifiedProductionTime = 40;
} }
pmcData.Hideout.Production[body.recipeId] = this.initProduction( const production = this.initProduction(
body.recipeId, body.recipeId,
modifiedProductionTime, modifiedProductionTime,
recipe.needFuelForAllProductionTime, recipe.needFuelForAllProductionTime,
); );
// Store the tools used for this production, so we can return them later
const productionTools = recipe.requirements.filter(req => req.type === "Tool").map(req => req.templateId);
if (productionTools.length > 0)
{
production.sptRequiredTools = productionTools;
}
pmcData.Hideout.Production[body.recipeId] = production;
} }
/** /**
@ -539,6 +548,7 @@ export class HideoutHelper
recipeId: HideoutHelper.waterCollector, recipeId: HideoutHelper.waterCollector,
Action: "HideoutSingleProductionStart", Action: "HideoutSingleProductionStart",
items: [], items: [],
tools: [],
timestamp: this.timeUtil.getTimestamp(), timestamp: this.timeUtil.getTimestamp(),
}; };

View File

@ -393,6 +393,8 @@ export interface Productive
sptIsComplete?: boolean; sptIsComplete?: boolean;
/** Is the craft a Continuous, e.g bitcoins/water collector */ /** Is the craft a Continuous, e.g bitcoins/water collector */
sptIsContinuous?: boolean; sptIsContinuous?: boolean;
/** Stores a list of tools used in this craft, to give back once the craft is done */
sptRequiredTools?: string[];
} }
export interface Production extends Productive export interface Production extends Productive

View File

@ -3,6 +3,7 @@ export interface IHideoutSingleProductionStartRequestData
Action: "HideoutSingleProductionStart"; Action: "HideoutSingleProductionStart";
recipeId: string; recipeId: string;
items: Item[]; items: Item[];
tools: Item[];
timestamp: number; timestamp: number;
} }