0166e30dd1
Reduce instances of `IItemEventRouterResponse` being reassigned in a function Rename `getMoney` to `giveProfileMoney`
1216 lines
46 KiB
TypeScript
1216 lines
46 KiB
TypeScript
import { inject, injectable } from "tsyringe";
|
|
|
|
import { ScavCaseRewardGenerator } from "@spt-aki/generators/ScavCaseRewardGenerator";
|
|
import { HideoutHelper } from "@spt-aki/helpers/HideoutHelper";
|
|
import { InventoryHelper } from "@spt-aki/helpers/InventoryHelper";
|
|
import { PaymentHelper } from "@spt-aki/helpers/PaymentHelper";
|
|
import { PresetHelper } from "@spt-aki/helpers/PresetHelper";
|
|
import { ProfileHelper } from "@spt-aki/helpers/ProfileHelper";
|
|
import { IPmcData } from "@spt-aki/models/eft/common/IPmcData";
|
|
import { HideoutArea, Product, Production, ScavCase } from "@spt-aki/models/eft/common/tables/IBotBase";
|
|
import { Upd } from "@spt-aki/models/eft/common/tables/IItem";
|
|
import { HideoutUpgradeCompleteRequestData } from "@spt-aki/models/eft/hideout/HideoutUpgradeCompleteRequestData";
|
|
import { IHandleQTEEventRequestData } from "@spt-aki/models/eft/hideout/IHandleQTEEventRequestData";
|
|
import { IHideoutArea, Stage } from "@spt-aki/models/eft/hideout/IHideoutArea";
|
|
import { IHideoutCancelProductionRequestData } from "@spt-aki/models/eft/hideout/IHideoutCancelProductionRequestData";
|
|
import { IHideoutContinuousProductionStartRequestData } from "@spt-aki/models/eft/hideout/IHideoutContinuousProductionStartRequestData";
|
|
import { IHideoutImproveAreaRequestData } from "@spt-aki/models/eft/hideout/IHideoutImproveAreaRequestData";
|
|
import { IHideoutProduction } from "@spt-aki/models/eft/hideout/IHideoutProduction";
|
|
import { IHideoutPutItemInRequestData } from "@spt-aki/models/eft/hideout/IHideoutPutItemInRequestData";
|
|
import { IHideoutScavCaseStartRequestData } from "@spt-aki/models/eft/hideout/IHideoutScavCaseStartRequestData";
|
|
import { IHideoutSingleProductionStartRequestData } from "@spt-aki/models/eft/hideout/IHideoutSingleProductionStartRequestData";
|
|
import { IHideoutTakeItemOutRequestData } from "@spt-aki/models/eft/hideout/IHideoutTakeItemOutRequestData";
|
|
import { IHideoutTakeProductionRequestData } from "@spt-aki/models/eft/hideout/IHideoutTakeProductionRequestData";
|
|
import { IHideoutToggleAreaRequestData } from "@spt-aki/models/eft/hideout/IHideoutToggleAreaRequestData";
|
|
import { IHideoutUpgradeRequestData } from "@spt-aki/models/eft/hideout/IHideoutUpgradeRequestData";
|
|
import { IQteData } from "@spt-aki/models/eft/hideout/IQteData";
|
|
import { IRecordShootingRangePoints } from "@spt-aki/models/eft/hideout/IRecordShootingRangePoints";
|
|
import { IItemEventRouterResponse } from "@spt-aki/models/eft/itemEvent/IItemEventRouterResponse";
|
|
import { ConfigTypes } from "@spt-aki/models/enums/ConfigTypes";
|
|
import { HideoutAreas } from "@spt-aki/models/enums/HideoutAreas";
|
|
import { SkillTypes } from "@spt-aki/models/enums/SkillTypes";
|
|
import { IHideoutConfig } from "@spt-aki/models/spt/config/IHideoutConfig";
|
|
import { ILogger } from "@spt-aki/models/spt/utils/ILogger";
|
|
import { EventOutputHolder } from "@spt-aki/routers/EventOutputHolder";
|
|
import { ConfigServer } from "@spt-aki/servers/ConfigServer";
|
|
import { DatabaseServer } from "@spt-aki/servers/DatabaseServer";
|
|
import { SaveServer } from "@spt-aki/servers/SaveServer";
|
|
import { FenceService } from "@spt-aki/services/FenceService";
|
|
import { LocalisationService } from "@spt-aki/services/LocalisationService";
|
|
import { PlayerService } from "@spt-aki/services/PlayerService";
|
|
import { HashUtil } from "@spt-aki/utils/HashUtil";
|
|
import { HttpResponseUtil } from "@spt-aki/utils/HttpResponseUtil";
|
|
import { JsonUtil } from "@spt-aki/utils/JsonUtil";
|
|
import { RandomUtil } from "@spt-aki/utils/RandomUtil";
|
|
import { TimeUtil } from "@spt-aki/utils/TimeUtil";
|
|
|
|
@injectable()
|
|
export class HideoutController
|
|
{
|
|
protected static nameTaskConditionCountersCrafting = "CounterHoursCrafting";
|
|
protected hideoutConfig: IHideoutConfig;
|
|
|
|
constructor(
|
|
@inject("WinstonLogger") protected logger: ILogger,
|
|
@inject("HashUtil") protected hashUtil: HashUtil,
|
|
@inject("TimeUtil") protected timeUtil: TimeUtil,
|
|
@inject("DatabaseServer") protected databaseServer: DatabaseServer,
|
|
@inject("RandomUtil") protected randomUtil: RandomUtil,
|
|
@inject("InventoryHelper") protected inventoryHelper: InventoryHelper,
|
|
@inject("SaveServer") protected saveServer: SaveServer,
|
|
@inject("PlayerService") protected playerService: PlayerService,
|
|
@inject("PresetHelper") protected presetHelper: PresetHelper,
|
|
@inject("PaymentHelper") protected paymentHelper: PaymentHelper,
|
|
@inject("EventOutputHolder") protected eventOutputHolder: EventOutputHolder,
|
|
@inject("HttpResponseUtil") protected httpResponse: HttpResponseUtil,
|
|
@inject("ProfileHelper") protected profileHelper: ProfileHelper,
|
|
@inject("HideoutHelper") protected hideoutHelper: HideoutHelper,
|
|
@inject("ScavCaseRewardGenerator") protected scavCaseRewardGenerator: ScavCaseRewardGenerator,
|
|
@inject("LocalisationService") protected localisationService: LocalisationService,
|
|
@inject("ConfigServer") protected configServer: ConfigServer,
|
|
@inject("JsonUtil") protected jsonUtil: JsonUtil,
|
|
@inject("FenceService") protected fenceService: FenceService,
|
|
)
|
|
{
|
|
this.hideoutConfig = this.configServer.getConfig(ConfigTypes.HIDEOUT);
|
|
}
|
|
|
|
/**
|
|
* Handle HideoutUpgrade event
|
|
* Start a hideout area upgrade
|
|
* @param pmcData Player profile
|
|
* @param request upgrade start request
|
|
* @param sessionID Session id
|
|
* @returns IItemEventRouterResponse
|
|
*/
|
|
public startUpgrade(
|
|
pmcData: IPmcData,
|
|
request: IHideoutUpgradeRequestData,
|
|
sessionID: string,
|
|
): IItemEventRouterResponse
|
|
{
|
|
const output = this.eventOutputHolder.getOutput(sessionID);
|
|
const items = request.items.map((reqItem) =>
|
|
{
|
|
const item = pmcData.Inventory.items.find((invItem) => invItem._id === reqItem.id);
|
|
return { inventoryItem: item, requestedItem: reqItem };
|
|
});
|
|
|
|
// If it's not money, its construction / barter items
|
|
for (const item of items)
|
|
{
|
|
if (!item.inventoryItem)
|
|
{
|
|
this.logger.error(
|
|
this.localisationService.getText("hideout-unable_to_find_item_in_inventory", item.requestedItem.id),
|
|
);
|
|
return this.httpResponse.appendErrorToOutput(output);
|
|
}
|
|
|
|
if (
|
|
this.paymentHelper.isMoneyTpl(item.inventoryItem._tpl)
|
|
&& item.inventoryItem.upd
|
|
&& item.inventoryItem.upd.StackObjectsCount
|
|
&& item.inventoryItem.upd.StackObjectsCount > item.requestedItem.count
|
|
)
|
|
{
|
|
item.inventoryItem.upd.StackObjectsCount -= item.requestedItem.count;
|
|
}
|
|
else
|
|
{
|
|
this.inventoryHelper.removeItem(pmcData, item.inventoryItem._id, sessionID, output);
|
|
}
|
|
}
|
|
|
|
// Construction time management
|
|
const hideoutArea = pmcData.Hideout.Areas.find((area) => area.type === request.areaType);
|
|
if (!hideoutArea)
|
|
{
|
|
this.logger.error(this.localisationService.getText("hideout-unable_to_find_area", request.areaType));
|
|
return this.httpResponse.appendErrorToOutput(output);
|
|
}
|
|
|
|
const hideoutData = this.databaseServer.getTables().hideout.areas.find((area) =>
|
|
area.type === request.areaType
|
|
);
|
|
if (!hideoutData)
|
|
{
|
|
this.logger.error(
|
|
this.localisationService.getText("hideout-unable_to_find_area_in_database", request.areaType),
|
|
);
|
|
return this.httpResponse.appendErrorToOutput(output);
|
|
}
|
|
|
|
const ctime = hideoutData.stages[hideoutArea.level + 1].constructionTime;
|
|
if (ctime > 0)
|
|
{
|
|
const timestamp = this.timeUtil.getTimestamp();
|
|
|
|
hideoutArea.completeTime = Math.round(timestamp + ctime);
|
|
hideoutArea.constructing = true;
|
|
}
|
|
|
|
return output;
|
|
}
|
|
|
|
/**
|
|
* Handle HideoutUpgradeComplete event
|
|
* Complete a hideout area upgrade
|
|
* @param pmcData Player profile
|
|
* @param request Completed upgrade request
|
|
* @param sessionID Session id
|
|
* @returns IItemEventRouterResponse
|
|
*/
|
|
public upgradeComplete(
|
|
pmcData: IPmcData,
|
|
request: HideoutUpgradeCompleteRequestData,
|
|
sessionID: string,
|
|
): IItemEventRouterResponse
|
|
{
|
|
const output = this.eventOutputHolder.getOutput(sessionID);
|
|
const db = this.databaseServer.getTables();
|
|
|
|
const profileHideoutArea = pmcData.Hideout.Areas.find((area) => area.type === request.areaType);
|
|
if (!profileHideoutArea)
|
|
{
|
|
this.logger.error(this.localisationService.getText("hideout-unable_to_find_area", request.areaType));
|
|
return this.httpResponse.appendErrorToOutput(output);
|
|
}
|
|
|
|
// Upgrade profile values
|
|
profileHideoutArea.level++;
|
|
profileHideoutArea.completeTime = 0;
|
|
profileHideoutArea.constructing = false;
|
|
|
|
const hideoutData = db.hideout.areas.find((area) => area.type === profileHideoutArea.type);
|
|
if (!hideoutData)
|
|
{
|
|
this.logger.error(
|
|
this.localisationService.getText("hideout-unable_to_find_area_in_database", request.areaType),
|
|
);
|
|
return this.httpResponse.appendErrorToOutput(output);
|
|
}
|
|
|
|
// Apply bonuses
|
|
const hideoutStage = hideoutData.stages[profileHideoutArea.level];
|
|
const bonuses = hideoutStage.bonuses;
|
|
if (bonuses?.length > 0)
|
|
{
|
|
for (const bonus of bonuses)
|
|
{
|
|
this.hideoutHelper.applyPlayerUpgradesBonuses(pmcData, bonus);
|
|
}
|
|
}
|
|
|
|
// Upgrade includes a container improvement/addition
|
|
if (hideoutStage?.container)
|
|
{
|
|
this.addContainerImprovementToProfile(
|
|
output,
|
|
sessionID,
|
|
pmcData,
|
|
profileHideoutArea,
|
|
hideoutData,
|
|
hideoutStage,
|
|
);
|
|
}
|
|
|
|
// Upgrading water collector / med station
|
|
if (
|
|
profileHideoutArea.type === HideoutAreas.WATER_COLLECTOR
|
|
|| profileHideoutArea.type === HideoutAreas.MEDSTATION
|
|
)
|
|
{
|
|
this.checkAndUpgradeWall(pmcData);
|
|
}
|
|
|
|
// Add Skill Points Per Area Upgrade
|
|
this.profileHelper.addSkillPointsToPlayer(
|
|
pmcData,
|
|
SkillTypes.HIDEOUT_MANAGEMENT,
|
|
db.globals.config.SkillsSettings.HideoutManagement.SkillPointsPerAreaUpgrade,
|
|
);
|
|
|
|
return output;
|
|
}
|
|
|
|
/**
|
|
* Upgrade wall status to visible in profile if medstation/water collector are both level 1
|
|
* @param pmcData Player profile
|
|
*/
|
|
protected checkAndUpgradeWall(pmcData: IPmcData): void
|
|
{
|
|
const medStation = pmcData.Hideout.Areas.find((area) => area.type === HideoutAreas.MEDSTATION);
|
|
const waterCollector = pmcData.Hideout.Areas.find((area) => area.type === HideoutAreas.WATER_COLLECTOR);
|
|
if (medStation?.level >= 1 && waterCollector?.level >= 1)
|
|
{
|
|
const wall = pmcData.Hideout.Areas.find((area) => area.type === HideoutAreas.EMERGENCY_WALL);
|
|
if (wall?.level === 0)
|
|
{
|
|
wall.level = 3;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param pmcData Profile to edit
|
|
* @param output Object to send back to client
|
|
* @param sessionID Session/player id
|
|
* @param profileParentHideoutArea Current hideout area for profile
|
|
* @param dbHideoutArea Hideout area being upgraded
|
|
* @param hideoutStage Stage hideout area is being upgraded to
|
|
*/
|
|
protected addContainerImprovementToProfile(
|
|
output: IItemEventRouterResponse,
|
|
sessionID: string,
|
|
pmcData: IPmcData,
|
|
profileParentHideoutArea: HideoutArea,
|
|
dbHideoutArea: IHideoutArea,
|
|
hideoutStage: Stage,
|
|
): void
|
|
{
|
|
// Add key/value to `hideoutAreaStashes` dictionary - used to link hideout area to inventory stash by its id
|
|
if (!pmcData.Inventory.hideoutAreaStashes[dbHideoutArea.type])
|
|
{
|
|
pmcData.Inventory.hideoutAreaStashes[dbHideoutArea.type] = dbHideoutArea._id;
|
|
}
|
|
|
|
// Add/upgrade stash item in player inventory
|
|
this.addUpdateInventoryItemToProfile(pmcData, dbHideoutArea, hideoutStage);
|
|
|
|
// Inform client of changes
|
|
this.addContainerUpgradeToClientOutput(output, sessionID, dbHideoutArea.type, dbHideoutArea, hideoutStage);
|
|
|
|
// Some areas like gun stand have a child area linked to it, it needs to do the same as above
|
|
const childDbArea = this.databaseServer.getTables().hideout.areas.find((x) =>
|
|
x.parentArea === dbHideoutArea._id
|
|
);
|
|
if (childDbArea)
|
|
{
|
|
// Add key/value to `hideoutAreaStashes` dictionary - used to link hideout area to inventory stash by its id
|
|
if (!pmcData.Inventory.hideoutAreaStashes[childDbArea.type])
|
|
{
|
|
pmcData.Inventory.hideoutAreaStashes[childDbArea.type] = childDbArea._id;
|
|
}
|
|
|
|
// Set child area level to same as parent area
|
|
pmcData.Hideout.Areas.find((x) => x.type === childDbArea.type).level = pmcData.Hideout.Areas.find((x) =>
|
|
x.type === profileParentHideoutArea.type
|
|
).level;
|
|
|
|
// Add/upgrade stash item in player inventory
|
|
const childDbAreaStage = childDbArea.stages[profileParentHideoutArea.level];
|
|
this.addUpdateInventoryItemToProfile(pmcData, childDbArea, childDbAreaStage);
|
|
|
|
// Inform client of the changes
|
|
this.addContainerUpgradeToClientOutput(output, sessionID, childDbArea.type, childDbArea, childDbAreaStage);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Add an inventory item to profile from a hideout area stage data
|
|
* @param pmcData Profile to update
|
|
* @param dbHideoutData Hideout area from db being upgraded
|
|
* @param hideoutStage Stage area upgraded to
|
|
*/
|
|
protected addUpdateInventoryItemToProfile(pmcData: IPmcData, dbHideoutData: IHideoutArea, hideoutStage: Stage): void
|
|
{
|
|
const existingInventoryItem = pmcData.Inventory.items.find((x) => x._id === dbHideoutData._id);
|
|
if (existingInventoryItem)
|
|
{
|
|
// Update existing items container tpl to point to new id (tpl)
|
|
existingInventoryItem._tpl = hideoutStage.container;
|
|
|
|
return;
|
|
}
|
|
|
|
// Add new item as none exists
|
|
pmcData.Inventory.items.push({ _id: dbHideoutData._id, _tpl: hideoutStage.container });
|
|
}
|
|
|
|
/**
|
|
* @param output Object to send to client
|
|
* @param sessionID Session/player id
|
|
* @param areaType Hideout area that had stash added
|
|
* @param hideoutDbData Hideout area that caused addition of stash
|
|
* @param hideoutStage Hideout area upgraded to this
|
|
*/
|
|
protected addContainerUpgradeToClientOutput(
|
|
output: IItemEventRouterResponse,
|
|
sessionID: string,
|
|
areaType: HideoutAreas,
|
|
hideoutDbData: IHideoutArea,
|
|
hideoutStage: Stage,
|
|
): void
|
|
{
|
|
if (!output.profileChanges[sessionID].changedHideoutStashes)
|
|
{
|
|
output.profileChanges[sessionID].changedHideoutStashes = {};
|
|
}
|
|
|
|
output.profileChanges[sessionID].changedHideoutStashes[areaType] = {
|
|
Id: hideoutDbData._id,
|
|
Tpl: hideoutStage.container,
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Handle HideoutPutItemsInAreaSlots
|
|
* Create item in hideout slot item array, remove item from player inventory
|
|
* @param pmcData Profile data
|
|
* @param addItemToHideoutRequest reqeust from client to place item in area slot
|
|
* @param sessionID Session id
|
|
* @returns IItemEventRouterResponse object
|
|
*/
|
|
public putItemsInAreaSlots(
|
|
pmcData: IPmcData,
|
|
addItemToHideoutRequest: IHideoutPutItemInRequestData,
|
|
sessionID: string,
|
|
): IItemEventRouterResponse
|
|
{
|
|
const output = this.eventOutputHolder.getOutput(sessionID);
|
|
|
|
const itemsToAdd = Object.entries(addItemToHideoutRequest.items).map((kvp) =>
|
|
{
|
|
const item = pmcData.Inventory.items.find((invItem) => invItem._id === kvp[1].id);
|
|
return { inventoryItem: item, requestedItem: kvp[1], slot: kvp[0] };
|
|
});
|
|
|
|
const hideoutArea = pmcData.Hideout.Areas.find((area) => area.type === addItemToHideoutRequest.areaType);
|
|
if (!hideoutArea)
|
|
{
|
|
this.logger.error(
|
|
this.localisationService.getText(
|
|
"hideout-unable_to_find_area_in_database",
|
|
addItemToHideoutRequest.areaType,
|
|
),
|
|
);
|
|
return this.httpResponse.appendErrorToOutput(output);
|
|
}
|
|
|
|
for (const item of itemsToAdd)
|
|
{
|
|
if (!item.inventoryItem)
|
|
{
|
|
this.logger.error(
|
|
this.localisationService.getText("hideout-unable_to_find_item_in_inventory", {
|
|
itemId: item.requestedItem.id,
|
|
area: hideoutArea.type,
|
|
}),
|
|
);
|
|
return this.httpResponse.appendErrorToOutput(output);
|
|
}
|
|
|
|
// Add item to area.slots
|
|
const destinationLocationIndex = Number(item.slot);
|
|
const hideoutSlotIndex = hideoutArea.slots.findIndex((x) => x.locationIndex === destinationLocationIndex);
|
|
hideoutArea.slots[hideoutSlotIndex].item = [{
|
|
_id: item.inventoryItem._id,
|
|
_tpl: item.inventoryItem._tpl,
|
|
upd: item.inventoryItem.upd,
|
|
}];
|
|
|
|
this.inventoryHelper.removeItem(pmcData, item.inventoryItem._id, sessionID, output);
|
|
}
|
|
|
|
// Trigger a forced update
|
|
this.hideoutHelper.updatePlayerHideout(sessionID);
|
|
|
|
return output;
|
|
}
|
|
|
|
/**
|
|
* Handle HideoutTakeItemsFromAreaSlots event
|
|
* Remove item from hideout area and place into player inventory
|
|
* @param pmcData Player profile
|
|
* @param request Take item out of area request
|
|
* @param sessionID Session id
|
|
* @returns IItemEventRouterResponse
|
|
*/
|
|
public takeItemsFromAreaSlots(
|
|
pmcData: IPmcData,
|
|
request: IHideoutTakeItemOutRequestData,
|
|
sessionID: string,
|
|
): IItemEventRouterResponse
|
|
{
|
|
const output = this.eventOutputHolder.getOutput(sessionID);
|
|
|
|
const hideoutArea = pmcData.Hideout.Areas.find((area) => area.type === request.areaType);
|
|
if (!hideoutArea)
|
|
{
|
|
this.logger.error(this.localisationService.getText("hideout-unable_to_find_area", request.areaType));
|
|
return this.httpResponse.appendErrorToOutput(output);
|
|
}
|
|
|
|
if (!hideoutArea.slots || hideoutArea.slots.length === 0)
|
|
{
|
|
this.logger.error(
|
|
this.localisationService.getText("hideout-unable_to_find_item_to_remove_from_area", hideoutArea.type),
|
|
);
|
|
return this.httpResponse.appendErrorToOutput(output);
|
|
}
|
|
|
|
// Handle areas that have resources that can be placed in/taken out of slots from the area
|
|
if (
|
|
[
|
|
HideoutAreas.AIR_FILTERING,
|
|
HideoutAreas.WATER_COLLECTOR,
|
|
HideoutAreas.GENERATOR,
|
|
HideoutAreas.BITCOIN_FARM,
|
|
].includes(hideoutArea.type)
|
|
)
|
|
{
|
|
const response = this.removeResourceFromArea(sessionID, pmcData, request, output, hideoutArea);
|
|
this.update();
|
|
return response;
|
|
}
|
|
|
|
throw new Error(
|
|
this.localisationService.getText("hideout-unhandled_remove_item_from_area_request", hideoutArea.type),
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Find resource item in hideout area, add copy to player inventory, remove Item from hideout slot
|
|
* @param sessionID Session id
|
|
* @param pmcData Profile to update
|
|
* @param removeResourceRequest client request
|
|
* @param output response to send to client
|
|
* @param hideoutArea Area fuel is being removed from
|
|
* @returns IItemEventRouterResponse response
|
|
*/
|
|
protected removeResourceFromArea(
|
|
sessionID: string,
|
|
pmcData: IPmcData,
|
|
removeResourceRequest: IHideoutTakeItemOutRequestData,
|
|
output: IItemEventRouterResponse,
|
|
hideoutArea: HideoutArea,
|
|
): IItemEventRouterResponse
|
|
{
|
|
const slotIndexToRemove = removeResourceRequest.slots[0];
|
|
|
|
const itemToReturn = hideoutArea.slots.find((x) => x.locationIndex === slotIndexToRemove).item[0];
|
|
|
|
const newReq = {
|
|
items: [{
|
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
item_id: itemToReturn._tpl,
|
|
count: 1,
|
|
}],
|
|
tid: "ragfair",
|
|
};
|
|
|
|
output = this.inventoryHelper.addItem(
|
|
pmcData,
|
|
newReq,
|
|
output,
|
|
sessionID,
|
|
null,
|
|
!!itemToReturn.upd.SpawnedInSession,
|
|
itemToReturn.upd,
|
|
);
|
|
|
|
// If addItem returned with errors, drop out
|
|
if (output.warnings && output.warnings.length > 0)
|
|
{
|
|
return output;
|
|
}
|
|
|
|
// Remove items from slot, locationIndex remains
|
|
const hideoutSlotIndex = hideoutArea.slots.findIndex((x) => x.locationIndex === slotIndexToRemove);
|
|
hideoutArea.slots[hideoutSlotIndex].item = undefined;
|
|
|
|
return output;
|
|
}
|
|
|
|
/**
|
|
* Handle HideoutToggleArea event
|
|
* Toggle area on/off
|
|
* @param pmcData Player profile
|
|
* @param request Toggle area request
|
|
* @param sessionID Session id
|
|
* @returns IItemEventRouterResponse
|
|
*/
|
|
public toggleArea(
|
|
pmcData: IPmcData,
|
|
request: IHideoutToggleAreaRequestData,
|
|
sessionID: string,
|
|
): IItemEventRouterResponse
|
|
{
|
|
const output = this.eventOutputHolder.getOutput(sessionID);
|
|
|
|
// Force a production update (occur before area is toggled as it could be generator and doing it after generator enabled would cause incorrect calculaton of production progress)
|
|
this.hideoutHelper.updatePlayerHideout(sessionID);
|
|
|
|
const hideoutArea = pmcData.Hideout.Areas.find((area) => area.type === request.areaType);
|
|
if (!hideoutArea)
|
|
{
|
|
this.logger.error(this.localisationService.getText("hideout-unable_to_find_area", request.areaType));
|
|
return this.httpResponse.appendErrorToOutput(output);
|
|
}
|
|
|
|
hideoutArea.active = request.enabled;
|
|
|
|
return output;
|
|
}
|
|
|
|
/**
|
|
* Handle HideoutSingleProductionStart event
|
|
* Start production for an item from hideout area
|
|
* @param pmcData Player profile
|
|
* @param body Start prodution of single item request
|
|
* @param sessionID Session id
|
|
* @returns IItemEventRouterResponse
|
|
*/
|
|
public singleProductionStart(
|
|
pmcData: IPmcData,
|
|
body: IHideoutSingleProductionStartRequestData,
|
|
sessionID: string,
|
|
): IItemEventRouterResponse
|
|
{
|
|
// Start production
|
|
this.registerProduction(pmcData, body, sessionID);
|
|
|
|
// Find the recipe of the production
|
|
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
|
|
const requirements = this.jsonUtil.clone(recipe.requirements.filter((i) => i.type === "Item"));
|
|
|
|
const output = this.eventOutputHolder.getOutput(sessionID);
|
|
|
|
for (const itemToDelete of body.items)
|
|
{
|
|
const itemToCheck = pmcData.Inventory.items.find((i) => i._id === itemToDelete.id);
|
|
const requirement = requirements.find((requirement) => requirement.templateId === itemToCheck._tpl);
|
|
if (requirement.count <= 0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
this.inventoryHelper.removeItemByCount(pmcData, itemToDelete.id, requirement.count, sessionID, output);
|
|
requirement.count -= itemToDelete.count;
|
|
}
|
|
|
|
return output;
|
|
}
|
|
|
|
/**
|
|
* Handle HideoutScavCaseProductionStart event
|
|
* Handles event after clicking 'start' on the scav case hideout page
|
|
* @param pmcData player profile
|
|
* @param body client request object
|
|
* @param sessionID session id
|
|
* @returns item event router response
|
|
*/
|
|
public scavCaseProductionStart(
|
|
pmcData: IPmcData,
|
|
body: IHideoutScavCaseStartRequestData,
|
|
sessionID: string,
|
|
): IItemEventRouterResponse
|
|
{
|
|
const output = this.eventOutputHolder.getOutput(sessionID);
|
|
|
|
for (const requestedItem of body.items)
|
|
{
|
|
const inventoryItem = pmcData.Inventory.items.find((item) => item._id === requestedItem.id);
|
|
if (!inventoryItem)
|
|
{
|
|
this.logger.error(
|
|
this.localisationService.getText(
|
|
"hideout-unable_to_find_scavcase_requested_item_in_profile_inventory",
|
|
requestedItem.id,
|
|
),
|
|
);
|
|
return this.httpResponse.appendErrorToOutput(output);
|
|
}
|
|
|
|
if (inventoryItem.upd?.StackObjectsCount && inventoryItem.upd.StackObjectsCount > requestedItem.count)
|
|
{
|
|
inventoryItem.upd.StackObjectsCount -= requestedItem.count;
|
|
}
|
|
else
|
|
{
|
|
this.inventoryHelper.removeItem(pmcData, requestedItem.id, sessionID, output);
|
|
}
|
|
}
|
|
|
|
const recipe = this.databaseServer.getTables().hideout.scavcase.find((r) => r._id === body.recipeId);
|
|
if (!recipe)
|
|
{
|
|
this.logger.error(
|
|
this.localisationService.getText("hideout-unable_to_find_scav_case_recipie_in_database", body.recipeId),
|
|
);
|
|
return this.httpResponse.appendErrorToOutput(output);
|
|
}
|
|
|
|
// @Important: Here we need to be very exact:
|
|
// - normal recipe: Production time value is stored in attribute "productionType" with small "p"
|
|
// - scav case recipe: Production time value is stored in attribute "ProductionType" with capital "P"
|
|
const modifiedScavCaseTime = this.getScavCaseTime(pmcData, recipe.ProductionTime);
|
|
|
|
pmcData.Hideout.Production[body.recipeId] = this.hideoutHelper.initProduction(
|
|
body.recipeId,
|
|
modifiedScavCaseTime,
|
|
false,
|
|
);
|
|
pmcData.Hideout.Production[body.recipeId].sptIsScavCase = true;
|
|
|
|
return output;
|
|
}
|
|
|
|
/**
|
|
* Adjust scav case time based on fence standing
|
|
*
|
|
* @param pmcData Player profile
|
|
* @param productionTime Time to complete scav case in seconds
|
|
* @returns Adjusted scav case time in seconds
|
|
*/
|
|
protected getScavCaseTime(pmcData: IPmcData, productionTime: number): number
|
|
{
|
|
const fenceLevel = this.fenceService.getFenceInfo(pmcData);
|
|
if (!fenceLevel)
|
|
{
|
|
return productionTime;
|
|
}
|
|
|
|
return productionTime * fenceLevel.ScavCaseTimeModifier;
|
|
}
|
|
|
|
/**
|
|
* Add generated scav case rewards to player profile
|
|
* @param pmcData player profile to add rewards to
|
|
* @param rewards reward items to add to profile
|
|
* @param recipeId recipe id to save into Production dict
|
|
*/
|
|
protected addScavCaseRewardsToProfile(pmcData: IPmcData, rewards: Product[], recipeId: string): void
|
|
{
|
|
pmcData.Hideout.Production[`ScavCase${recipeId}`] = { Products: rewards };
|
|
}
|
|
|
|
/**
|
|
* Start production of continuously created item
|
|
* @param pmcData Player profile
|
|
* @param request Continious production request
|
|
* @param sessionID Session id
|
|
* @returns IItemEventRouterResponse
|
|
*/
|
|
public continuousProductionStart(
|
|
pmcData: IPmcData,
|
|
request: IHideoutContinuousProductionStartRequestData,
|
|
sessionID: string,
|
|
): IItemEventRouterResponse
|
|
{
|
|
this.registerProduction(pmcData, request, sessionID);
|
|
|
|
return this.eventOutputHolder.getOutput(sessionID);
|
|
}
|
|
|
|
/**
|
|
* Handle HideoutTakeProduction event
|
|
* Take completed item out of hideout area and place into player inventory
|
|
* @param pmcData Player profile
|
|
* @param request Remove production from area request
|
|
* @param sessionID Session id
|
|
* @returns IItemEventRouterResponse
|
|
*/
|
|
public takeProduction(
|
|
pmcData: IPmcData,
|
|
request: IHideoutTakeProductionRequestData,
|
|
sessionID: string,
|
|
): IItemEventRouterResponse
|
|
{
|
|
const output = this.eventOutputHolder.getOutput(sessionID);
|
|
const hideoutDb = this.databaseServer.getTables().hideout;
|
|
|
|
if (request.recipeId === HideoutHelper.bitcoinFarm)
|
|
{
|
|
return this.hideoutHelper.getBTC(pmcData, request, sessionID);
|
|
}
|
|
|
|
const recipe = hideoutDb.production.find((r) => r._id === request.recipeId);
|
|
if (recipe)
|
|
{
|
|
return this.handleRecipe(sessionID, recipe, pmcData, request, output);
|
|
}
|
|
|
|
const scavCase = hideoutDb.scavcase.find((r) => r._id === request.recipeId);
|
|
if (scavCase)
|
|
{
|
|
return this.handleScavCase(sessionID, pmcData, request, output);
|
|
}
|
|
|
|
this.logger.error(
|
|
this.localisationService.getText(
|
|
"hideout-unable_to_find_production_in_profile_by_recipie_id",
|
|
request.recipeId,
|
|
),
|
|
);
|
|
|
|
return this.httpResponse.appendErrorToOutput(output);
|
|
}
|
|
|
|
/**
|
|
* Take recipe-type production out of hideout area and place into player inventory
|
|
* @param sessionID Session id
|
|
* @param recipe Completed recipe of item
|
|
* @param pmcData Player profile
|
|
* @param request Remove production from area request
|
|
* @param output Output object to update
|
|
* @returns IItemEventRouterResponse
|
|
*/
|
|
protected handleRecipe(
|
|
sessionID: string,
|
|
recipe: IHideoutProduction,
|
|
pmcData: IPmcData,
|
|
request: IHideoutTakeProductionRequestData,
|
|
output: IItemEventRouterResponse,
|
|
): IItemEventRouterResponse
|
|
{
|
|
// Variables for managemnet of skill
|
|
let craftingExpAmount = 0;
|
|
|
|
// ? move the logic of TaskConditionCounters in new method?
|
|
let counterHoursCrafting = pmcData.TaskConditionCounters[HideoutController.nameTaskConditionCountersCrafting];
|
|
if (!counterHoursCrafting)
|
|
{
|
|
pmcData.TaskConditionCounters[HideoutController.nameTaskConditionCountersCrafting] = {
|
|
id: recipe._id,
|
|
type: HideoutController.nameTaskConditionCountersCrafting,
|
|
sourceId: "CounterCrafting",
|
|
value: 0,
|
|
};
|
|
counterHoursCrafting = pmcData.TaskConditionCounters[HideoutController.nameTaskConditionCountersCrafting];
|
|
}
|
|
let hoursCrafting = counterHoursCrafting.value;
|
|
|
|
// create item and throw it into profile
|
|
let id = recipe.endProduct;
|
|
|
|
// replace the base item with its main preset
|
|
if (this.presetHelper.hasPreset(id))
|
|
{
|
|
id = this.presetHelper.getDefaultPreset(id)._id;
|
|
}
|
|
|
|
const newReq = {
|
|
items: [{
|
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
item_id: id,
|
|
count: recipe.count,
|
|
}],
|
|
tid: "ragfair",
|
|
};
|
|
|
|
const entries = Object.entries(pmcData.Hideout.Production);
|
|
let prodId: string;
|
|
for (const x of entries)
|
|
{
|
|
// Skip null production objects
|
|
if (!x[1])
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (this.hideoutHelper.isProductionType(x[1]))
|
|
{ // Production or ScavCase
|
|
if ((x[1] as Production).RecipeId === request.recipeId)
|
|
{
|
|
prodId = x[0]; // set to objects key
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (prodId === undefined)
|
|
{
|
|
this.logger.error(
|
|
this.localisationService.getText(
|
|
"hideout-unable_to_find_production_in_profile_by_recipie_id",
|
|
request.recipeId,
|
|
),
|
|
);
|
|
|
|
return this.httpResponse.appendErrorToOutput(output);
|
|
}
|
|
|
|
// Check if the recipe is the same as the last one
|
|
const area = pmcData.Hideout.Areas.find((x) => x.type === recipe.areaType);
|
|
if (area && request.recipeId !== area.lastRecipe)
|
|
{
|
|
// 1 point per craft upon the end of production for alternating between 2 different crafting recipes in the same module
|
|
craftingExpAmount += this.hideoutConfig.expCraftAmount; // Default is 10
|
|
}
|
|
|
|
// 1 point per 8 hours of crafting
|
|
hoursCrafting += recipe.productionTime;
|
|
if ((hoursCrafting / this.hideoutConfig.hoursForSkillCrafting) >= 1)
|
|
{
|
|
const multiplierCrafting = Math.floor(hoursCrafting / this.hideoutConfig.hoursForSkillCrafting);
|
|
craftingExpAmount += 1 * multiplierCrafting;
|
|
hoursCrafting -= this.hideoutConfig.hoursForSkillCrafting * multiplierCrafting;
|
|
}
|
|
|
|
// Increment
|
|
// if addItem passes validation:
|
|
// - increment skill point for crafting
|
|
// - delete the production in profile Hideout.Production
|
|
const callback = () =>
|
|
{
|
|
// Manager Hideout skill
|
|
// ? use a configuration variable for the value?
|
|
const globals = this.databaseServer.getTables().globals;
|
|
this.profileHelper.addSkillPointsToPlayer(
|
|
pmcData,
|
|
SkillTypes.HIDEOUT_MANAGEMENT,
|
|
globals.config.SkillsSettings.HideoutManagement.SkillPointsPerCraft,
|
|
true,
|
|
);
|
|
// Manager Crafting skill
|
|
if (craftingExpAmount > 0)
|
|
{
|
|
this.profileHelper.addSkillPointsToPlayer(pmcData, SkillTypes.CRAFTING, craftingExpAmount);
|
|
|
|
const intellectAmountToGive = 0.5 * (Math.round(craftingExpAmount / 15));
|
|
if (intellectAmountToGive > 0)
|
|
{
|
|
this.profileHelper.addSkillPointsToPlayer(
|
|
pmcData,
|
|
SkillTypes.INTELLECT,
|
|
intellectAmountToGive,
|
|
);
|
|
}
|
|
|
|
}
|
|
area.lastRecipe = request.recipeId;
|
|
counterHoursCrafting.value = hoursCrafting;
|
|
|
|
// Continuous crafts have special handling in EventOutputHolder.updateOutputProperties()
|
|
pmcData.Hideout.Production[prodId].sptIsComplete = true;
|
|
pmcData.Hideout.Production[prodId].sptIsContinuous = recipe.continuous;
|
|
|
|
// Flag normal crafts as complete
|
|
if (!recipe.continuous)
|
|
{
|
|
pmcData.Hideout.Production[prodId].inProgress = false;
|
|
}
|
|
};
|
|
|
|
// Handle the isEncoded flag from recipe
|
|
if (recipe.isEncoded)
|
|
{
|
|
const upd: Upd = { RecodableComponent: { IsEncoded: true } };
|
|
|
|
return this.inventoryHelper.addItem(pmcData, newReq, output, sessionID, callback, true, upd);
|
|
}
|
|
|
|
return this.inventoryHelper.addItem(pmcData, newReq, output, sessionID, callback, true);
|
|
}
|
|
|
|
/**
|
|
* Handles generating case rewards and sending to player inventory
|
|
* @param sessionID Session id
|
|
* @param pmcData Player profile
|
|
* @param request Get rewards from scavcase craft request
|
|
* @param output Output object to update
|
|
* @returns IItemEventRouterResponse
|
|
*/
|
|
protected handleScavCase(
|
|
sessionID: string,
|
|
pmcData: IPmcData,
|
|
request: IHideoutTakeProductionRequestData,
|
|
output: IItemEventRouterResponse,
|
|
): IItemEventRouterResponse
|
|
{
|
|
const ongoingProductions = Object.entries(pmcData.Hideout.Production);
|
|
let prodId: string;
|
|
for (const production of ongoingProductions)
|
|
{
|
|
if (this.hideoutHelper.isProductionType(production[1]))
|
|
{ // Production or ScavCase
|
|
if ((production[1] as ScavCase).RecipeId === request.recipeId)
|
|
{
|
|
prodId = production[0]; // Set to objects key
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (prodId === undefined)
|
|
{
|
|
this.logger.error(
|
|
this.localisationService.getText(
|
|
"hideout-unable_to_find_production_in_profile_by_recipie_id",
|
|
request.recipeId,
|
|
),
|
|
);
|
|
|
|
return this.httpResponse.appendErrorToOutput(output);
|
|
}
|
|
|
|
// Create rewards for scav case
|
|
const scavCaseRewards = this.scavCaseRewardGenerator.generate(request.recipeId);
|
|
|
|
// Add scav case rewards to player profile
|
|
pmcData.Hideout.Production[prodId].Products = scavCaseRewards;
|
|
|
|
// Remove the old production from output object before its sent to client
|
|
delete output.profileChanges[sessionID].production[request.recipeId];
|
|
|
|
// Get array of item created + count of them after completing hideout craft
|
|
const itemsToAdd = pmcData.Hideout.Production[prodId].Products.map(
|
|
(x: { _tpl: string; upd?: { StackObjectsCount?: number; }; }) =>
|
|
{
|
|
const itemTpl = this.presetHelper.hasPreset(x._tpl)
|
|
? this.presetHelper.getDefaultPreset(x._tpl)._id
|
|
: x._tpl;
|
|
|
|
// Count of items crafted
|
|
const numOfItems = !x.upd?.StackObjectsCount ? 1 : x.upd.StackObjectsCount;
|
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
return { item_id: itemTpl, count: numOfItems };
|
|
},
|
|
);
|
|
|
|
const newReq = { items: itemsToAdd, tid: "ragfair" };
|
|
const callback = () =>
|
|
{
|
|
// Flag as complete - will be cleaned up later by hideoutController.update()
|
|
pmcData.Hideout.Production[prodId].sptIsComplete = true;
|
|
|
|
// Crafting complete, flag as such
|
|
pmcData.Hideout.Production[prodId].inProgress = false;
|
|
};
|
|
|
|
// Add crafted item to player inventory
|
|
return this.inventoryHelper.addItem(pmcData, newReq, output, sessionID, callback, true);
|
|
}
|
|
|
|
/**
|
|
* Start area production for item by adding production to profiles' Hideout.Production array
|
|
* @param pmcData Player profile
|
|
* @param request Start production request
|
|
* @param sessionID Session id
|
|
* @returns IItemEventRouterResponse
|
|
*/
|
|
public registerProduction(
|
|
pmcData: IPmcData,
|
|
request: IHideoutSingleProductionStartRequestData | IHideoutContinuousProductionStartRequestData,
|
|
sessionID: string,
|
|
): IItemEventRouterResponse
|
|
{
|
|
return this.hideoutHelper.registerProduction(pmcData, request, sessionID);
|
|
}
|
|
|
|
/**
|
|
* Get quick time event list for hideout
|
|
* // TODO - implement this
|
|
* @param sessionId Session id
|
|
* @returns IQteData array
|
|
*/
|
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
public getQteList(sessionId: string): IQteData[]
|
|
{
|
|
return this.databaseServer.getTables().hideout.qte;
|
|
}
|
|
|
|
/**
|
|
* Handle HideoutQuickTimeEvent on client/game/profile/items/moving
|
|
* Called after completing workout at gym
|
|
* @param sessionId Session id
|
|
* @param pmcData Profile to adjust
|
|
* @param request QTE result object
|
|
*/
|
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
public handleQTEEventOutcome(
|
|
sessionId: string,
|
|
pmcData: IPmcData,
|
|
request: IHandleQTEEventRequestData,
|
|
): IItemEventRouterResponse
|
|
{
|
|
// {
|
|
// "Action": "HideoutQuickTimeEvent",
|
|
// "results": [true, false, true, true, true, true, true, true, true, false, false, false, false, false, false],
|
|
// "id": "63b16feb5d012c402c01f6ef",
|
|
// "timestamp": 1672585349
|
|
// }
|
|
|
|
// Skill changes are done in
|
|
// /client/hideout/workout (applyWorkoutChanges).
|
|
|
|
pmcData.Health.Energy.Current -= 50;
|
|
if (pmcData.Health.Energy.Current < 1)
|
|
{
|
|
pmcData.Health.Energy.Current = 1;
|
|
}
|
|
|
|
pmcData.Health.Hydration.Current -= 50;
|
|
if (pmcData.Health.Hydration.Current < 1)
|
|
{
|
|
pmcData.Health.Hydration.Current = 1;
|
|
}
|
|
|
|
return this.eventOutputHolder.getOutput(sessionId);
|
|
}
|
|
|
|
/**
|
|
* Record a high score from the shooting range into a player profiles overallcounters
|
|
* @param sessionId Session id
|
|
* @param pmcData Profile to update
|
|
* @param request shooting range score request
|
|
* @returns IItemEventRouterResponse
|
|
*/
|
|
public recordShootingRangePoints(
|
|
sessionId: string,
|
|
pmcData: IPmcData,
|
|
request: IRecordShootingRangePoints,
|
|
): IItemEventRouterResponse
|
|
{
|
|
// Check if counter exists, add placeholder if it doesnt
|
|
if (!pmcData.Stats.Eft.OverallCounters.Items.find((x) => x.Key.includes("ShootingRangePoints")))
|
|
{
|
|
pmcData.Stats.Eft.OverallCounters.Items.push({ Key: ["ShootingRangePoints"], Value: 0 });
|
|
}
|
|
|
|
// Find counter by key and update value
|
|
const shootingRangeHighScore = pmcData.Stats.Eft.OverallCounters.Items.find((x) =>
|
|
x.Key.includes("ShootingRangePoints")
|
|
);
|
|
shootingRangeHighScore.Value = request.points;
|
|
|
|
// Check against live, maybe a response isnt necessary
|
|
return this.eventOutputHolder.getOutput(sessionId);
|
|
}
|
|
|
|
/**
|
|
* Handle client/game/profile/items/moving - HideoutImproveArea
|
|
* @param sessionId Session id
|
|
* @param pmcData Profile to improve area in
|
|
* @param request Improve area request data
|
|
*/
|
|
public improveArea(
|
|
sessionId: string,
|
|
pmcData: IPmcData,
|
|
request: IHideoutImproveAreaRequestData,
|
|
): IItemEventRouterResponse
|
|
{
|
|
const output = this.eventOutputHolder.getOutput(sessionId);
|
|
|
|
// Create mapping of required item with corrisponding item from player inventory
|
|
const items = request.items.map((reqItem) =>
|
|
{
|
|
const item = pmcData.Inventory.items.find((invItem) => invItem._id === reqItem.id);
|
|
return { inventoryItem: item, requestedItem: reqItem };
|
|
});
|
|
|
|
// If it's not money, its construction / barter items
|
|
for (const item of items)
|
|
{
|
|
if (!item.inventoryItem)
|
|
{
|
|
this.logger.error(
|
|
this.localisationService.getText("hideout-unable_to_find_item_in_inventory", item.requestedItem.id),
|
|
);
|
|
return this.httpResponse.appendErrorToOutput(output);
|
|
}
|
|
|
|
if (
|
|
this.paymentHelper.isMoneyTpl(item.inventoryItem._tpl)
|
|
&& item.inventoryItem.upd
|
|
&& item.inventoryItem.upd.StackObjectsCount
|
|
&& item.inventoryItem.upd.StackObjectsCount > item.requestedItem.count
|
|
)
|
|
{
|
|
item.inventoryItem.upd.StackObjectsCount -= item.requestedItem.count;
|
|
}
|
|
else
|
|
{
|
|
this.inventoryHelper.removeItem(pmcData, item.inventoryItem._id, sessionId, output);
|
|
}
|
|
}
|
|
|
|
const profileHideoutArea = pmcData.Hideout.Areas.find((x) => x.type === request.areaType);
|
|
if (!profileHideoutArea)
|
|
{
|
|
this.logger.error(this.localisationService.getText("hideout-unable_to_find_area", request.areaType));
|
|
return this.httpResponse.appendErrorToOutput(output);
|
|
}
|
|
|
|
const hideoutDbData = this.databaseServer.getTables().hideout.areas.find((x) => x.type === request.areaType);
|
|
if (!hideoutDbData)
|
|
{
|
|
this.logger.error(
|
|
this.localisationService.getText("hideout-unable_to_find_area_in_database", request.areaType),
|
|
);
|
|
return this.httpResponse.appendErrorToOutput(output);
|
|
}
|
|
|
|
// Add all improvemets to output object
|
|
const improvements = hideoutDbData.stages[profileHideoutArea.level].improvements;
|
|
const timestamp = this.timeUtil.getTimestamp();
|
|
|
|
if (!output.profileChanges[sessionId].improvements)
|
|
{
|
|
output.profileChanges[sessionId].improvements = {};
|
|
}
|
|
|
|
for (const improvement of improvements)
|
|
{
|
|
const improvementDetails = {
|
|
completed: false,
|
|
improveCompleteTimestamp: timestamp + improvement.improvementTime,
|
|
};
|
|
output.profileChanges[sessionId].improvements[improvement.id] = improvementDetails;
|
|
pmcData.Hideout.Improvement[improvement.id] = improvementDetails;
|
|
}
|
|
|
|
return output;
|
|
}
|
|
|
|
/**
|
|
* Handle client/game/profile/items/moving HideoutCancelProductionCommand
|
|
* @param sessionId Session id
|
|
* @param pmcData Profile with craft to cancel
|
|
* @param request Cancel production request data
|
|
* @returns IItemEventRouterResponse
|
|
*/
|
|
public cancelProduction(
|
|
sessionId: string,
|
|
pmcData: IPmcData,
|
|
request: IHideoutCancelProductionRequestData,
|
|
): IItemEventRouterResponse
|
|
{
|
|
const output = this.eventOutputHolder.getOutput(sessionId);
|
|
|
|
const craftToCancel = pmcData.Hideout.Production[request.recipeId];
|
|
if (!craftToCancel)
|
|
{
|
|
const errorMessage = `Unable to find craft ${request.recipeId} to cancel`;
|
|
this.logger.error(errorMessage);
|
|
|
|
return this.httpResponse.appendErrorToOutput(output, errorMessage);
|
|
}
|
|
|
|
// Null out production data so client gets informed when response send back
|
|
pmcData.Hideout.Production[request.recipeId] = null;
|
|
|
|
// TODO - handle timestamp somehow?
|
|
|
|
return output;
|
|
}
|
|
|
|
/**
|
|
* Function called every x seconds as part of onUpdate event
|
|
*/
|
|
public update(): void
|
|
{
|
|
for (const sessionID in this.saveServer.getProfiles())
|
|
{
|
|
if ("Hideout" in this.saveServer.getProfile(sessionID).characters.pmc)
|
|
{
|
|
this.hideoutHelper.updatePlayerHideout(sessionID);
|
|
}
|
|
}
|
|
}
|
|
}
|