Project Code Format

Ran the `npm run format` command to bring the entire project in-line with the formatting rules.
This commit is contained in:
Refringe 2024-05-17 15:32:41 -04:00
parent 5def42416b
commit cb169a18b9
No known key found for this signature in database
GPG Key ID: 7715B85B4A6306ED
158 changed files with 1999 additions and 1691 deletions

View File

@ -23,7 +23,7 @@ export class ErrorHandler
this.logger.error(`\nStacktrace:\n${err.stack}`); this.logger.error(`\nStacktrace:\n${err.stack}`);
} }
this.readLine.question("Press Enter to close the window", _ans => this.readLine.close()); this.readLine.question("Press Enter to close the window", (_ans) => this.readLine.close());
this.readLine.on("close", () => process.exit(1)); this.readLine.on("close", () => process.exit(1));
} }
} }

View File

@ -2,7 +2,6 @@ import { inject, injectable } from "tsyringe";
import { AchievementController } from "@spt-aki/controllers/AchievementController"; import { AchievementController } from "@spt-aki/controllers/AchievementController";
import { ProfileController } from "@spt-aki/controllers/ProfileController"; import { ProfileController } from "@spt-aki/controllers/ProfileController";
import { IEmptyRequestData } from "@spt-aki/models/eft/common/IEmptyRequestData"; import { IEmptyRequestData } from "@spt-aki/models/eft/common/IEmptyRequestData";
import { IAchievement } from "@spt-aki/models/eft/common/tables/IAchievement";
import { IGetBodyResponseData } from "@spt-aki/models/eft/httpResponse/IGetBodyResponseData"; import { IGetBodyResponseData } from "@spt-aki/models/eft/httpResponse/IGetBodyResponseData";
import { ICompletedAchievementsResponse } from "@spt-aki/models/eft/profile/ICompletedAchievementsResponse"; import { ICompletedAchievementsResponse } from "@spt-aki/models/eft/profile/ICompletedAchievementsResponse";
import { IGetAchievementsResponse } from "@spt-aki/models/eft/profile/IGetAchievementsResponse"; import { IGetAchievementsResponse } from "@spt-aki/models/eft/profile/IGetAchievementsResponse";

View File

@ -48,7 +48,11 @@ export class BotCallbacks
* Handle singleplayer/settings/bot/difficulties * Handle singleplayer/settings/bot/difficulties
* @returns dictionary of every bot and its diffiulty settings * @returns dictionary of every bot and its diffiulty settings
*/ */
public getAllBotDifficulties(url: string, info: IEmptyRequestData, sessionID: string): Record<string, Difficulties> public getAllBotDifficulties(
url: string,
info: IEmptyRequestData,
sessionID: string,
): Record<string, Difficulties>
{ {
return this.httpResponse.noBody(this.botController.getAllBotDifficulties()); return this.httpResponse.noBody(this.botController.getAllBotDifficulties());
} }

View File

@ -46,7 +46,11 @@ export class CustomizationCallbacks
/** /**
* Handle CustomizationWear event * Handle CustomizationWear event
*/ */
public wearClothing(pmcData: IPmcData, body: IWearClothingRequestData, sessionID: string): IItemEventRouterResponse public wearClothing(
pmcData: IPmcData,
body: IWearClothingRequestData,
sessionID: string,
): IItemEventRouterResponse
{ {
return this.customizationController.wearClothing(pmcData, body, sessionID); return this.customizationController.wearClothing(pmcData, body, sessionID);
} }

View File

@ -88,7 +88,11 @@ export class DataCallbacks
* Handle client/account/customization * Handle client/account/customization
* @returns string[] * @returns string[]
*/ */
public getTemplateCharacter(url: string, info: IEmptyRequestData, sessionID: string): IGetBodyResponseData<string[]> public getTemplateCharacter(
url: string,
info: IEmptyRequestData,
sessionID: string,
): IGetBodyResponseData<string[]>
{ {
return this.httpResponse.getBody(this.databaseServer.getTables().templates.character); return this.httpResponse.getBody(this.databaseServer.getTables().templates.character);
} }

View File

@ -47,8 +47,7 @@ export class DialogueCallbacks implements OnUpdate
@inject("HttpResponseUtil") protected httpResponse: HttpResponseUtil, @inject("HttpResponseUtil") protected httpResponse: HttpResponseUtil,
@inject("DialogueController") protected dialogueController: DialogueController, @inject("DialogueController") protected dialogueController: DialogueController,
) )
{ {}
}
/** /**
* Handle client/friend/list * Handle client/friend/list
@ -276,11 +275,7 @@ export class DialogueCallbacks implements OnUpdate
return this.httpResponse.emptyArrayResponse(); return this.httpResponse.emptyArrayResponse();
} }
public createGroupMail( public createGroupMail(url: string, info: ICreateGroupMailRequest, sessionID: string): IGetBodyResponseData<any[]>
url: string,
info: ICreateGroupMailRequest,
sessionID: string,
): IGetBodyResponseData<any[]>
{ {
throw new Error("Method not implemented."); throw new Error("Method not implemented.");
} }
@ -294,11 +289,7 @@ export class DialogueCallbacks implements OnUpdate
throw new Error("Method not implemented."); throw new Error("Method not implemented.");
} }
public addUserToMail( public addUserToMail(url: string, info: IAddUserGroupMailRequest, sessionID: string): IGetBodyResponseData<any[]>
url: string,
info: IAddUserGroupMailRequest,
sessionID: string,
): IGetBodyResponseData<any[]>
{ {
throw new Error("Method not implemented."); throw new Error("Method not implemented.");
} }

View File

@ -56,7 +56,11 @@ export class GameCallbacks implements OnLoad
* Handle client/game/start * Handle client/game/start
* @returns IGameStartResponse * @returns IGameStartResponse
*/ */
public gameStart(url: string, info: IEmptyRequestData, sessionID: string): IGetBodyResponseData<IGameStartResponse> public gameStart(
url: string,
info: IEmptyRequestData,
sessionID: string,
): IGetBodyResponseData<IGameStartResponse>
{ {
const today = new Date().toUTCString(); const today = new Date().toUTCString();
const startTimeStampMS = Date.parse(today); const startTimeStampMS = Date.parse(today);

View File

@ -102,7 +102,11 @@ export class InventoryCallbacks
return this.inventoryController.foldItem(pmcData, body, sessionID); return this.inventoryController.foldItem(pmcData, body, sessionID);
} }
public toggleItem(pmcData: IPmcData, body: IInventoryToggleRequestData, sessionID: string): IItemEventRouterResponse public toggleItem(
pmcData: IPmcData,
body: IInventoryToggleRequestData,
sessionID: string,
): IItemEventRouterResponse
{ {
return this.inventoryController.toggleItem(pmcData, body, sessionID); return this.inventoryController.toggleItem(pmcData, body, sessionID);
} }

View File

@ -101,22 +101,14 @@ export class MatchCallbacks
/** Handle client/match/group/invite/decline */ /** Handle client/match/group/invite/decline */
// eslint-disable-next-line @typescript-eslint/no-unused-vars // eslint-disable-next-line @typescript-eslint/no-unused-vars
public declineGroupInvite( public declineGroupInvite(url: string, info: IRequestIdRequest, sessionId: string): IGetBodyResponseData<boolean>
url: string,
info: IRequestIdRequest,
sessionId: string,
): IGetBodyResponseData<boolean>
{ {
return this.httpResponse.getBody(true); return this.httpResponse.getBody(true);
} }
/** Handle client/match/group/invite/cancel */ /** Handle client/match/group/invite/cancel */
// eslint-disable-next-line @typescript-eslint/no-unused-vars // eslint-disable-next-line @typescript-eslint/no-unused-vars
public cancelGroupInvite( public cancelGroupInvite(url: string, info: IRequestIdRequest, sessionID: string): IGetBodyResponseData<boolean>
url: string,
info: IRequestIdRequest,
sessionID: string,
): IGetBodyResponseData<boolean>
{ {
return this.httpResponse.getBody(true); return this.httpResponse.getBody(true);
} }
@ -246,21 +238,13 @@ export class MatchCallbacks
} }
/** Handle client/match/group/raid/ready */ /** Handle client/match/group/raid/ready */
public raidReady( public raidReady(url: string, info: IEmptyRequestData, sessionId: string): IGetBodyResponseData<boolean>
url: string,
info: IEmptyRequestData,
sessionId: string,
): IGetBodyResponseData<boolean>
{ {
return this.httpResponse.getBody(true); return this.httpResponse.getBody(true);
} }
/** Handle client/match/group/raid/not-ready */ /** Handle client/match/group/raid/not-ready */
public notRaidReady( public notRaidReady(url: string, info: IEmptyRequestData, sessionId: string): IGetBodyResponseData<boolean>
url: string,
info: IEmptyRequestData,
sessionId: string,
): IGetBodyResponseData<boolean>
{ {
return this.httpResponse.getBody(true); return this.httpResponse.getBody(true);
} }

View File

@ -36,9 +36,10 @@ export class NotifierCallbacks
* Take our array of JSON message objects and cast them to JSON strings, so that they can then * Take our array of JSON message objects and cast them to JSON strings, so that they can then
* be sent to client as NEWLINE separated strings... yup. * be sent to client as NEWLINE separated strings... yup.
*/ */
this.notifierController.notifyAsync(tmpSessionID).then((messages: any) => this.notifierController
messages.map((message: any) => this.jsonUtil.serialize(message)).join("\n"), .notifyAsync(tmpSessionID)
).then(text => this.httpServerHelper.sendTextJson(resp, text)); .then((messages: any) => messages.map((message: any) => this.jsonUtil.serialize(message)).join("\n"))
.then((text) => this.httpServerHelper.sendTextJson(resp, text));
} }
/** Handle push/notifier/get */ /** Handle push/notifier/get */

View File

@ -17,7 +17,11 @@ export class WishlistCallbacks
} }
/** Handle RemoveFromWishList event */ /** Handle RemoveFromWishList event */
public removeFromWishlist(pmcData: IPmcData, body: IWishlistActionData, sessionID: string): IItemEventRouterResponse public removeFromWishlist(
pmcData: IPmcData,
body: IWishlistActionData,
sessionID: string,
): IItemEventRouterResponse
{ {
return this.wishlistController.removeFromWishList(pmcData, body, sessionID); return this.wishlistController.removeFromWishList(pmcData, body, sessionID);
} }

View File

@ -5,6 +5,7 @@ import { BotGenerator } from "@spt-aki/generators/BotGenerator";
import { BotDifficultyHelper } from "@spt-aki/helpers/BotDifficultyHelper"; import { BotDifficultyHelper } from "@spt-aki/helpers/BotDifficultyHelper";
import { BotHelper } from "@spt-aki/helpers/BotHelper"; import { BotHelper } from "@spt-aki/helpers/BotHelper";
import { ProfileHelper } from "@spt-aki/helpers/ProfileHelper"; import { ProfileHelper } from "@spt-aki/helpers/ProfileHelper";
import { MinMax } from "@spt-aki/models/common/MinMax";
import { Condition, IGenerateBotsRequestData } from "@spt-aki/models/eft/bot/IGenerateBotsRequestData"; import { Condition, IGenerateBotsRequestData } from "@spt-aki/models/eft/bot/IGenerateBotsRequestData";
import { IPmcData } from "@spt-aki/models/eft/common/IPmcData"; import { IPmcData } from "@spt-aki/models/eft/common/IPmcData";
import { IBotBase } from "@spt-aki/models/eft/common/tables/IBotBase"; import { IBotBase } from "@spt-aki/models/eft/common/tables/IBotBase";
@ -25,7 +26,6 @@ import { MatchBotDetailsCacheService } from "@spt-aki/services/MatchBotDetailsCa
import { SeasonalEventService } from "@spt-aki/services/SeasonalEventService"; import { SeasonalEventService } from "@spt-aki/services/SeasonalEventService";
import { ICloner } from "@spt-aki/utils/cloners/ICloner"; import { ICloner } from "@spt-aki/utils/cloners/ICloner";
import { RandomUtil } from "@spt-aki/utils/RandomUtil"; import { RandomUtil } from "@spt-aki/utils/RandomUtil";
import { MinMax } from "@spt-aki/models/common/MinMax";
@injectable() @injectable()
export class BotController export class BotController
@ -94,9 +94,9 @@ export class BotController
{ {
let difficulty = diffLevel.toLowerCase(); let difficulty = diffLevel.toLowerCase();
const raidConfig = this.applicationContext.getLatestValue(ContextVariableType.RAID_CONFIGURATION)?.getValue< const raidConfig = this.applicationContext
IGetRaidConfigurationRequestData .getLatestValue(ContextVariableType.RAID_CONFIGURATION)
>(); ?.getValue<IGetRaidConfigurationRequestData>();
if (!(raidConfig || ignoreRaidSettings)) if (!(raidConfig || ignoreRaidSettings))
{ {
this.logger.error( this.logger.error(
@ -109,9 +109,8 @@ export class BotController
const botDifficultyDropDownValue = raidConfig?.wavesSettings.botDifficulty.toLowerCase() ?? "asonline"; const botDifficultyDropDownValue = raidConfig?.wavesSettings.botDifficulty.toLowerCase() ?? "asonline";
if (botDifficultyDropDownValue !== "asonline") if (botDifficultyDropDownValue !== "asonline")
{ {
difficulty = this.botDifficultyHelper.convertBotDifficultyDropdownToBotDifficulty( difficulty
botDifficultyDropDownValue, = this.botDifficultyHelper.convertBotDifficultyDropdownToBotDifficulty(botDifficultyDropDownValue);
);
} }
let difficultySettings: Difficulty; let difficultySettings: Difficulty;
@ -147,7 +146,7 @@ export class BotController
const result = {}; const result = {};
const botDb = this.databaseServer.getTables().bots.types; const botDb = this.databaseServer.getTables().bots.types;
const botTypes = Object.keys(WildSpawnTypeNumber).filter(v => Number.isNaN(Number(v))); const botTypes = Object.keys(WildSpawnTypeNumber).filter((v) => Number.isNaN(Number(v)));
for (let botType of botTypes) for (let botType of botTypes)
{ {
const enumType = botType.toLowerCase(); const enumType = botType.toLowerCase();
@ -228,12 +227,13 @@ export class BotController
allPmcsHaveSameNameAsPlayer, allPmcsHaveSameNameAsPlayer,
pmcLevelRangeForMap, pmcLevelRangeForMap,
this.botConfig.presetBatch[condition.Role], this.botConfig.presetBatch[condition.Role],
false); false,
);
conditionPromises.push(this.generateWithBotDetails(condition, botGenerationDetails, sessionId)); conditionPromises.push(this.generateWithBotDetails(condition, botGenerationDetails, sessionId));
} }
await Promise.all(conditionPromises).then(p => Promise.all(p)); await Promise.all(conditionPromises).then((p) => Promise.all(p));
return []; return [];
} }
@ -254,8 +254,8 @@ export class BotController
allPmcsHaveSameNameAsPlayer: boolean, allPmcsHaveSameNameAsPlayer: boolean,
pmcLevelRangeForMap: MinMax, pmcLevelRangeForMap: MinMax,
botCountToGenerate: number, botCountToGenerate: number,
generateAsPmc: boolean): BotGenerationDetails generateAsPmc: boolean,
): BotGenerationDetails
{ {
return { return {
isPmc: generateAsPmc, isPmc: generateAsPmc,
@ -357,14 +357,17 @@ export class BotController
* @param request Bot generation request object * @param request Bot generation request object
* @returns Single IBotBase object * @returns Single IBotBase object
*/ */
protected async returnSingleBotFromCache(sessionId: string, request: IGenerateBotsRequestData): Promise<IBotBase[]> protected async returnSingleBotFromCache(
sessionId: string,
request: IGenerateBotsRequestData,
): Promise<IBotBase[]>
{ {
const pmcProfile = this.profileHelper.getPmcProfile(sessionId); const pmcProfile = this.profileHelper.getPmcProfile(sessionId);
const requestedBot = request.conditions[0]; const requestedBot = request.conditions[0];
const raidSettings = this.applicationContext.getLatestValue(ContextVariableType.RAID_CONFIGURATION)?.getValue< const raidSettings = this.applicationContext
IGetRaidConfigurationRequestData .getLatestValue(ContextVariableType.RAID_CONFIGURATION)
>(); ?.getValue<IGetRaidConfigurationRequestData>();
const pmcLevelRangeForMap const pmcLevelRangeForMap
= this.pmcConfig.locationSpecificPmcLevelOverride[raidSettings.location.toLowerCase()]; = this.pmcConfig.locationSpecificPmcLevelOverride[raidSettings.location.toLowerCase()];
@ -372,7 +375,7 @@ export class BotController
const condition: Condition = { const condition: Condition = {
Role: requestedBot.Role, Role: requestedBot.Role,
Limit: 5, Limit: 5,
Difficulty: requestedBot.Difficulty Difficulty: requestedBot.Difficulty,
}; };
const botGenerationDetails = this.getBotGenerationDetailsForWave( const botGenerationDetails = this.getBotGenerationDetailsForWave(
condition, condition,
@ -380,7 +383,8 @@ export class BotController
false, false,
pmcLevelRangeForMap, pmcLevelRangeForMap,
this.botConfig.presetBatch[requestedBot.Role], this.botConfig.presetBatch[requestedBot.Role],
false); false,
);
// Event bots need special actions to occur, set data up for them // Event bots need special actions to occur, set data up for them
const isEventBot = requestedBot.Role.toLowerCase().includes("event"); const isEventBot = requestedBot.Role.toLowerCase().includes("event");
@ -475,9 +479,9 @@ export class BotController
public getBotCap(): number public getBotCap(): number
{ {
const defaultMapCapId = "default"; const defaultMapCapId = "default";
const raidConfig = this.applicationContext.getLatestValue(ContextVariableType.RAID_CONFIGURATION).getValue< const raidConfig = this.applicationContext
IGetRaidConfigurationRequestData .getLatestValue(ContextVariableType.RAID_CONFIGURATION)
>(); .getValue<IGetRaidConfigurationRequestData>();
if (!raidConfig) if (!raidConfig)
{ {

View File

@ -42,11 +42,11 @@ export class BuildController
const defaultEquipmentPresetsClone = this.cloner.clone( const defaultEquipmentPresetsClone = this.cloner.clone(
this.databaseServer.getTables().templates.defaultEquipmentPresets, this.databaseServer.getTables().templates.defaultEquipmentPresets,
); );
const playerSecureContainer = profile.characters.pmc.Inventory.items?.find(x => const playerSecureContainer = profile.characters.pmc.Inventory.items?.find(
x.slotId === secureContainerSlotId, (x) => x.slotId === secureContainerSlotId,
); );
const firstDefaultItemsSecureContainer = defaultEquipmentPresetsClone[0]?.Items?.find(x => const firstDefaultItemsSecureContainer = defaultEquipmentPresetsClone[0]?.Items?.find(
x.slotId === secureContainerSlotId, (x) => x.slotId === secureContainerSlotId,
); );
if (playerSecureContainer && playerSecureContainer?._tpl !== firstDefaultItemsSecureContainer?._tpl) if (playerSecureContainer && playerSecureContainer?._tpl !== firstDefaultItemsSecureContainer?._tpl)
{ {
@ -54,7 +54,7 @@ export class BuildController
for (const defaultPreset of defaultEquipmentPresetsClone) for (const defaultPreset of defaultEquipmentPresetsClone)
{ {
// Find presets secure container // Find presets secure container
const secureContainer = defaultPreset.Items.find(item => item.slotId === secureContainerSlotId); const secureContainer = defaultPreset.Items.find((item) => item.slotId === secureContainerSlotId);
if (secureContainer) if (secureContainer)
{ {
secureContainer._tpl = playerSecureContainer._tpl; secureContainer._tpl = playerSecureContainer._tpl;
@ -83,15 +83,13 @@ export class BuildController
const newBuild: IWeaponBuild = { Id: body.Id, Name: body.Name, Root: body.Root, Items: body.Items }; const newBuild: IWeaponBuild = { Id: body.Id, Name: body.Name, Root: body.Root, Items: body.Items };
const savedWeaponBuilds = this.saveServer.getProfile(sessionId).userbuilds.weaponBuilds; const savedWeaponBuilds = this.saveServer.getProfile(sessionId).userbuilds.weaponBuilds;
const existingBuild = savedWeaponBuilds.find(x => x.Id === body.Id); const existingBuild = savedWeaponBuilds.find((x) => x.Id === body.Id);
if (existingBuild) if (existingBuild)
{ {
// exists, replace // exists, replace
this.saveServer.getProfile(sessionId).userbuilds.weaponBuilds.splice( this.saveServer
savedWeaponBuilds.indexOf(existingBuild), .getProfile(sessionId)
1, .userbuilds.weaponBuilds.splice(savedWeaponBuilds.indexOf(existingBuild), 1, newBuild);
newBuild,
);
} }
else else
{ {
@ -106,8 +104,8 @@ export class BuildController
const buildType = "equipmentBuilds"; const buildType = "equipmentBuilds";
const pmcData = this.profileHelper.getPmcProfile(sessionID); const pmcData = this.profileHelper.getPmcProfile(sessionID);
const existingSavedEquipmentBuilds: IEquipmentBuild[] = this.saveServer.getProfile(sessionID) const existingSavedEquipmentBuilds: IEquipmentBuild[]
.userbuilds[buildType]; = this.saveServer.getProfile(sessionID).userbuilds[buildType];
// Replace duplicate ID's. The first item is the base item. // Replace duplicate ID's. The first item is the base item.
// Root ID and the base item ID need to match. // Root ID and the base item ID need to match.
@ -121,17 +119,15 @@ export class BuildController
Items: request.Items, Items: request.Items,
}; };
const existingBuild = existingSavedEquipmentBuilds.find(build => const existingBuild = existingSavedEquipmentBuilds.find(
build.Name === request.Name || build.Id === request.Id, (build) => build.Name === request.Name || build.Id === request.Id,
); );
if (existingBuild) if (existingBuild)
{ {
// Already exists, replace // Already exists, replace
this.saveServer.getProfile(sessionID).userbuilds[buildType].splice( this.saveServer
existingSavedEquipmentBuilds.indexOf(existingBuild), .getProfile(sessionID)
1, .userbuilds[buildType].splice(existingSavedEquipmentBuilds.indexOf(existingBuild), 1, newBuild);
newBuild,
);
} }
else else
{ {
@ -154,7 +150,7 @@ export class BuildController
const magazineBuilds = profile.userbuilds.magazineBuilds; const magazineBuilds = profile.userbuilds.magazineBuilds;
// Check for id in weapon array first // Check for id in weapon array first
const matchingWeaponBuild = weaponBuilds.find(weaponBuild => weaponBuild.Id === idToRemove); const matchingWeaponBuild = weaponBuilds.find((weaponBuild) => weaponBuild.Id === idToRemove);
if (matchingWeaponBuild) if (matchingWeaponBuild)
{ {
weaponBuilds.splice(weaponBuilds.indexOf(matchingWeaponBuild), 1); weaponBuilds.splice(weaponBuilds.indexOf(matchingWeaponBuild), 1);
@ -163,7 +159,7 @@ export class BuildController
} }
// Id not found in weapons, try equipment // Id not found in weapons, try equipment
const matchingEquipmentBuild = equipmentBuilds.find(equipmentBuild => equipmentBuild.Id === idToRemove); const matchingEquipmentBuild = equipmentBuilds.find((equipmentBuild) => equipmentBuild.Id === idToRemove);
if (matchingEquipmentBuild) if (matchingEquipmentBuild)
{ {
equipmentBuilds.splice(equipmentBuilds.indexOf(matchingEquipmentBuild), 1); equipmentBuilds.splice(equipmentBuilds.indexOf(matchingEquipmentBuild), 1);
@ -172,7 +168,7 @@ export class BuildController
} }
// Id not found in weapons/equipment, try mags // Id not found in weapons/equipment, try mags
const matchingMagazineBuild = magazineBuilds.find(magBuild => magBuild.Id === idToRemove); const matchingMagazineBuild = magazineBuilds.find((magBuild) => magBuild.Id === idToRemove);
if (matchingMagazineBuild) if (matchingMagazineBuild)
{ {
magazineBuilds.splice(magazineBuilds.indexOf(matchingMagazineBuild), 1); magazineBuilds.splice(magazineBuilds.indexOf(matchingMagazineBuild), 1);
@ -207,7 +203,7 @@ export class BuildController
profile.userbuilds.magazineBuilds = []; profile.userbuilds.magazineBuilds = [];
} }
const existingArrayId = profile.userbuilds.magazineBuilds.findIndex(item => item.Name === request.Name); const existingArrayId = profile.userbuilds.magazineBuilds.findIndex((item) => item.Name === request.Name);
if (existingArrayId === -1) if (existingArrayId === -1)
{ {

View File

@ -42,10 +42,10 @@ export class CustomizationController
const suits = this.databaseServer.getTables().traders[traderID].suits; const suits = this.databaseServer.getTables().traders[traderID].suits;
// Get an inner join of clothing from templates.customization and Ragman's suits array // Get an inner join of clothing from templates.customization and Ragman's suits array
const matchingSuits = suits.filter(x => x.suiteId in templates); const matchingSuits = suits.filter((x) => x.suiteId in templates);
// Return all suits that have a side array containing the players side (usec/bear) // Return all suits that have a side array containing the players side (usec/bear)
return matchingSuits.filter(x => templates[x.suiteId]._props.Side.includes(pmcData.Info.Side)); return matchingSuits.filter((x) => templates[x.suiteId]._props.Side.includes(pmcData.Info.Side));
} }
/** /**
@ -132,7 +132,7 @@ export class CustomizationController
protected getTraderClothingOffer(sessionId: string, offerId: string): ISuit protected getTraderClothingOffer(sessionId: string, offerId: string): ISuit
{ {
return this.getAllTraderSuits(sessionId).find(x => x._id === offerId); return this.getAllTraderSuits(sessionId).find((x) => x._id === offerId);
} }
/** /**
@ -180,7 +180,7 @@ export class CustomizationController
output: IItemEventRouterResponse, output: IItemEventRouterResponse,
): void ): void
{ {
const relatedItem = pmcData.Inventory.items.find(x => x._id === clothingItem.id); const relatedItem = pmcData.Inventory.items.find((x) => x._id === clothingItem.id);
if (!relatedItem) if (!relatedItem)
{ {
this.logger.error( this.logger.error(

View File

@ -1,6 +1,8 @@
import { inject, injectAll, injectable } from "tsyringe"; import { inject, injectAll, injectable } from "tsyringe";
import { IDialogueChatBot } from "@spt-aki/helpers/Dialogue/IDialogueChatBot"; import { IDialogueChatBot } from "@spt-aki/helpers/Dialogue/IDialogueChatBot";
import { DialogueHelper } from "@spt-aki/helpers/DialogueHelper"; import { DialogueHelper } from "@spt-aki/helpers/DialogueHelper";
import { IFriendRequestData } from "@spt-aki/models/eft/dialog/IFriendRequestData";
import { IFriendRequestSendResponse } from "@spt-aki/models/eft/dialog/IFriendRequestSendResponse";
import { IGetAllAttachmentsResponse } from "@spt-aki/models/eft/dialog/IGetAllAttachmentsResponse"; import { IGetAllAttachmentsResponse } from "@spt-aki/models/eft/dialog/IGetAllAttachmentsResponse";
import { IGetFriendListDataResponse } from "@spt-aki/models/eft/dialog/IGetFriendListDataResponse"; import { IGetFriendListDataResponse } from "@spt-aki/models/eft/dialog/IGetFriendListDataResponse";
import { IGetMailDialogViewRequestData } from "@spt-aki/models/eft/dialog/IGetMailDialogViewRequestData"; import { IGetMailDialogViewRequestData } from "@spt-aki/models/eft/dialog/IGetMailDialogViewRequestData";
@ -15,8 +17,6 @@ import { ConfigServer } from "@spt-aki/servers/ConfigServer";
import { SaveServer } from "@spt-aki/servers/SaveServer"; import { SaveServer } from "@spt-aki/servers/SaveServer";
import { MailSendService } from "@spt-aki/services/MailSendService"; import { MailSendService } from "@spt-aki/services/MailSendService";
import { TimeUtil } from "@spt-aki/utils/TimeUtil"; import { TimeUtil } from "@spt-aki/utils/TimeUtil";
import { IFriendRequestData } from "@spt-aki/models/eft/dialog/IFriendRequestData";
import { IFriendRequestSendResponse } from "@spt-aki/models/eft/dialog/IFriendRequestSendResponse";
@injectable() @injectable()
export class DialogueController export class DialogueController
@ -35,21 +35,21 @@ export class DialogueController
// if give command is disabled or commando commands are disabled // if give command is disabled or commando commands are disabled
if (!coreConfigs.features?.chatbotFeatures?.commandoEnabled) if (!coreConfigs.features?.chatbotFeatures?.commandoEnabled)
{ {
const sptCommando = this.dialogueChatBots.find(c => const sptCommando = this.dialogueChatBots.find(
c.getChatBot()._id.toLocaleLowerCase() === "sptcommando", (c) => c.getChatBot()._id.toLocaleLowerCase() === "sptcommando",
); );
this.dialogueChatBots.splice(this.dialogueChatBots.indexOf(sptCommando), 1); this.dialogueChatBots.splice(this.dialogueChatBots.indexOf(sptCommando), 1);
} }
if (!coreConfigs.features?.chatbotFeatures?.sptFriendEnabled) if (!coreConfigs.features?.chatbotFeatures?.sptFriendEnabled)
{ {
const sptFriend = this.dialogueChatBots.find(c => c.getChatBot()._id.toLocaleLowerCase() === "sptFriend"); const sptFriend = this.dialogueChatBots.find((c) => c.getChatBot()._id.toLocaleLowerCase() === "sptFriend");
this.dialogueChatBots.splice(this.dialogueChatBots.indexOf(sptFriend), 1); this.dialogueChatBots.splice(this.dialogueChatBots.indexOf(sptFriend), 1);
} }
} }
public registerChatBot(chatBot: IDialogueChatBot): void public registerChatBot(chatBot: IDialogueChatBot): void
{ {
if (this.dialogueChatBots.some(cb => cb.getChatBot()._id === chatBot.getChatBot()._id)) if (this.dialogueChatBots.some((cb) => cb.getChatBot()._id === chatBot.getChatBot()._id))
{ {
throw new Error(`The chat bot ${chatBot.getChatBot()._id} being registered already exists!`); throw new Error(`The chat bot ${chatBot.getChatBot()._id} being registered already exists!`);
} }
@ -74,7 +74,7 @@ export class DialogueController
public getFriendList(sessionID: string): IGetFriendListDataResponse public getFriendList(sessionID: string): IGetFriendListDataResponse
{ {
// Force a fake friend called SPT into friend list // Force a fake friend called SPT into friend list
return { Friends: this.dialogueChatBots.map(v => v.getChatBot()), Ignore: [], InIgnoreList: [] }; return { Friends: this.dialogueChatBots.map((v) => v.getChatBot()), Ignore: [], InIgnoreList: [] };
} }
/** /**
@ -133,7 +133,7 @@ export class DialogueController
// User to user messages are special in that they need the player to exist in them, add if they don't // User to user messages are special in that they need the player to exist in them, add if they don't
if ( if (
messageType === MessageType.USER_MESSAGE messageType === MessageType.USER_MESSAGE
&& !dialog.Users?.find(userDialog => userDialog._id === profile.characters.pmc.sessionId) && !dialog.Users?.find((userDialog) => userDialog._id === profile.characters.pmc.sessionId)
) )
{ {
if (!dialog.Users) if (!dialog.Users)
@ -209,7 +209,7 @@ export class DialogueController
if (request.type === MessageType.USER_MESSAGE) if (request.type === MessageType.USER_MESSAGE)
{ {
profile.dialogues[request.dialogId].Users = []; profile.dialogues[request.dialogId].Users = [];
const chatBot = this.dialogueChatBots.find(cb => cb.getChatBot()._id === request.dialogId); const chatBot = this.dialogueChatBots.find((cb) => cb.getChatBot()._id === request.dialogId);
if (chatBot) if (chatBot)
{ {
profile.dialogues[request.dialogId].Users.push(chatBot.getChatBot()); profile.dialogues[request.dialogId].Users.push(chatBot.getChatBot());
@ -233,7 +233,7 @@ export class DialogueController
{ {
result.push(...dialogUsers); result.push(...dialogUsers);
if (!result.find(userDialog => userDialog._id === fullProfile.info.id)) if (!result.find((userDialog) => userDialog._id === fullProfile.info.id))
{ {
// Player doesnt exist, add them in before returning // Player doesnt exist, add them in before returning
const pmcProfile = fullProfile.characters.pmc; const pmcProfile = fullProfile.characters.pmc;
@ -281,7 +281,7 @@ export class DialogueController
*/ */
protected messagesHaveUncollectedRewards(messages: Message[]): boolean protected messagesHaveUncollectedRewards(messages: Message[]): boolean
{ {
return messages.some(message => message.items?.data?.length > 0); return messages.some((message) => message.items?.data?.length > 0);
} }
/** /**
@ -378,10 +378,11 @@ export class DialogueController
{ {
this.mailSendService.sendPlayerMessageToNpc(sessionId, request.dialogId, request.text); this.mailSendService.sendPlayerMessageToNpc(sessionId, request.dialogId, request.text);
return this.dialogueChatBots.find(cb => cb.getChatBot()._id === request.dialogId)?.handleMessage( return (
sessionId, this.dialogueChatBots
request, .find((cb) => cb.getChatBot()._id === request.dialogId)
) ?? request.dialogId; ?.handleMessage(sessionId, request) ?? request.dialogId
);
} }
/** /**
@ -394,7 +395,7 @@ export class DialogueController
{ {
const timeNow = this.timeUtil.getTimestamp(); const timeNow = this.timeUtil.getTimestamp();
const dialogs = this.dialogueHelper.getDialogsForProfile(sessionId); const dialogs = this.dialogueHelper.getDialogsForProfile(sessionId);
return dialogs[dialogueId].messages.filter(message => timeNow < message.dt + message.maxStorageTime); return dialogs[dialogueId].messages.filter((message) => timeNow < message.dt + message.maxStorageTime);
} }
/** /**
@ -404,7 +405,7 @@ export class DialogueController
*/ */
protected getMessagesWithAttachments(messages: Message[]): Message[] protected getMessagesWithAttachments(messages: Message[]): Message[]
{ {
return messages.filter(message => message.items?.data?.length > 0); return messages.filter((message) => message.items?.data?.length > 0);
} }
/** /**
@ -453,12 +454,8 @@ export class DialogueController
} }
/** Handle client/friend/request/send */ /** Handle client/friend/request/send */
public sendFriendRequest( public sendFriendRequest(sessionID: string, request: IFriendRequestData): IFriendRequestSendResponse
sessionID: string,
request: IFriendRequestData
): IFriendRequestSendResponse
{ {
return { status: 0, requestId: "12345", retryAfter: 600 } return { status: 0, requestId: "12345", retryAfter: 600 };
} }
} }

View File

@ -359,8 +359,8 @@ export class GameController
for (const positionToAdd of positionsToAdd) for (const positionToAdd of positionsToAdd)
{ {
// Exists already, add new items to existing positions pool // Exists already, add new items to existing positions pool
const existingLootPosition = mapLooseLoot.spawnpoints.find(x => const existingLootPosition = mapLooseLoot.spawnpoints.find(
x.template.Id === positionToAdd.template.Id, (x) => x.template.Id === positionToAdd.template.Id,
); );
if (existingLootPosition) if (existingLootPosition)
{ {
@ -390,7 +390,7 @@ export class GameController
const mapLootAdjustmentsDict = adjustments[mapId]; const mapLootAdjustmentsDict = adjustments[mapId];
for (const lootKey in mapLootAdjustmentsDict) for (const lootKey in mapLootAdjustmentsDict)
{ {
const lootPostionToAdjust = mapLooseLootData.spawnpoints.find(x => x.template.Id === lootKey); const lootPostionToAdjust = mapLooseLootData.spawnpoints.find((x) => x.template.Id === lootKey);
if (!lootPostionToAdjust) if (!lootPostionToAdjust)
{ {
this.logger.warning(`Unable to adjust loot position: ${lootKey} on map: ${mapId}`); this.logger.warning(`Unable to adjust loot position: ${lootKey} on map: ${mapId}`);
@ -423,7 +423,7 @@ export class GameController
for (const botToLimit of this.locationConfig.botTypeLimits[mapId]) for (const botToLimit of this.locationConfig.botTypeLimits[mapId])
{ {
const index = map.base.MinMaxBots.findIndex(x => x.WildSpawnType === botToLimit.type); const index = map.base.MinMaxBots.findIndex((x) => x.WildSpawnType === botToLimit.type);
if (index !== -1) if (index !== -1)
{ {
// Existing bot type found in MinMaxBots array, edit // Existing bot type found in MinMaxBots array, edit
@ -452,8 +452,8 @@ export class GameController
{ {
const profile = this.profileHelper.getPmcProfile(sessionID); const profile = this.profileHelper.getPmcProfile(sessionID);
const gameTime const gameTime
= profile.Stats?.Eft.OverallCounters.Items?.find(counter => = profile.Stats?.Eft.OverallCounters.Items?.find(
counter.Key.includes("LifeTime") && counter.Key.includes("Pmc"), (counter) => counter.Key.includes("LifeTime") && counter.Key.includes("Pmc"),
)?.Value ?? 0; )?.Value ?? 0;
const config: IGameConfigResponse = { const config: IGameConfigResponse = {
@ -598,12 +598,13 @@ export class GameController
let hpRegenPerHour = 456.6; let hpRegenPerHour = 456.6;
// Set new values, whatever is smallest // Set new values, whatever is smallest
energyRegenPerHour += pmcProfile.Bonuses.filter(bonus => bonus.type === BonusType.ENERGY_REGENERATION) energyRegenPerHour += pmcProfile.Bonuses.filter(
.reduce((sum, curr) => sum + curr.value, 0); (bonus) => bonus.type === BonusType.ENERGY_REGENERATION,
hydrationRegenPerHour += pmcProfile.Bonuses.filter(bonus =>
bonus.type === BonusType.HYDRATION_REGENERATION,
).reduce((sum, curr) => sum + curr.value, 0); ).reduce((sum, curr) => sum + curr.value, 0);
hpRegenPerHour += pmcProfile.Bonuses.filter(bonus => bonus.type === BonusType.HEALTH_REGENERATION).reduce( hydrationRegenPerHour += pmcProfile.Bonuses.filter(
(bonus) => bonus.type === BonusType.HYDRATION_REGENERATION,
).reduce((sum, curr) => sum + curr.value, 0);
hpRegenPerHour += pmcProfile.Bonuses.filter((bonus) => bonus.type === BonusType.HEALTH_REGENERATION).reduce(
(sum, curr) => sum + curr.value, (sum, curr) => sum + curr.value,
0, 0,
); );
@ -738,13 +739,13 @@ export class GameController
const currentTimeStamp = this.timeUtil.getTimestamp(); const currentTimeStamp = this.timeUtil.getTimestamp();
// One day post-profile creation // One day post-profile creation
if (currentTimeStamp > (timeStampProfileCreated + oneDaySeconds)) if (currentTimeStamp > timeStampProfileCreated + oneDaySeconds)
{ {
this.giftService.sendPraporStartingGift(pmcProfile.sessionId, 1); this.giftService.sendPraporStartingGift(pmcProfile.sessionId, 1);
} }
// Two day post-profile creation // Two day post-profile creation
if (currentTimeStamp > (timeStampProfileCreated + oneDaySeconds * 2)) if (currentTimeStamp > timeStampProfileCreated + oneDaySeconds * 2)
{ {
this.giftService.sendPraporStartingGift(pmcProfile.sessionId, 2); this.giftService.sendPraporStartingGift(pmcProfile.sessionId, 2);
} }
@ -841,8 +842,11 @@ export class GameController
{ {
const modDetails = activeMods[modKey]; const modDetails = activeMods[modKey];
if ( if (
fullProfile.aki.mods.some(x => fullProfile.aki.mods.some(
x.author === modDetails.author && x.name === modDetails.name && x.version === modDetails.version, (x) =>
x.author === modDetails.author
&& x.name === modDetails.name
&& x.version === modDetails.version,
) )
) )
{ {
@ -957,8 +961,8 @@ export class GameController
protected adjustLabsRaiderSpawnRate(): void protected adjustLabsRaiderSpawnRate(): void
{ {
const labsBase = this.databaseServer.getTables().locations.laboratory.base; const labsBase = this.databaseServer.getTables().locations.laboratory.base;
const nonTriggerLabsBossSpawns = labsBase.BossLocationSpawn.filter(x => const nonTriggerLabsBossSpawns = labsBase.BossLocationSpawn.filter(
x.TriggerId === "" && x.TriggerName === "", (x) => x.TriggerId === "" && x.TriggerName === "",
); );
if (nonTriggerLabsBossSpawns) if (nonTriggerLabsBossSpawns)
{ {

View File

@ -60,12 +60,16 @@ export class HealthController
* @param sessionID Player id * @param sessionID Player id
* @returns IItemEventRouterResponse * @returns IItemEventRouterResponse
*/ */
public offraidHeal(pmcData: IPmcData, request: IOffraidHealRequestData, sessionID: string): IItemEventRouterResponse public offraidHeal(
pmcData: IPmcData,
request: IOffraidHealRequestData,
sessionID: string,
): IItemEventRouterResponse
{ {
const output = this.eventOutputHolder.getOutput(sessionID); const output = this.eventOutputHolder.getOutput(sessionID);
// Update medkit used (hpresource) // Update medkit used (hpresource)
const healingItemToUse = pmcData.Inventory.items.find(item => item._id === request.item); const healingItemToUse = pmcData.Inventory.items.find((item) => item._id === request.item);
if (!healingItemToUse) if (!healingItemToUse)
{ {
const errorMessage = this.localisationService.getText( const errorMessage = this.localisationService.getText(
@ -113,7 +117,7 @@ export class HealthController
const output = this.eventOutputHolder.getOutput(sessionID); const output = this.eventOutputHolder.getOutput(sessionID);
let resourceLeft = 0; let resourceLeft = 0;
const itemToConsume = pmcData.Inventory.items.find(x => x._id === request.item); const itemToConsume = pmcData.Inventory.items.find((x) => x._id === request.item);
if (!itemToConsume) if (!itemToConsume)
{ {
// Item not found, very bad // Item not found, very bad

View File

@ -11,7 +11,6 @@ import {
HideoutArea, HideoutArea,
ITaskConditionCounter, ITaskConditionCounter,
Product, Product,
Production,
ScavCase, ScavCase,
} from "@spt-aki/models/eft/common/tables/IBotBase"; } from "@spt-aki/models/eft/common/tables/IBotBase";
import { Item } from "@spt-aki/models/eft/common/tables/IItem"; import { Item } from "@spt-aki/models/eft/common/tables/IItem";
@ -105,7 +104,7 @@ export class HideoutController
{ {
const items = request.items.map((reqItem) => const items = request.items.map((reqItem) =>
{ {
const item = pmcData.Inventory.items.find(invItem => invItem._id === reqItem.id); const item = pmcData.Inventory.items.find((invItem) => invItem._id === reqItem.id);
return { inventoryItem: item, requestedItem: reqItem }; return { inventoryItem: item, requestedItem: reqItem };
}); });
@ -138,7 +137,7 @@ export class HideoutController
} }
// Construction time management // Construction time management
const profileHideoutArea = pmcData.Hideout.Areas.find(area => area.type === request.areaType); const profileHideoutArea = pmcData.Hideout.Areas.find((area) => area.type === request.areaType);
if (!profileHideoutArea) if (!profileHideoutArea)
{ {
this.logger.error(this.localisationService.getText("hideout-unable_to_find_area", request.areaType)); this.logger.error(this.localisationService.getText("hideout-unable_to_find_area", request.areaType));
@ -147,9 +146,9 @@ export class HideoutController
return; return;
} }
const hideoutDataDb = this.databaseServer.getTables().hideout.areas.find(area => const hideoutDataDb = this.databaseServer
area.type === request.areaType, .getTables()
); .hideout.areas.find((area) => area.type === request.areaType);
if (!hideoutDataDb) if (!hideoutDataDb)
{ {
this.logger.error( this.logger.error(
@ -191,7 +190,7 @@ export class HideoutController
{ {
const db = this.databaseServer.getTables(); const db = this.databaseServer.getTables();
const profileHideoutArea = pmcData.Hideout.Areas.find(area => area.type === request.areaType); const profileHideoutArea = pmcData.Hideout.Areas.find((area) => area.type === request.areaType);
if (!profileHideoutArea) if (!profileHideoutArea)
{ {
this.logger.error(this.localisationService.getText("hideout-unable_to_find_area", request.areaType)); this.logger.error(this.localisationService.getText("hideout-unable_to_find_area", request.areaType));
@ -205,7 +204,7 @@ export class HideoutController
profileHideoutArea.completeTime = 0; profileHideoutArea.completeTime = 0;
profileHideoutArea.constructing = false; profileHideoutArea.constructing = false;
const hideoutData = db.hideout.areas.find(area => area.type === profileHideoutArea.type); const hideoutData = db.hideout.areas.find((area) => area.type === profileHideoutArea.type);
if (!hideoutData) if (!hideoutData)
{ {
this.logger.error( this.logger.error(
@ -263,11 +262,11 @@ export class HideoutController
*/ */
protected checkAndUpgradeWall(pmcData: IPmcData): void protected checkAndUpgradeWall(pmcData: IPmcData): void
{ {
const medStation = pmcData.Hideout.Areas.find(area => area.type === HideoutAreas.MEDSTATION); const medStation = pmcData.Hideout.Areas.find((area) => area.type === HideoutAreas.MEDSTATION);
const waterCollector = pmcData.Hideout.Areas.find(area => area.type === HideoutAreas.WATER_COLLECTOR); const waterCollector = pmcData.Hideout.Areas.find((area) => area.type === HideoutAreas.WATER_COLLECTOR);
if (medStation?.level >= 1 && waterCollector?.level >= 1) if (medStation?.level >= 1 && waterCollector?.level >= 1)
{ {
const wall = pmcData.Hideout.Areas.find(area => area.type === HideoutAreas.EMERGENCY_WALL); const wall = pmcData.Hideout.Areas.find((area) => area.type === HideoutAreas.EMERGENCY_WALL);
if (wall?.level === 0) if (wall?.level === 0)
{ {
wall.level = 3; wall.level = 3;
@ -309,9 +308,9 @@ export class HideoutController
} }
// Some areas like gun stand have a child area linked to it, it needs to do the same as above // 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 => const childDbArea = this.databaseServer
x.parentArea === dbHideoutArea._id, .getTables()
); .hideout.areas.find((x) => x.parentArea === dbHideoutArea._id);
if (childDbArea) if (childDbArea)
{ {
// Add key/value to `hideoutAreaStashes` dictionary - used to link hideout area to inventory stash by its id // Add key/value to `hideoutAreaStashes` dictionary - used to link hideout area to inventory stash by its id
@ -321,8 +320,8 @@ export class HideoutController
} }
// Set child area level to same as parent area // Set child area level to same as parent area
pmcData.Hideout.Areas.find(x => x.type === childDbArea.type).level = pmcData.Hideout.Areas.find(x => pmcData.Hideout.Areas.find((x) => x.type === childDbArea.type).level = pmcData.Hideout.Areas.find(
x.type === profileParentHideoutArea.type, (x) => x.type === profileParentHideoutArea.type,
).level; ).level;
// Add/upgrade stash item in player inventory // Add/upgrade stash item in player inventory
@ -340,9 +339,13 @@ export class HideoutController
* @param dbHideoutData Hideout area from db being upgraded * @param dbHideoutData Hideout area from db being upgraded
* @param hideoutStage Stage area upgraded to * @param hideoutStage Stage area upgraded to
*/ */
protected addUpdateInventoryItemToProfile(pmcData: IPmcData, dbHideoutData: IHideoutArea, hideoutStage: Stage): void protected addUpdateInventoryItemToProfile(
pmcData: IPmcData,
dbHideoutData: IHideoutArea,
hideoutStage: Stage,
): void
{ {
const existingInventoryItem = pmcData.Inventory.items.find(x => x._id === dbHideoutData._id); const existingInventoryItem = pmcData.Inventory.items.find((x) => x._id === dbHideoutData._id);
if (existingInventoryItem) if (existingInventoryItem)
{ {
// Update existing items container tpl to point to new id (tpl) // Update existing items container tpl to point to new id (tpl)
@ -399,11 +402,11 @@ export class HideoutController
const itemsToAdd = Object.entries(addItemToHideoutRequest.items).map((kvp) => const itemsToAdd = Object.entries(addItemToHideoutRequest.items).map((kvp) =>
{ {
const item = pmcData.Inventory.items.find(invItem => invItem._id === kvp[1].id); const item = pmcData.Inventory.items.find((invItem) => invItem._id === kvp[1].id);
return { inventoryItem: item, requestedItem: kvp[1], slot: kvp[0] }; return { inventoryItem: item, requestedItem: kvp[1], slot: kvp[0] };
}); });
const hideoutArea = pmcData.Hideout.Areas.find(area => area.type === addItemToHideoutRequest.areaType); const hideoutArea = pmcData.Hideout.Areas.find((area) => area.type === addItemToHideoutRequest.areaType);
if (!hideoutArea) if (!hideoutArea)
{ {
this.logger.error( this.logger.error(
@ -430,12 +433,14 @@ export class HideoutController
// Add item to area.slots // Add item to area.slots
const destinationLocationIndex = Number(item.slot); const destinationLocationIndex = Number(item.slot);
const hideoutSlotIndex = hideoutArea.slots.findIndex(x => x.locationIndex === destinationLocationIndex); const hideoutSlotIndex = hideoutArea.slots.findIndex((x) => x.locationIndex === destinationLocationIndex);
hideoutArea.slots[hideoutSlotIndex].item = [{ hideoutArea.slots[hideoutSlotIndex].item = [
_id: item.inventoryItem._id, {
_tpl: item.inventoryItem._tpl, _id: item.inventoryItem._id,
upd: item.inventoryItem.upd, _tpl: item.inventoryItem._tpl,
}]; upd: item.inventoryItem.upd,
},
];
this.inventoryHelper.removeItem(pmcData, item.inventoryItem._id, sessionID, output); this.inventoryHelper.removeItem(pmcData, item.inventoryItem._id, sessionID, output);
} }
@ -462,7 +467,7 @@ export class HideoutController
{ {
const output = this.eventOutputHolder.getOutput(sessionID); const output = this.eventOutputHolder.getOutput(sessionID);
const hideoutArea = pmcData.Hideout.Areas.find(area => area.type === request.areaType); const hideoutArea = pmcData.Hideout.Areas.find((area) => area.type === request.areaType);
if (!hideoutArea) if (!hideoutArea)
{ {
this.logger.error(this.localisationService.getText("hideout-unable_to_find_area", request.areaType)); this.logger.error(this.localisationService.getText("hideout-unable_to_find_area", request.areaType));
@ -519,7 +524,7 @@ export class HideoutController
const slotIndexToRemove = removeResourceRequest.slots[0]; const slotIndexToRemove = removeResourceRequest.slots[0];
// Assume only one item in slot // Assume only one item in slot
const itemToReturn = hideoutArea.slots.find(slot => slot.locationIndex === slotIndexToRemove).item[0]; const itemToReturn = hideoutArea.slots.find((slot) => slot.locationIndex === slotIndexToRemove).item[0];
const request: IAddItemDirectRequest = { const request: IAddItemDirectRequest = {
itemWithModsToAdd: [itemToReturn], itemWithModsToAdd: [itemToReturn],
@ -536,7 +541,7 @@ export class HideoutController
} }
// Remove items from slot, locationIndex remains // Remove items from slot, locationIndex remains
const hideoutSlotIndex = hideoutArea.slots.findIndex(slot => slot.locationIndex === slotIndexToRemove); const hideoutSlotIndex = hideoutArea.slots.findIndex((slot) => slot.locationIndex === slotIndexToRemove);
hideoutArea.slots[hideoutSlotIndex].item = undefined; hideoutArea.slots[hideoutSlotIndex].item = undefined;
return output; return output;
@ -561,7 +566,7 @@ export class HideoutController
// 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) // 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); this.hideoutHelper.updatePlayerHideout(sessionID);
const hideoutArea = pmcData.Hideout.Areas.find(area => area.type === request.areaType); const hideoutArea = pmcData.Hideout.Areas.find((area) => area.type === request.areaType);
if (!hideoutArea) if (!hideoutArea)
{ {
this.logger.error(this.localisationService.getText("hideout-unable_to_find_area", request.areaType)); this.logger.error(this.localisationService.getText("hideout-unable_to_find_area", request.areaType));
@ -591,20 +596,20 @@ export class HideoutController
this.hideoutHelper.registerProduction(pmcData, body, sessionID); this.hideoutHelper.registerProduction(pmcData, body, sessionID);
// Find the recipe of the production // Find the recipe of the production
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.cloner.clone( const recipeRequirementsClone = this.cloner.clone(
recipe.requirements.filter(i => i.type === "Item" || i.type === "Tool"), 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); const itemsToDelete = body.items.concat(body.tools);
for (const itemToDelete of itemsToDelete) 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.templateId === itemToCheck._tpl, (requirement) => requirement.templateId === itemToCheck._tpl,
); );
// Handle tools not having a `count`, but always only requiring 1 // Handle tools not having a `count`, but always only requiring 1
@ -644,7 +649,7 @@ export class HideoutController
for (const requestedItem of body.items) for (const requestedItem of body.items)
{ {
const inventoryItem = pmcData.Inventory.items.find(item => item._id === requestedItem.id); const inventoryItem = pmcData.Inventory.items.find((item) => item._id === requestedItem.id);
if (!inventoryItem) if (!inventoryItem)
{ {
this.logger.error( this.logger.error(
@ -666,7 +671,7 @@ export class HideoutController
} }
} }
const recipe = this.databaseServer.getTables().hideout.scavcase.find(r => r._id === body.recipeId); const recipe = this.databaseServer.getTables().hideout.scavcase.find((r) => r._id === body.recipeId);
if (!recipe) if (!recipe)
{ {
this.logger.error( this.logger.error(
@ -678,12 +683,14 @@ export class HideoutController
// @Important: Here we need to be very exact: // @Important: Here we need to be very exact:
// - normal recipe: Production time value is stored in attribute "productionTime" with small "p" // - normal recipe: Production time value is stored in attribute "productionTime" with small "p"
// - scav case recipe: Production time value is stored in attribute "ProductionTime" with capital "P" // - scav case recipe: Production time value is stored in attribute "ProductionTime" with capital "P"
const adjustedCraftTime = recipe.ProductionTime - this.hideoutHelper.getSkillProductionTimeReduction( const adjustedCraftTime
pmcData, = recipe.ProductionTime
recipe.ProductionTime, - this.hideoutHelper.getSkillProductionTimeReduction(
SkillTypes.CRAFTING, pmcData,
this.databaseServer.getTables().globals.config.SkillsSettings.Crafting.CraftTimeReductionPerLevel, recipe.ProductionTime,
); SkillTypes.CRAFTING,
this.databaseServer.getTables().globals.config.SkillsSettings.Crafting.CraftTimeReductionPerLevel,
);
const modifiedScavCaseTime = this.getScavCaseTime(pmcData, adjustedCraftTime); const modifiedScavCaseTime = this.getScavCaseTime(pmcData, adjustedCraftTime);
@ -770,7 +777,7 @@ export class HideoutController
return output; return output;
} }
const recipe = hideoutDb.production.find(r => r._id === request.recipeId); const recipe = hideoutDb.production.find((r) => r._id === request.recipeId);
if (recipe) if (recipe)
{ {
this.handleRecipe(sessionID, recipe, pmcData, request, output); this.handleRecipe(sessionID, recipe, pmcData, request, output);
@ -778,7 +785,7 @@ export class HideoutController
return output; return output;
} }
const scavCase = hideoutDb.scavcase.find(r => r._id === request.recipeId); const scavCase = hideoutDb.scavcase.find((r) => r._id === request.recipeId);
if (scavCase) if (scavCase)
{ {
this.handleScavCase(sessionID, pmcData, request, output); this.handleScavCase(sessionID, pmcData, request, output);
@ -936,7 +943,7 @@ export class HideoutController
} }
// 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
const area = pmcData.Hideout.Areas.find(area => area.type === recipe.areaType); const area = pmcData.Hideout.Areas.find((area) => area.type === recipe.areaType);
if (area && request.recipeId !== area.lastRecipe) 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 // 1 point per craft upon the end of production for alternating between 2 different crafting recipes in the same module
@ -1042,7 +1049,10 @@ export class HideoutController
* @param recipe Recipe being crafted * @param recipe Recipe being crafted
* @returns ITaskConditionCounter * @returns ITaskConditionCounter
*/ */
protected getHoursCraftingTaskConditionCounter(pmcData: IPmcData, recipe: IHideoutProduction): ITaskConditionCounter protected getHoursCraftingTaskConditionCounter(
pmcData: IPmcData,
recipe: IHideoutProduction,
): ITaskConditionCounter
{ {
let counterHoursCrafting = pmcData.TaskConditionCounters[HideoutController.nameTaskConditionCountersCrafting]; let counterHoursCrafting = pmcData.TaskConditionCounters[HideoutController.nameTaskConditionCountersCrafting];
if (!counterHoursCrafting) if (!counterHoursCrafting)
@ -1078,7 +1088,8 @@ export class HideoutController
for (const production of ongoingProductions) for (const production of ongoingProductions)
{ {
if (this.hideoutHelper.isProductionType(production[1])) if (this.hideoutHelper.isProductionType(production[1]))
{ // Production or ScavCase {
// Production or ScavCase
if ((production[1] as ScavCase).RecipeId === request.recipeId) if ((production[1] as ScavCase).RecipeId === request.recipeId)
{ {
prodId = production[0]; // Set to objects key prodId = production[0]; // Set to objects key
@ -1187,13 +1198,13 @@ export class HideoutController
public recordShootingRangePoints(sessionId: string, pmcData: IPmcData, request: IRecordShootingRangePoints): void public recordShootingRangePoints(sessionId: string, pmcData: IPmcData, request: IRecordShootingRangePoints): void
{ {
// Check if counter exists, add placeholder if it doesnt // Check if counter exists, add placeholder if it doesnt
if (!pmcData.Stats.Eft.OverallCounters.Items.find(x => x.Key.includes("ShootingRangePoints"))) if (!pmcData.Stats.Eft.OverallCounters.Items.find((x) => x.Key.includes("ShootingRangePoints")))
{ {
pmcData.Stats.Eft.OverallCounters.Items.push({ Key: ["ShootingRangePoints"], Value: 0 }); pmcData.Stats.Eft.OverallCounters.Items.push({ Key: ["ShootingRangePoints"], Value: 0 });
} }
// Find counter by key and update value // Find counter by key and update value
const shootingRangeHighScore = pmcData.Stats.Eft.OverallCounters.Items.find(x => const shootingRangeHighScore = pmcData.Stats.Eft.OverallCounters.Items.find((x) =>
x.Key.includes("ShootingRangePoints"), x.Key.includes("ShootingRangePoints"),
); );
shootingRangeHighScore.Value = request.points; shootingRangeHighScore.Value = request.points;
@ -1216,7 +1227,7 @@ export class HideoutController
// Create mapping of required item with corrisponding item from player inventory // Create mapping of required item with corrisponding item from player inventory
const items = request.items.map((reqItem) => const items = request.items.map((reqItem) =>
{ {
const item = pmcData.Inventory.items.find(invItem => invItem._id === reqItem.id); const item = pmcData.Inventory.items.find((invItem) => invItem._id === reqItem.id);
return { inventoryItem: item, requestedItem: reqItem }; return { inventoryItem: item, requestedItem: reqItem };
}); });
@ -1246,14 +1257,14 @@ export class HideoutController
} }
} }
const profileHideoutArea = pmcData.Hideout.Areas.find(x => x.type === request.areaType); const profileHideoutArea = pmcData.Hideout.Areas.find((x) => x.type === request.areaType);
if (!profileHideoutArea) if (!profileHideoutArea)
{ {
this.logger.error(this.localisationService.getText("hideout-unable_to_find_area", request.areaType)); this.logger.error(this.localisationService.getText("hideout-unable_to_find_area", request.areaType));
return this.httpResponse.appendErrorToOutput(output); return this.httpResponse.appendErrorToOutput(output);
} }
const hideoutDbData = this.databaseServer.getTables().hideout.areas.find(x => x.type === request.areaType); const hideoutDbData = this.databaseServer.getTables().hideout.areas.find((x) => x.type === request.areaType);
if (!hideoutDbData) if (!hideoutDbData)
{ {
this.logger.error( this.logger.error(

View File

@ -201,7 +201,7 @@ export class InraidController
if (locationName === "lighthouse" && postRaidRequest.profile.Info.Side.toLowerCase() === "usec") if (locationName === "lighthouse" && postRaidRequest.profile.Info.Side.toLowerCase() === "usec")
{ {
// Decrement counter if it exists, don't go below 0 // Decrement counter if it exists, don't go below 0
const remainingCounter = serverPmcProfile?.Stats.Eft.OverallCounters.Items.find(x => const remainingCounter = serverPmcProfile?.Stats.Eft.OverallCounters.Items.find((x) =>
x.Key.includes("UsecRaidRemainKills"), x.Key.includes("UsecRaidRemainKills"),
); );
if (remainingCounter?.Value > 0) if (remainingCounter?.Value > 0)
@ -226,9 +226,9 @@ export class InraidController
// Not dead // Not dead
// Check for cultist amulets in special slot (only slot it can fit) // Check for cultist amulets in special slot (only slot it can fit)
const amuletOnPlayer = serverPmcProfile.Inventory.items.filter(item => const amuletOnPlayer = serverPmcProfile.Inventory.items
item.slotId?.startsWith("SpecialSlot"), .filter((item) => item.slotId?.startsWith("SpecialSlot"))
).find(item => item._tpl === "64d0b40fbe2eed70e254e2d4"); .find((item) => item._tpl === "64d0b40fbe2eed70e254e2d4");
if (amuletOnPlayer) if (amuletOnPlayer)
{ {
// No charges left, delete it // No charges left, delete it
@ -247,7 +247,7 @@ export class InraidController
} }
} }
const victims = postRaidRequest.profile.Stats.Eft.Victims.filter(x => const victims = postRaidRequest.profile.Stats.Eft.Victims.filter((x) =>
["sptbear", "sptusec"].includes(x.Role.toLowerCase()), ["sptbear", "sptusec"].includes(x.Role.toLowerCase()),
); );
if (victims?.length > 0) if (victims?.length > 0)
@ -281,12 +281,11 @@ export class InraidController
// and quest items cannot be picked up again // and quest items cannot be picked up again
const allQuests = this.questHelper.getQuestsFromDb(); const allQuests = this.questHelper.getQuestsFromDb();
const activeQuestIdsInProfile = pmcData.Quests.filter( const activeQuestIdsInProfile = pmcData.Quests.filter(
profileQuest => ![ (profileQuest) =>
QuestStatus.AvailableForStart, ![QuestStatus.AvailableForStart, QuestStatus.Success, QuestStatus.Expired].includes(
QuestStatus.Success, profileQuest.status,
QuestStatus.Expired, ),
].includes(profileQuest.status), ).map((x) => x.qid);
).map(x => x.qid);
for (const questItem of postRaidSaveRequest.profile.Stats.Eft.CarriedQuestItems) for (const questItem of postRaidSaveRequest.profile.Stats.Eft.CarriedQuestItems)
{ {
// Get quest/find condition for carried quest item // Get quest/find condition for carried quest item
@ -470,7 +469,7 @@ export class InraidController
for (const quest of scavProfile.Quests) for (const quest of scavProfile.Quests)
{ {
const pmcQuest = pmcProfile.Quests.find(x => x.qid === quest.qid); const pmcQuest = pmcProfile.Quests.find((x) => x.qid === quest.qid);
if (!pmcQuest) if (!pmcQuest)
{ {
this.logger.warning(`No PMC quest found for ID: ${quest.qid}`); this.logger.warning(`No PMC quest found for ID: ${quest.qid}`);
@ -505,7 +504,7 @@ export class InraidController
for (const scavCounter of Object.values(scavProfile.TaskConditionCounters)) for (const scavCounter of Object.values(scavProfile.TaskConditionCounters))
{ {
// If this is an achievement that isn't for the scav, don't process it // If this is an achievement that isn't for the scav, don't process it
const achievement = achievements.find(achievement => achievement.id === scavCounter.sourceId); const achievement = achievements.find((achievement) => achievement.id === scavCounter.sourceId);
if (achievement && achievement.side !== "Savage") if (achievement && achievement.side !== "Savage")
{ {
continue; continue;
@ -687,8 +686,8 @@ export class InraidController
// Remove any items that were returned by the item delivery, but also insured, from the player's insurance list // Remove any items that were returned by the item delivery, but also insured, from the player's insurance list
// This is to stop items being duplicated by being returned from both the item delivery, and insurance // This is to stop items being duplicated by being returned from both the item delivery, and insurance
const deliveredItemIds = items.map(x => x._id); const deliveredItemIds = items.map((x) => x._id);
pmcData.InsuredItems = pmcData.InsuredItems.filter(x => !deliveredItemIds.includes(x.itemId)); pmcData.InsuredItems = pmcData.InsuredItems.filter((x) => !deliveredItemIds.includes(x.itemId));
// Send the items to the player // Send the items to the player
this.mailSendService.sendLocalisedNpcMessageToPlayer( this.mailSendService.sendLocalisedNpcMessageToPlayer(

View File

@ -112,7 +112,7 @@ export class InsuranceController
this.logger.debug(`Found ${profileInsuranceDetails.length} insurance packages in profile ${sessionID}`); this.logger.debug(`Found ${profileInsuranceDetails.length} insurance packages in profile ${sessionID}`);
} }
return profileInsuranceDetails.filter(insured => insuranceTime >= insured.scheduledTime); return profileInsuranceDetails.filter((insured) => insuranceTime >= insured.scheduledTime);
} }
/** /**
@ -125,9 +125,9 @@ export class InsuranceController
protected processInsuredItems(insuranceDetails: Insurance[], sessionID: string): void protected processInsuredItems(insuranceDetails: Insurance[], sessionID: string): void
{ {
this.logger.debug( this.logger.debug(
`Processing ${insuranceDetails.length} insurance packages, which includes a total of ${ `Processing ${insuranceDetails.length} insurance packages, which includes a total of ${this.countAllInsuranceItems(
this.countAllInsuranceItems(insuranceDetails) insuranceDetails,
} items, in profile ${sessionID}`, )} items, in profile ${sessionID}`,
); );
// Fetch the root Item parentId property value that should be used for insurance packages. // Fetch the root Item parentId property value that should be used for insurance packages.
@ -160,7 +160,7 @@ export class InsuranceController
*/ */
protected countAllInsuranceItems(insurance: Insurance[]): number protected countAllInsuranceItems(insurance: Insurance[]): number
{ {
return this.mathUtil.arraySum(insurance.map(ins => ins.items.length)); return this.mathUtil.arraySum(insurance.map((ins) => ins.items.length));
} }
/** /**
@ -173,11 +173,12 @@ export class InsuranceController
protected removeInsurancePackageFromProfile(sessionID: string, insPackage: Insurance): void protected removeInsurancePackageFromProfile(sessionID: string, insPackage: Insurance): void
{ {
const profile = this.saveServer.getProfile(sessionID); const profile = this.saveServer.getProfile(sessionID);
profile.insurance = profile.insurance.filter(insurance => profile.insurance = profile.insurance.filter(
insurance.traderId !== insPackage.traderId (insurance) =>
|| insurance.systemData.date !== insPackage.systemData.date insurance.traderId !== insPackage.traderId
|| insurance.systemData.time !== insPackage.systemData.time || insurance.systemData.date !== insPackage.systemData.date
|| insurance.systemData.location !== insPackage.systemData.location, || insurance.systemData.time !== insPackage.systemData.time
|| insurance.systemData.location !== insPackage.systemData.location,
); );
this.logger.debug(`Removed processed insurance package. Remaining packages: ${profile.insurance.length}`); this.logger.debug(`Removed processed insurance package. Remaining packages: ${profile.insurance.length}`);
@ -200,8 +201,8 @@ export class InsuranceController
let parentAttachmentsMap = this.populateParentAttachmentsMap(rootItemParentID, insured, itemsMap); let parentAttachmentsMap = this.populateParentAttachmentsMap(rootItemParentID, insured, itemsMap);
// Check to see if any regular items are present. // Check to see if any regular items are present.
const hasRegularItems = Array.from(itemsMap.values()).some(item => const hasRegularItems = Array.from(itemsMap.values()).some(
!this.itemHelper.isAttachmentAttached(item), (item) => !this.itemHelper.isAttachmentAttached(item),
); );
// Process all items that are not attached, attachments; those are handled separately, by value. // Process all items that are not attached, attachments; those are handled separately, by value.
@ -249,7 +250,7 @@ export class InsuranceController
for (const insuredItem of insured.items) for (const insuredItem of insured.items)
{ {
// Use the parent ID from the item to get the parent item. // Use the parent ID from the item to get the parent item.
const parentItem = insured.items.find(item => item._id === insuredItem.parentId); const parentItem = insured.items.find((item) => item._id === insuredItem.parentId);
// The parent (not the hideout) could not be found. Skip and warn. // The parent (not the hideout) could not be found. Skip and warn.
if (!parentItem && insuredItem.parentId !== rootItemParentID) if (!parentItem && insuredItem.parentId !== rootItemParentID)
@ -489,7 +490,7 @@ export class InsuranceController
{ {
this.logger.debug( this.logger.debug(
`Attachment ${index} Id: ${attachmentId} Tpl: ${ `Attachment ${index} Id: ${attachmentId} Tpl: ${
attachments.find(x => x._id === attachmentId)?._tpl attachments.find((x) => x._id === attachmentId)?._tpl
} - Price: ${attachmentPrices[attachmentId]}`, } - Price: ${attachmentPrices[attachmentId]}`,
); );
index++; index++;
@ -556,7 +557,7 @@ export class InsuranceController
*/ */
protected removeItemsFromInsurance(insured: Insurance, toDelete: Set<string>): void protected removeItemsFromInsurance(insured: Insurance, toDelete: Set<string>): void
{ {
insured.items = insured.items.filter(item => !toDelete.has(item._id)); insured.items = insured.items.filter((item) => !toDelete.has(item._id));
} }
/** /**

View File

@ -106,7 +106,7 @@ export class InventoryController
} }
// Check for item in inventory before allowing internal transfer // Check for item in inventory before allowing internal transfer
const originalItemLocation = ownerInventoryItems.from.find(item => item._id === moveRequest.item); const originalItemLocation = ownerInventoryItems.from.find((item) => item._id === moveRequest.item);
if (!originalItemLocation) if (!originalItemLocation)
{ {
// Internal item move but item never existed, possible dupe glitch // Internal item move but item never existed, possible dupe glitch
@ -168,9 +168,10 @@ export class InventoryController
return; return;
} }
const profileToRemoveItemFrom = (!request.fromOwner || request.fromOwner.id === pmcData._id) const profileToRemoveItemFrom
? pmcData = !request.fromOwner || request.fromOwner.id === pmcData._id
: this.profileHelper.getFullProfile(sessionID).characters.scav; ? pmcData
: this.profileHelper.getFullProfile(sessionID).characters.scav;
this.inventoryHelper.removeItem(profileToRemoveItemFrom, request.item, sessionID, output); this.inventoryHelper.removeItem(profileToRemoveItemFrom, request.item, sessionID, output);
} }
@ -197,12 +198,12 @@ export class InventoryController
// Handle cartridge edge-case // Handle cartridge edge-case
if (!request.container.location && request.container.container === "cartridges") if (!request.container.location && request.container.container === "cartridges")
{ {
const matchingItems = inventoryItems.to.filter(x => x.parentId === request.container.id); const matchingItems = inventoryItems.to.filter((x) => x.parentId === request.container.id);
request.container.location = matchingItems.length; // Wrong location for first cartridge request.container.location = matchingItems.length; // Wrong location for first cartridge
} }
// The item being merged has three possible sources: pmc, scav or mail, getOwnerInventoryItems() handles getting correct one // The item being merged has three possible sources: pmc, scav or mail, getOwnerInventoryItems() handles getting correct one
const itemToSplit = inventoryItems.from.find(x => x._id === request.splitItem); const itemToSplit = inventoryItems.from.find((x) => x._id === request.splitItem);
if (!itemToSplit) if (!itemToSplit)
{ {
const errorMessage = `Unable to split stack as source item: ${request.splitItem} cannot be found`; const errorMessage = `Unable to split stack as source item: ${request.splitItem} cannot be found`;
@ -258,7 +259,7 @@ export class InventoryController
const inventoryItems = this.inventoryHelper.getOwnerInventoryItems(body, sessionID); const inventoryItems = this.inventoryHelper.getOwnerInventoryItems(body, sessionID);
// Get source item (can be from player or trader or mail) // Get source item (can be from player or trader or mail)
const sourceItem = inventoryItems.from.find(x => x._id === body.item); const sourceItem = inventoryItems.from.find((x) => x._id === body.item);
if (!sourceItem) if (!sourceItem)
{ {
const errorMessage = `Unable to merge stacks as source item: ${body.with} cannot be found`; const errorMessage = `Unable to merge stacks as source item: ${body.with} cannot be found`;
@ -270,7 +271,7 @@ export class InventoryController
} }
// Get item being merged into // Get item being merged into
const destinationItem = inventoryItems.to.find(x => x._id === body.with); const destinationItem = inventoryItems.to.find((x) => x._id === body.with);
if (!destinationItem) if (!destinationItem)
{ {
const errorMessage = `Unable to merge stacks as destination item: ${body.with} cannot be found`; const errorMessage = `Unable to merge stacks as destination item: ${body.with} cannot be found`;
@ -306,7 +307,7 @@ export class InventoryController
destinationItem.upd.StackObjectsCount += sourceItem.upd.StackObjectsCount; // Add source stackcount to destination destinationItem.upd.StackObjectsCount += sourceItem.upd.StackObjectsCount; // Add source stackcount to destination
output.profileChanges[sessionID].items.del.push({ _id: sourceItem._id }); // Inform client source item being deleted output.profileChanges[sessionID].items.del.push({ _id: sourceItem._id }); // Inform client source item being deleted
const indexOfItemToRemove = inventoryItems.from.findIndex(x => x._id === sourceItem._id); const indexOfItemToRemove = inventoryItems.from.findIndex((x) => x._id === sourceItem._id);
if (indexOfItemToRemove === -1) if (indexOfItemToRemove === -1)
{ {
const errorMessage = `Unable to find item: ${sourceItem._id} to remove from sender inventory`; const errorMessage = `Unable to find item: ${sourceItem._id} to remove from sender inventory`;
@ -339,8 +340,8 @@ export class InventoryController
): IItemEventRouterResponse ): IItemEventRouterResponse
{ {
const inventoryItems = this.inventoryHelper.getOwnerInventoryItems(body, sessionID); const inventoryItems = this.inventoryHelper.getOwnerInventoryItems(body, sessionID);
const sourceItem = inventoryItems.from.find(item => item._id === body.item); const sourceItem = inventoryItems.from.find((item) => item._id === body.item);
const destinationItem = inventoryItems.to.find(item => item._id === body.with); const destinationItem = inventoryItems.to.find((item) => item._id === body.with);
if (sourceItem === null) if (sourceItem === null)
{ {
@ -394,15 +395,19 @@ export class InventoryController
* its used for "reload" if you have weapon in hands and magazine is somewhere else in rig or backpack in equipment * its used for "reload" if you have weapon in hands and magazine is somewhere else in rig or backpack in equipment
* Also used to swap items using quick selection on character screen * Also used to swap items using quick selection on character screen
*/ */
public swapItem(pmcData: IPmcData, request: IInventorySwapRequestData, sessionID: string): IItemEventRouterResponse public swapItem(
pmcData: IPmcData,
request: IInventorySwapRequestData,
sessionID: string,
): IItemEventRouterResponse
{ {
const itemOne = pmcData.Inventory.items.find(x => x._id === request.item); const itemOne = pmcData.Inventory.items.find((x) => x._id === request.item);
if (!itemOne) if (!itemOne)
{ {
this.logger.error(`Unable to find item: ${request.item} to swap positions with: ${request.item2}`); this.logger.error(`Unable to find item: ${request.item} to swap positions with: ${request.item2}`);
} }
const itemTwo = pmcData.Inventory.items.find(x => x._id === request.item2); const itemTwo = pmcData.Inventory.items.find((x) => x._id === request.item2);
if (!itemTwo) if (!itemTwo)
{ {
this.logger.error(`Unable to find item: ${request.item2} to swap positions with: ${request.item}`); this.logger.error(`Unable to find item: ${request.item2} to swap positions with: ${request.item}`);
@ -442,7 +447,11 @@ export class InventoryController
/** /**
* Handles folding of Weapons * Handles folding of Weapons
*/ */
public foldItem(pmcData: IPmcData, request: IInventoryFoldRequestData, sessionID: string): IItemEventRouterResponse public foldItem(
pmcData: IPmcData,
request: IInventoryFoldRequestData,
sessionID: string,
): IItemEventRouterResponse
{ {
// May need to reassign to scav profile // May need to reassign to scav profile
let playerData = pmcData; let playerData = pmcData;
@ -453,7 +462,7 @@ export class InventoryController
playerData = this.profileHelper.getScavProfile(sessionID); playerData = this.profileHelper.getScavProfile(sessionID);
} }
const itemToFold = playerData.Inventory.items.find(item => item?._id === request.item); const itemToFold = playerData.Inventory.items.find((item) => item?._id === request.item);
if (!itemToFold) if (!itemToFold)
{ {
// Item not found // Item not found
@ -476,7 +485,11 @@ export class InventoryController
* @param sessionID Session id * @param sessionID Session id
* @returns IItemEventRouterResponse * @returns IItemEventRouterResponse
*/ */
public toggleItem(pmcData: IPmcData, body: IInventoryToggleRequestData, sessionID: string): IItemEventRouterResponse public toggleItem(
pmcData: IPmcData,
body: IInventoryToggleRequestData,
sessionID: string,
): IItemEventRouterResponse
{ {
// May need to reassign to scav profile // May need to reassign to scav profile
let playerData = pmcData; let playerData = pmcData;
@ -487,7 +500,7 @@ export class InventoryController
playerData = this.profileHelper.getScavProfile(sessionID); playerData = this.profileHelper.getScavProfile(sessionID);
} }
const itemToToggle = playerData.Inventory.items.find(x => x._id === body.item); const itemToToggle = playerData.Inventory.items.find((x) => x._id === body.item);
if (itemToToggle) if (itemToToggle)
{ {
this.itemHelper.addUpdObjectToItem( this.itemHelper.addUpdObjectToItem(
@ -688,16 +701,16 @@ export class InventoryController
if (request.fromOwner.id === Traders.FENCE) if (request.fromOwner.id === Traders.FENCE)
{ {
// Get tpl from fence assorts // Get tpl from fence assorts
return this.fenceService.getRawFenceAssorts().items.find(x => x._id === request.item)._tpl; return this.fenceService.getRawFenceAssorts().items.find((x) => x._id === request.item)._tpl;
} }
if (request.fromOwner.type === "Trader") if (request.fromOwner.type === "Trader")
{ {
// Not fence // Not fence
// get tpl from trader assort // get tpl from trader assort
return this.databaseServer.getTables().traders[request.fromOwner.id].assort.items.find(item => return this.databaseServer
item._id === request.item, .getTables()
)._tpl; .traders[request.fromOwner.id].assort.items.find((item) => item._id === request.item)._tpl;
} }
if (request.fromOwner.type === "RagFair") if (request.fromOwner.type === "RagFair")
@ -717,7 +730,7 @@ export class InventoryController
} }
// Try find examine item inside offer items array // Try find examine item inside offer items array
const matchingItem = offer.items.find(offerItem => offerItem._id === request.item); const matchingItem = offer.items.find((offerItem) => offerItem._id === request.item);
if (matchingItem) if (matchingItem)
{ {
return matchingItem._tpl; return matchingItem._tpl;
@ -753,7 +766,7 @@ export class InventoryController
{ {
for (const change of request.changedItems) for (const change of request.changedItems)
{ {
const inventoryItem = pmcData.Inventory.items.find(x => x._id === change._id); const inventoryItem = pmcData.Inventory.items.find((x) => x._id === change._id);
if (!inventoryItem) if (!inventoryItem)
{ {
this.logger.error( this.logger.error(
@ -792,7 +805,7 @@ export class InventoryController
): void ): void
{ {
// Get map from inventory // Get map from inventory
const mapItem = pmcData.Inventory.items.find(i => i._id === request.item); const mapItem = pmcData.Inventory.items.find((i) => i._id === request.item);
// add marker // add marker
mapItem.upd.Map = mapItem.upd.Map || { Markers: [] }; mapItem.upd.Map = mapItem.upd.Map || { Markers: [] };
@ -818,7 +831,7 @@ export class InventoryController
): void ): void
{ {
// Get map from inventory // Get map from inventory
const mapItem = pmcData.Inventory.items.find(i => i._id === request.item); const mapItem = pmcData.Inventory.items.find((i) => i._id === request.item);
// remove marker // remove marker
const markers = mapItem.upd.Map.Markers.filter((marker) => const markers = mapItem.upd.Map.Markers.filter((marker) =>
@ -846,10 +859,10 @@ export class InventoryController
): void ): void
{ {
// Get map from inventory // Get map from inventory
const mapItem = pmcData.Inventory.items.find(i => i._id === request.item); const mapItem = pmcData.Inventory.items.find((i) => i._id === request.item);
// edit marker // edit marker
const indexOfExistingNote = mapItem.upd.Map.Markers.findIndex(m => m.X === request.X && m.Y === request.Y); const indexOfExistingNote = mapItem.upd.Map.Markers.findIndex((m) => m.X === request.X && m.Y === request.Y);
request.mapMarker.Note = this.sanitiseMapMarkerText(request.mapMarker.Note); request.mapMarker.Note = this.sanitiseMapMarkerText(request.mapMarker.Note);
mapItem.upd.Map.Markers[indexOfExistingNote] = request.mapMarker; mapItem.upd.Map.Markers[indexOfExistingNote] = request.mapMarker;
@ -883,7 +896,7 @@ export class InventoryController
): void ): void
{ {
/** Container player opened in their inventory */ /** Container player opened in their inventory */
const openedItem = pmcData.Inventory.items.find(item => item._id === body.item); const openedItem = pmcData.Inventory.items.find((item) => item._id === body.item);
const containerDetailsDb = this.itemHelper.getItem(openedItem._tpl); const containerDetailsDb = this.itemHelper.getItem(openedItem._tpl);
const isSealedWeaponBox = containerDetailsDb[1]._name.includes("event_container_airdrop"); const isSealedWeaponBox = containerDetailsDb[1]._name.includes("event_container_airdrop");
@ -934,8 +947,8 @@ export class InventoryController
// Hard coded to `SYSTEM` for now // Hard coded to `SYSTEM` for now
// TODO: make this dynamic // TODO: make this dynamic
const dialog = fullProfile.dialogues["59e7125688a45068a6249071"]; const dialog = fullProfile.dialogues["59e7125688a45068a6249071"];
const mail = dialog.messages.find(x => x._id === event.MessageId); const mail = dialog.messages.find((x) => x._id === event.MessageId);
const mailEvent = mail.profileChangeEvents.find(x => x._id === event.EventId); const mailEvent = mail.profileChangeEvents.find((x) => x._id === event.EventId);
switch (mailEvent.Type) switch (mailEvent.Type)
{ {
@ -954,15 +967,18 @@ export class InventoryController
break; break;
case "SkillPoints": case "SkillPoints":
{ {
const profileSkill = pmcData.Skills.Common.find(x => x.Id === mailEvent.entity); const profileSkill = pmcData.Skills.Common.find((x) => x.Id === mailEvent.entity);
profileSkill.Progress = mailEvent.value; profileSkill.Progress = mailEvent.value;
this.logger.success(`Set profile skill: ${mailEvent.entity} to: ${mailEvent.value}`); this.logger.success(`Set profile skill: ${mailEvent.entity} to: ${mailEvent.value}`);
break; break;
} }
case "ExamineAllItems": case "ExamineAllItems":
{ {
const itemsToInspect = this.itemHelper.getItems().filter(x => x._type !== "Node"); const itemsToInspect = this.itemHelper.getItems().filter((x) => x._type !== "Node");
this.flagItemsAsInspectedAndRewardXp(itemsToInspect.map(x => x._id), fullProfile); this.flagItemsAsInspectedAndRewardXp(
itemsToInspect.map((x) => x._id),
fullProfile,
);
this.logger.success(`Flagged ${itemsToInspect.length} items as examined`); this.logger.success(`Flagged ${itemsToInspect.length} items as examined`);
break; break;
} }
@ -987,7 +1003,7 @@ export class InventoryController
for (const itemId of request.items) for (const itemId of request.items)
{ {
// If id already exists in array, we're removing it // If id already exists in array, we're removing it
const indexOfItemAlreadyFavorited = pmcData.Inventory.favoriteItems.findIndex(x => x === itemId); const indexOfItemAlreadyFavorited = pmcData.Inventory.favoriteItems.findIndex((x) => x === itemId);
if (indexOfItemAlreadyFavorited > -1) if (indexOfItemAlreadyFavorited > -1)
{ {
pmcData.Inventory.favoriteItems.splice(indexOfItemAlreadyFavorited, 1); pmcData.Inventory.favoriteItems.splice(indexOfItemAlreadyFavorited, 1);

View File

@ -92,9 +92,9 @@ export class LocationController
// Check for a loot multipler adjustment in app context and apply if one is found // Check for a loot multipler adjustment in app context and apply if one is found
let locationConfigCopy: ILocationConfig; let locationConfigCopy: ILocationConfig;
const raidAdjustments = this.applicationContext.getLatestValue(ContextVariableType.RAID_ADJUSTMENTS)?.getValue< const raidAdjustments = this.applicationContext
IRaidChanges .getLatestValue(ContextVariableType.RAID_ADJUSTMENTS)
>(); ?.getValue<IRaidChanges>();
if (raidAdjustments) if (raidAdjustments)
{ {
locationConfigCopy = this.cloner.clone(this.locationConfig); // Clone values so they can be used to reset originals later locationConfigCopy = this.cloner.clone(this.locationConfig); // Clone values so they can be used to reset originals later

View File

@ -24,9 +24,7 @@ export class PresetController
if (id !== preset._id) if (id !== preset._id)
{ {
this.logger.error( this.logger.error(
`Preset for template tpl: '${ `Preset for template tpl: '${preset._items[0]._tpl} ${preset._name}' has invalid key: (${id} != ${preset._id}). Skipping`,
preset._items[0]._tpl
} ${preset._name}' has invalid key: (${id} != ${preset._id}). Skipping`,
); );
continue; continue;

View File

@ -129,10 +129,8 @@ export class ProfileController
public createProfile(info: IProfileCreateRequestData, sessionID: string): string public createProfile(info: IProfileCreateRequestData, sessionID: string): string
{ {
const account = this.saveServer.getProfile(sessionID).info; const account = this.saveServer.getProfile(sessionID).info;
const profile: ITemplateSide = this.databaseServer const profile: ITemplateSide
.getTables() = this.databaseServer.getTables().templates.profiles[account.edition][info.side.toLowerCase()];
.templates
.profiles[account.edition][info.side.toLowerCase()];
const pmcData = profile.character; const pmcData = profile.character;
// Delete existing profile // Delete existing profile
@ -152,7 +150,7 @@ export class ProfileController
pmcData.Customization.Head = info.headId; pmcData.Customization.Head = info.headId;
pmcData.Health.UpdateTime = this.timeUtil.getTimestamp(); pmcData.Health.UpdateTime = this.timeUtil.getTimestamp();
pmcData.Quests = []; pmcData.Quests = [];
pmcData.Hideout.Seed = this.timeUtil.getTimestamp() + (8 * 60 * 60 * 24 * 365); // 8 years in future why? who knows, we saw it in live pmcData.Hideout.Seed = this.timeUtil.getTimestamp() + 8 * 60 * 60 * 24 * 365; // 8 years in future why? who knows, we saw it in live
pmcData.RepeatableQuests = []; pmcData.RepeatableQuests = [];
pmcData.CarExtractCounts = {}; pmcData.CarExtractCounts = {};
pmcData.CoopExtractCounts = {}; pmcData.CoopExtractCounts = {};
@ -398,16 +396,18 @@ export class ProfileController
const profile = this.saveServer.getProfile(sessionID); const profile = this.saveServer.getProfile(sessionID);
// return some of the current player info for now // return some of the current player info for now
return [{ return [
_id: profile.characters.pmc._id, {
aid: profile.characters.pmc.aid, _id: profile.characters.pmc._id,
Info: { aid: profile.characters.pmc.aid,
Nickname: info.nickname, Info: {
Side: "Bear", Nickname: info.nickname,
Level: 1, Side: "Bear",
MemberCategory: profile.characters.pmc.Info.MemberCategory, Level: 1,
MemberCategory: profile.characters.pmc.Info.MemberCategory,
},
}, },
}]; ];
} }
/** /**
@ -418,14 +418,17 @@ export class ProfileController
const account = this.saveServer.getProfile(sessionId).info; const account = this.saveServer.getProfile(sessionId).info;
const response: GetProfileStatusResponseData = { const response: GetProfileStatusResponseData = {
maxPveCountExceeded: false, maxPveCountExceeded: false,
profiles: [{ profileid: account.scavId, profileToken: null, status: "Free", sid: "", ip: "", port: 0 }, { profiles: [
profileid: account.id, { profileid: account.scavId, profileToken: null, status: "Free", sid: "", ip: "", port: 0 },
profileToken: null, {
status: "Free", profileid: account.id,
sid: "", profileToken: null,
ip: "", status: "Free",
port: 0, sid: "",
}], ip: "",
port: 0,
},
],
}; };
return response; return response;
@ -459,7 +462,7 @@ export class ProfileController
skills: playerPmc.Skills, skills: playerPmc.Skills,
equipment: { equipment: {
// Default inventory tpl // Default inventory tpl
Id: playerPmc.Inventory.items.find(x => x._tpl === "55d7217a4bdc2d86028b456d")._id, Id: playerPmc.Inventory.items.find((x) => x._tpl === "55d7217a4bdc2d86028b456d")._id,
Items: playerPmc.Inventory.items, Items: playerPmc.Inventory.items,
}, },
achievements: playerPmc.Achievements, achievements: playerPmc.Achievements,

View File

@ -78,7 +78,7 @@ export class QuestController
for (const quest of allQuests) for (const quest of allQuests)
{ {
// Player already accepted the quest, show it regardless of status // Player already accepted the quest, show it regardless of status
const questInProfile = profile.Quests.find(x => x.qid === quest._id); const questInProfile = profile.Quests.find((x) => x.qid === quest._id);
if (questInProfile) if (questInProfile)
{ {
quest.sptStatus = questInProfile.status; quest.sptStatus = questInProfile.status;
@ -123,7 +123,11 @@ export class QuestController
); );
// Quest has no conditions, standing or loyalty conditions, add to visible quest list // Quest has no conditions, standing or loyalty conditions, add to visible quest list
if (questRequirements.length === 0 && loyaltyRequirements.length === 0 && standingRequirements.length === 0) if (
questRequirements.length === 0
&& loyaltyRequirements.length === 0
&& standingRequirements.length === 0
)
{ {
quest.sptStatus = QuestStatus.AvailableForStart; quest.sptStatus = QuestStatus.AvailableForStart;
questsToShowPlayer.push(quest); questsToShowPlayer.push(quest);
@ -136,7 +140,7 @@ export class QuestController
for (const conditionToFulfil of questRequirements) for (const conditionToFulfil of questRequirements)
{ {
// If the previous quest isn't in the user profile, it hasn't been completed or started // If the previous quest isn't in the user profile, it hasn't been completed or started
const prerequisiteQuest = profile.Quests.find(profileQuest => const prerequisiteQuest = profile.Quests.find((profileQuest) =>
conditionToFulfil.target.includes(profileQuest.qid), conditionToFulfil.target.includes(profileQuest.qid),
); );
if (!prerequisiteQuest) if (!prerequisiteQuest)
@ -147,7 +151,7 @@ export class QuestController
// Prereq does not have its status requirement fulfilled // Prereq does not have its status requirement fulfilled
// Some bsg status ids are strings, MUST convert to number before doing includes check // Some bsg status ids are strings, MUST convert to number before doing includes check
if (!conditionToFulfil.status.map(status => Number(status)).includes(prerequisiteQuest.status)) if (!conditionToFulfil.status.map((status) => Number(status)).includes(prerequisiteQuest.status))
{ {
haveCompletedPreviousQuest = false; haveCompletedPreviousQuest = false;
break; break;
@ -291,7 +295,7 @@ export class QuestController
// Does quest exist in profile // Does quest exist in profile
// Restarting a failed quest can mean quest exists in profile // Restarting a failed quest can mean quest exists in profile
const existingQuestStatus = pmcData.Quests.find(x => x.qid === acceptedQuest.qid); const existingQuestStatus = pmcData.Quests.find((x) => x.qid === acceptedQuest.qid);
if (existingQuestStatus) if (existingQuestStatus)
{ {
// Update existing // Update existing
@ -398,16 +402,17 @@ export class QuestController
fullProfile.characters.scav.Quests.push(newRepeatableQuest); fullProfile.characters.scav.Quests.push(newRepeatableQuest);
} }
const repeatableSettings = pmcData.RepeatableQuests.find(x => const repeatableSettings = pmcData.RepeatableQuests.find(
x.name === repeatableQuestProfile.sptRepatableGroupName, (x) => x.name === repeatableQuestProfile.sptRepatableGroupName,
); );
const change = {}; const change = {};
change[repeatableQuestProfile._id] = repeatableSettings.changeRequirement[repeatableQuestProfile._id]; change[repeatableQuestProfile._id] = repeatableSettings.changeRequirement[repeatableQuestProfile._id];
const responseData: IPmcDataRepeatableQuest = { const responseData: IPmcDataRepeatableQuest = {
id: repeatableSettings.id ?? this.questConfig.repeatableQuests.find(x => id:
x.name === repeatableQuestProfile.sptRepatableGroupName, repeatableSettings.id
).id, ?? this.questConfig.repeatableQuests.find((x) => x.name === repeatableQuestProfile.sptRepatableGroupName)
.id,
name: repeatableSettings.name, name: repeatableSettings.name,
endTime: repeatableSettings.endTime, endTime: repeatableSettings.endTime,
changeRequirement: change, changeRequirement: change,
@ -430,11 +435,14 @@ export class QuestController
* @param acceptedQuest Quest to search for * @param acceptedQuest Quest to search for
* @returns IRepeatableQuest * @returns IRepeatableQuest
*/ */
protected getRepeatableQuestFromProfile(pmcData: IPmcData, acceptedQuest: IAcceptQuestRequestData): IRepeatableQuest protected getRepeatableQuestFromProfile(
pmcData: IPmcData,
acceptedQuest: IAcceptQuestRequestData,
): IRepeatableQuest
{ {
for (const repeatableQuest of pmcData.RepeatableQuests) for (const repeatableQuest of pmcData.RepeatableQuests)
{ {
const matchingQuest = repeatableQuest.activeQuests.find(x => x._id === acceptedQuest.qid); const matchingQuest = repeatableQuest.activeQuests.find((x) => x._id === acceptedQuest.qid);
if (matchingQuest) if (matchingQuest)
{ {
this.logger.debug(`Accepted repeatable quest ${acceptedQuest.qid} from ${repeatableQuest.name}`); this.logger.debug(`Accepted repeatable quest ${acceptedQuest.qid} from ${repeatableQuest.name}`);
@ -503,8 +511,8 @@ export class QuestController
// Check if it's a repeatable quest. If so, remove from Quests // Check if it's a repeatable quest. If so, remove from Quests
for (const currentRepeatable of pmcData.RepeatableQuests) for (const currentRepeatable of pmcData.RepeatableQuests)
{ {
const repeatableQuest = currentRepeatable.activeQuests.find(activeRepeatable => const repeatableQuest = currentRepeatable.activeQuests.find(
activeRepeatable._id === completedQuestId, (activeRepeatable) => activeRepeatable._id === completedQuestId,
); );
if (repeatableQuest) if (repeatableQuest)
{ {
@ -547,15 +555,15 @@ export class QuestController
// Quest already failed in profile, skip // Quest already failed in profile, skip
if ( if (
pmcProfile.Quests.some(profileQuest => pmcProfile.Quests.some(
profileQuest.qid === quest._id && profileQuest.status === QuestStatus.Fail, (profileQuest) => profileQuest.qid === quest._id && profileQuest.status === QuestStatus.Fail,
) )
) )
{ {
return false; return false;
} }
return quest.conditions.Fail.some(condition => condition.target?.includes(completedQuestId)); return quest.conditions.Fail.some((condition) => condition.target?.includes(completedQuestId));
}); });
} }
@ -567,7 +575,7 @@ export class QuestController
protected removeQuestFromScavProfile(sessionId: string, questIdToRemove: string): void protected removeQuestFromScavProfile(sessionId: string, questIdToRemove: string): void
{ {
const fullProfile = this.profileHelper.getFullProfile(sessionId); const fullProfile = this.profileHelper.getFullProfile(sessionId);
const repeatableInScavProfile = fullProfile.characters.scav.Quests?.find(x => x.qid === questIdToRemove); const repeatableInScavProfile = fullProfile.characters.scav.Quests?.find((x) => x.qid === questIdToRemove);
if (!repeatableInScavProfile) if (!repeatableInScavProfile)
{ {
this.logger.warning( this.logger.warning(
@ -599,7 +607,7 @@ export class QuestController
for (const quest of postQuestStatuses) for (const quest of postQuestStatuses)
{ {
// Add quest if status differs or quest not found // Add quest if status differs or quest not found
const preQuest = preQuestStatusus.find(x => x.qid === quest.qid); const preQuest = preQuestStatusus.find((x) => x.qid === quest.qid);
if (!preQuest || preQuest.status !== quest.status) if (!preQuest || preQuest.status !== quest.status)
{ {
result.push(quest); result.push(quest);
@ -652,8 +660,8 @@ export class QuestController
for (const quest of quests) for (const quest of quests)
{ {
// If quest has prereq of completed quest + availableAfter value > 0 (quest has wait time) // If quest has prereq of completed quest + availableAfter value > 0 (quest has wait time)
const nextQuestWaitCondition = quest.conditions.AvailableForStart.find(x => const nextQuestWaitCondition = quest.conditions.AvailableForStart.find(
x.target?.includes(completedQuestId) && x.availableAfter > 0, (x) => x.target?.includes(completedQuestId) && x.availableAfter > 0,
); );
if (nextQuestWaitCondition) if (nextQuestWaitCondition)
{ {
@ -661,7 +669,7 @@ export class QuestController
const availableAfterTimestamp = this.timeUtil.getTimestamp() + nextQuestWaitCondition.availableAfter; const availableAfterTimestamp = this.timeUtil.getTimestamp() + nextQuestWaitCondition.availableAfter;
// Update quest in profile with status of AvailableAfter // Update quest in profile with status of AvailableAfter
const existingQuestInProfile = pmcData.Quests.find(x => x.qid === quest._id); const existingQuestInProfile = pmcData.Quests.find((x) => x.qid === quest._id);
if (existingQuestInProfile) if (existingQuestInProfile)
{ {
existingQuestInProfile.availableAfter = availableAfterTimestamp; existingQuestInProfile.availableAfter = availableAfterTimestamp;
@ -704,12 +712,12 @@ export class QuestController
for (const questToFail of questsToFail) for (const questToFail of questsToFail)
{ {
// Skip failing a quest that has a fail status of something other than success // Skip failing a quest that has a fail status of something other than success
if (questToFail.conditions.Fail?.some(x => x.status?.some(status => status !== QuestStatus.Success))) if (questToFail.conditions.Fail?.some((x) => x.status?.some((status) => status !== QuestStatus.Success)))
{ {
continue; continue;
} }
const isActiveQuestInPlayerProfile = pmcData.Quests.find(quest => quest.qid === questToFail._id); const isActiveQuestInPlayerProfile = pmcData.Quests.find((quest) => quest.qid === questToFail._id);
if (isActiveQuestInPlayerProfile) if (isActiveQuestInPlayerProfile)
{ {
if (isActiveQuestInPlayerProfile.status !== QuestStatus.Fail) if (isActiveQuestInPlayerProfile.status !== QuestStatus.Fail)
@ -771,9 +779,10 @@ export class QuestController
isItemHandoverQuest = condition.conditionType === handoverQuestTypes[0]; isItemHandoverQuest = condition.conditionType === handoverQuestTypes[0];
handoverRequirements = condition; handoverRequirements = condition;
const profileCounter = handoverQuestRequest.conditionId in pmcData.TaskConditionCounters const profileCounter
? pmcData.TaskConditionCounters[handoverQuestRequest.conditionId].value = handoverQuestRequest.conditionId in pmcData.TaskConditionCounters
: 0; ? pmcData.TaskConditionCounters[handoverQuestRequest.conditionId].value
: 0;
handedInCount -= profileCounter; handedInCount -= profileCounter;
if (handedInCount <= 0) if (handedInCount <= 0)
@ -805,7 +814,7 @@ export class QuestController
let totalItemCountToRemove = 0; let totalItemCountToRemove = 0;
for (const itemHandover of handoverQuestRequest.items) for (const itemHandover of handoverQuestRequest.items)
{ {
const matchingItemInProfile = pmcData.Inventory.items.find(item => item._id === itemHandover.id); const matchingItemInProfile = pmcData.Inventory.items.find((item) => item._id === itemHandover.id);
if (!(matchingItemInProfile && handoverRequirements.target.includes(matchingItemInProfile._tpl))) if (!(matchingItemInProfile && handoverRequirements.target.includes(matchingItemInProfile._tpl)))
{ {
// Item handed in by player doesnt match what was requested // Item handed in by player doesnt match what was requested

View File

@ -27,7 +27,6 @@ import { ISearchRequestData } from "@spt-aki/models/eft/ragfair/ISearchRequestDa
import { IProcessBuyTradeRequestData } from "@spt-aki/models/eft/trade/IProcessBuyTradeRequestData"; import { IProcessBuyTradeRequestData } from "@spt-aki/models/eft/trade/IProcessBuyTradeRequestData";
import { ConfigTypes } from "@spt-aki/models/enums/ConfigTypes"; import { ConfigTypes } from "@spt-aki/models/enums/ConfigTypes";
import { MemberCategory } from "@spt-aki/models/enums/MemberCategory"; import { MemberCategory } from "@spt-aki/models/enums/MemberCategory";
import { RagfairSort } from "@spt-aki/models/enums/RagfairSort";
import { IRagfairConfig } from "@spt-aki/models/spt/config/IRagfairConfig"; import { IRagfairConfig } from "@spt-aki/models/spt/config/IRagfairConfig";
import { ILogger } from "@spt-aki/models/spt/utils/ILogger"; import { ILogger } from "@spt-aki/models/spt/utils/ILogger";
import { EventOutputHolder } from "@spt-aki/routers/EventOutputHolder"; import { EventOutputHolder } from "@spt-aki/routers/EventOutputHolder";
@ -159,7 +158,7 @@ export class RagfairController
public getOfferById(sessionId: string, request: IGetRagfairOfferByIdRequest): IRagfairOffer public getOfferById(sessionId: string, request: IGetRagfairOfferByIdRequest): IRagfairOffer
{ {
const offers = this.ragfairOfferService.getOffers(); const offers = this.ragfairOfferService.getOffers();
const offerToReturn = offers.find(x => x.intId === request.id); const offerToReturn = offers.find((x) => x.intId === request.id);
return offerToReturn; return offerToReturn;
} }
@ -207,12 +206,8 @@ export class RagfairController
): Record<string, number> ): Record<string, number>
{ {
// Linked/required search categories // Linked/required search categories
const playerHasFleaUnlocked = pmcProfile.Info.Level >= this.databaseServer const playerHasFleaUnlocked
.getTables() = pmcProfile.Info.Level >= this.databaseServer.getTables().globals.config.RagFair.minUserLevel;
.globals
.config
.RagFair
.minUserLevel;
let offerPool = []; let offerPool = [];
if (this.isLinkedSearch(searchRequest) || this.isRequiredSearch(searchRequest)) if (this.isLinkedSearch(searchRequest) || this.isRequiredSearch(searchRequest))
{ {
@ -263,7 +258,7 @@ export class RagfairController
const traderAssorts = this.traderHelper.getTraderAssortsByTraderId(offer.user.id).items; const traderAssorts = this.traderHelper.getTraderAssortsByTraderId(offer.user.id).items;
const assortId = offer.items[0]._id; const assortId = offer.items[0]._id;
const assortData = traderAssorts.find(x => x._id === assortId); const assortData = traderAssorts.find((x) => x._id === assortId);
// Use value stored in profile, otherwise use value directly from in-memory trader assort data // Use value stored in profile, otherwise use value directly from in-memory trader assort data
offer.buyRestrictionCurrent = fullProfile.traderPurchases[offer.user.id][assortId] offer.buyRestrictionCurrent = fullProfile.traderPurchases[offer.user.id][assortId]
@ -282,7 +277,7 @@ export class RagfairController
const firstItem = offer.items[0]; const firstItem = offer.items[0];
const traderAssorts = this.traderHelper.getTraderAssortsByTraderId(offer.user.id).items; const traderAssorts = this.traderHelper.getTraderAssortsByTraderId(offer.user.id).items;
const assortPurchased = traderAssorts.find(x => x._id === offer.items[0]._id); const assortPurchased = traderAssorts.find((x) => x._id === offer.items[0]._id);
if (!assortPurchased) if (!assortPurchased)
{ {
this.logger.warning( this.logger.warning(
@ -357,31 +352,34 @@ export class RagfairController
// Get the average offer price, excluding barter offers // Get the average offer price, excluding barter offers
let avgOfferCount = 0; let avgOfferCount = 0;
const avg = offers.reduce((sum, offer) => const avg
{ = offers.reduce((sum, offer) =>
// Exclude barter items, they tend to have outrageous equivalent prices
if (offer.requirements.some(req => !this.paymentHelper.isMoneyTpl(req._tpl)))
{ {
return sum; // Exclude barter items, they tend to have outrageous equivalent prices
} if (offer.requirements.some((req) => !this.paymentHelper.isMoneyTpl(req._tpl)))
{
return sum;
}
// Figure out how many items the requirementsCost is applying to, and what the per-item price is // Figure out how many items the requirementsCost is applying to, and what the per-item price is
const offerItemCount = Math.max(offer.sellInOnePiece ? offer.items[0].upd?.StackObjectsCount ?? 1 : 1); const offerItemCount = Math.max(
const perItemPrice = offer.requirementsCost / offerItemCount; offer.sellInOnePiece ? offer.items[0].upd?.StackObjectsCount ?? 1 : 1,
);
const perItemPrice = offer.requirementsCost / offerItemCount;
// Handle min/max calculations based on the per-item price // Handle min/max calculations based on the per-item price
if (perItemPrice < min) if (perItemPrice < min)
{ {
min = perItemPrice; min = perItemPrice;
} }
else if (perItemPrice > max) else if (perItemPrice > max)
{ {
max = perItemPrice; max = perItemPrice;
} }
avgOfferCount++; avgOfferCount++;
return sum + perItemPrice; return sum + perItemPrice;
}, 0) / Math.max(avgOfferCount, 1); }, 0) / Math.max(avgOfferCount, 1);
// If no items were actually counted, min will still be MAX_VALUE, so set it to 0 // If no items were actually counted, min will still be MAX_VALUE, so set it to 0
if (min === Number.MAX_VALUE) if (min === Number.MAX_VALUE)
@ -428,8 +426,8 @@ export class RagfairController
} }
// Get an array of items from player inventory to list on flea // Get an array of items from player inventory to list on flea
const { items: itemsInInventoryToList, errorMessage: itemsInInventoryError } = this const { items: itemsInInventoryToList, errorMessage: itemsInInventoryError }
.getItemsToListOnFleaFromInventory(pmcData, offerRequest.items); = this.getItemsToListOnFleaFromInventory(pmcData, offerRequest.items);
if (!itemsInInventoryToList || itemsInInventoryError) if (!itemsInInventoryToList || itemsInInventoryError)
{ {
this.httpResponse.appendErrorToOutput(output, itemsInInventoryError); this.httpResponse.appendErrorToOutput(output, itemsInInventoryError);
@ -605,8 +603,8 @@ export class RagfairController
} }
else else
{ {
requirementsPriceInRub += this.ragfairPriceService requirementsPriceInRub
.getDynamicPriceForItem(requestedItemTpl) * item.count; += this.ragfairPriceService.getDynamicPriceForItem(requestedItemTpl) * item.count;
} }
} }
@ -630,7 +628,7 @@ export class RagfairController
// Count how many items are being sold and multiply the requested amount accordingly // Count how many items are being sold and multiply the requested amount accordingly
for (const itemId of itemIdsFromFleaOfferRequest) for (const itemId of itemIdsFromFleaOfferRequest)
{ {
let item = pmcData.Inventory.items.find(i => i._id === itemId); let item = pmcData.Inventory.items.find((i) => i._id === itemId);
if (!item) if (!item)
{ {
errorMessage = this.localisationService.getText("ragfair-unable_to_find_item_in_inventory", { errorMessage = this.localisationService.getText("ragfair-unable_to_find_item_in_inventory", {
@ -666,7 +664,7 @@ export class RagfairController
const loyalLevel = 1; const loyalLevel = 1;
const formattedItems: Item[] = items.map((item) => const formattedItems: Item[] = items.map((item) =>
{ {
const isChild = items.find(it => it._id === item.parentId); const isChild = items.find((it) => it._id === item.parentId);
return { return {
_id: item._id, _id: item._id,
@ -727,7 +725,7 @@ export class RagfairController
pmcData.RagfairInfo.offers = []; pmcData.RagfairInfo.offers = [];
} }
const playerOfferIndex = playerProfileOffers.findIndex(offer => offer._id === removeRequest.offerId); const playerOfferIndex = playerProfileOffers.findIndex((offer) => offer._id === removeRequest.offerId);
if (playerOfferIndex === -1) if (playerOfferIndex === -1)
{ {
this.logger.error( this.logger.error(
@ -764,7 +762,7 @@ export class RagfairController
const pmcData = this.saveServer.getProfile(sessionId).characters.pmc; const pmcData = this.saveServer.getProfile(sessionId).characters.pmc;
const playerOffers = pmcData.RagfairInfo.offers; const playerOffers = pmcData.RagfairInfo.offers;
const playerOfferIndex = playerOffers.findIndex(offer => offer._id === extendRequest.offerId); const playerOfferIndex = playerOffers.findIndex((offer) => offer._id === extendRequest.offerId);
const secondsToAdd = extendRequest.renewalTime * TimeUtil.ONE_HOUR_AS_SECONDS; const secondsToAdd = extendRequest.renewalTime * TimeUtil.ONE_HOUR_AS_SECONDS;
if (playerOfferIndex === -1) if (playerOfferIndex === -1)

View File

@ -7,7 +7,6 @@ import { IPmcData } from "@spt-aki/models/eft/common/IPmcData";
import { IItemEventRouterResponse } from "@spt-aki/models/eft/itemEvent/IItemEventRouterResponse"; import { IItemEventRouterResponse } from "@spt-aki/models/eft/itemEvent/IItemEventRouterResponse";
import { IRepairActionDataRequest } from "@spt-aki/models/eft/repair/IRepairActionDataRequest"; import { IRepairActionDataRequest } from "@spt-aki/models/eft/repair/IRepairActionDataRequest";
import { ITraderRepairActionDataRequest } from "@spt-aki/models/eft/repair/ITraderRepairActionDataRequest"; import { ITraderRepairActionDataRequest } from "@spt-aki/models/eft/repair/ITraderRepairActionDataRequest";
import { SkillTypes } from "@spt-aki/models/enums/SkillTypes";
import { IRepairConfig } from "@spt-aki/models/spt/config/IRepairConfig"; import { IRepairConfig } from "@spt-aki/models/spt/config/IRepairConfig";
import { ILogger } from "@spt-aki/models/spt/utils/ILogger"; import { ILogger } from "@spt-aki/models/spt/utils/ILogger";
import { EventOutputHolder } from "@spt-aki/routers/EventOutputHolder"; import { EventOutputHolder } from "@spt-aki/routers/EventOutputHolder";
@ -86,7 +85,11 @@ export class RepairController
* @param pmcData player profile * @param pmcData player profile
* @returns item event router action * @returns item event router action
*/ */
public repairWithKit(sessionID: string, body: IRepairActionDataRequest, pmcData: IPmcData): IItemEventRouterResponse public repairWithKit(
sessionID: string,
body: IRepairActionDataRequest,
pmcData: IPmcData,
): IItemEventRouterResponse
{ {
const output = this.eventOutputHolder.getOutput(sessionID); const output = this.eventOutputHolder.getOutput(sessionID);

View File

@ -90,7 +90,7 @@ export class RepeatableQuestController
const pmcData = this.profileHelper.getPmcProfile(sessionID); const pmcData = this.profileHelper.getPmcProfile(sessionID);
const time = this.timeUtil.getTimestamp(); const time = this.timeUtil.getTimestamp();
const scavQuestUnlocked const scavQuestUnlocked
= pmcData?.Hideout?.Areas?.find(hideoutArea => hideoutArea.type === HideoutAreas.INTEL_CENTER)?.level >= 1; = pmcData?.Hideout?.Areas?.find((hideoutArea) => hideoutArea.type === HideoutAreas.INTEL_CENTER)?.level >= 1;
// Daily / weekly / Daily_Savage // Daily / weekly / Daily_Savage
for (const repeatableConfig of this.questConfig.repeatableQuests) for (const repeatableConfig of this.questConfig.repeatableQuests)
@ -118,7 +118,7 @@ export class RepeatableQuestController
for (const activeQuest of currentRepeatableQuestType.activeQuests) for (const activeQuest of currentRepeatableQuestType.activeQuests)
{ {
// Keep finished quests in list so player can hand in // Keep finished quests in list so player can hand in
const quest = pmcData.Quests.find(quest => quest.qid === activeQuest._id); const quest = pmcData.Quests.find((quest) => quest.qid === activeQuest._id);
if (quest) if (quest)
{ {
if (quest.status === QuestStatus.AvailableForFinish) if (quest.status === QuestStatus.AvailableForFinish)
@ -134,7 +134,7 @@ export class RepeatableQuestController
this.profileFixerService.removeDanglingConditionCounters(pmcData); this.profileFixerService.removeDanglingConditionCounters(pmcData);
// Remove expired quest from pmc.quest array // Remove expired quest from pmc.quest array
pmcData.Quests = pmcData.Quests.filter(quest => quest.qid !== activeQuest._id); pmcData.Quests = pmcData.Quests.filter((quest) => quest.qid !== activeQuest._id);
currentRepeatableQuestType.inactiveQuests.push(activeQuest); currentRepeatableQuestType.inactiveQuests.push(activeQuest);
} }
currentRepeatableQuestType.activeQuests = questsToKeep; currentRepeatableQuestType.activeQuests = questsToKeep;
@ -216,14 +216,11 @@ export class RepeatableQuestController
) )
{ {
// Elite charisma skill gives extra daily quest(s) // Elite charisma skill gives extra daily quest(s)
return repeatableConfig.numQuests + this.databaseServer.getTables() return (
.globals repeatableConfig.numQuests
.config + this.databaseServer.getTables().globals.config.SkillsSettings.Charisma.BonusSettings.EliteBonusSettings
.SkillsSettings .RepeatableQuestExtraCount
.Charisma );
.BonusSettings
.EliteBonusSettings
.RepeatableQuestExtraCount;
} }
return repeatableConfig.numQuests; return repeatableConfig.numQuests;
@ -241,7 +238,7 @@ export class RepeatableQuestController
): IPmcDataRepeatableQuest ): IPmcDataRepeatableQuest
{ {
// Get from profile, add if missing // Get from profile, add if missing
let repeatableQuestDetails = pmcData.RepeatableQuests.find(x => x.name === repeatableConfig.name); let repeatableQuestDetails = pmcData.RepeatableQuests.find((x) => x.name === repeatableConfig.name);
if (!repeatableQuestDetails) if (!repeatableQuestDetails)
{ {
repeatableQuestDetails = { repeatableQuestDetails = {
@ -331,9 +328,10 @@ export class RepeatableQuestController
const possibleLocations = Object.keys(locations); const possibleLocations = Object.keys(locations);
// Set possible locations for elimination task, if target is savage, exclude labs from locations // Set possible locations for elimination task, if target is savage, exclude labs from locations
questPool.pool.Elimination.targets[probabilityObject.key] = probabilityObject.key === "Savage" questPool.pool.Elimination.targets[probabilityObject.key]
? { locations: possibleLocations.filter(x => x !== "laboratory") } = probabilityObject.key === "Savage"
: { locations: possibleLocations }; ? { locations: possibleLocations.filter((x) => x !== "laboratory") }
: { locations: possibleLocations };
} }
} }
@ -443,7 +441,7 @@ export class RepeatableQuestController
for (const currentRepeatablePool of pmcData.RepeatableQuests) for (const currentRepeatablePool of pmcData.RepeatableQuests)
{ {
// Check for existing quest in (daily/weekly/scav arrays) // Check for existing quest in (daily/weekly/scav arrays)
const questToReplace = currentRepeatablePool.activeQuests.find(x => x._id === changeRequest.qid); const questToReplace = currentRepeatablePool.activeQuests.find((x) => x._id === changeRequest.qid);
if (!questToReplace) if (!questToReplace)
{ {
continue; continue;
@ -453,8 +451,8 @@ export class RepeatableQuestController
replacedQuestTraderId = questToReplace.traderId; replacedQuestTraderId = questToReplace.traderId;
// Update active quests to exclude the quest we're replacing // Update active quests to exclude the quest we're replacing
currentRepeatablePool.activeQuests = currentRepeatablePool.activeQuests.filter(x => currentRepeatablePool.activeQuests = currentRepeatablePool.activeQuests.filter(
x._id !== changeRequest.qid, (x) => x._id !== changeRequest.qid,
); );
// Get cost to replace existing quest // Get cost to replace existing quest
@ -462,8 +460,8 @@ export class RepeatableQuestController
delete currentRepeatablePool.changeRequirement[changeRequest.qid]; delete currentRepeatablePool.changeRequirement[changeRequest.qid];
// TODO: somehow we need to reduce the questPool by the currently active quests (for all repeatables) // TODO: somehow we need to reduce the questPool by the currently active quests (for all repeatables)
const repeatableConfig = this.questConfig.repeatableQuests.find(x => const repeatableConfig = this.questConfig.repeatableQuests.find(
x.name === currentRepeatablePool.name, (x) => x.name === currentRepeatablePool.name,
); );
const questTypePool = this.generateQuestPool(repeatableConfig, pmcData.Info.Level); const questTypePool = this.generateQuestPool(repeatableConfig, pmcData.Info.Level);
const newRepeatableQuest = this.attemptToGenerateRepeatableQuest(pmcData, questTypePool, repeatableConfig); const newRepeatableQuest = this.attemptToGenerateRepeatableQuest(pmcData, questTypePool, repeatableConfig);

View File

@ -4,7 +4,7 @@ import { ProfileHelper } from "@spt-aki/helpers/ProfileHelper";
import { TradeHelper } from "@spt-aki/helpers/TradeHelper"; import { TradeHelper } from "@spt-aki/helpers/TradeHelper";
import { TraderHelper } from "@spt-aki/helpers/TraderHelper"; import { TraderHelper } from "@spt-aki/helpers/TraderHelper";
import { IPmcData } from "@spt-aki/models/eft/common/IPmcData"; import { IPmcData } from "@spt-aki/models/eft/common/IPmcData";
import { Item, Upd } from "@spt-aki/models/eft/common/tables/IItem"; import { Item } from "@spt-aki/models/eft/common/tables/IItem";
import { ITraderBase } from "@spt-aki/models/eft/common/tables/ITrader"; import { ITraderBase } from "@spt-aki/models/eft/common/tables/ITrader";
import { IItemEventRouterResponse } from "@spt-aki/models/eft/itemEvent/IItemEventRouterResponse"; import { IItemEventRouterResponse } from "@spt-aki/models/eft/itemEvent/IItemEventRouterResponse";
import { IRagfairOffer } from "@spt-aki/models/eft/ragfair/IRagfairOffer"; import { IRagfairOffer } from "@spt-aki/models/eft/ragfair/IRagfairOffer";
@ -170,9 +170,7 @@ export class TradeController
// Skip buying items when player doesn't have needed loyalty // Skip buying items when player doesn't have needed loyalty
if (this.playerLacksTraderLoyaltyLevelToBuyOffer(fleaOffer, pmcData)) if (this.playerLacksTraderLoyaltyLevelToBuyOffer(fleaOffer, pmcData))
{ {
const errorMessage = `Unable to buy item: ${ const errorMessage = `Unable to buy item: ${fleaOffer.items[0]._tpl} from trader: ${fleaOffer.user.id} as loyalty level too low, skipping`;
fleaOffer.items[0]._tpl
} from trader: ${fleaOffer.user.id} as loyalty level too low, skipping`;
this.logger.debug(errorMessage); this.logger.debug(errorMessage);
this.httpResponse.appendErrorToOutput(output, errorMessage, BackendErrorCodes.RAGFAIRUNAVAILABLE); this.httpResponse.appendErrorToOutput(output, errorMessage, BackendErrorCodes.RAGFAIRUNAVAILABLE);
@ -291,7 +289,7 @@ export class TradeController
this.traderHelper.getTraderById(trader), this.traderHelper.getTraderById(trader),
MessageType.MESSAGE_WITH_ITEMS, MessageType.MESSAGE_WITH_ITEMS,
this.randomUtil.getArrayValue(this.databaseServer.getTables().traders[trader].dialogue.soldItems), this.randomUtil.getArrayValue(this.databaseServer.getTables().traders[trader].dialogue.soldItems),
curencyReward.flatMap(x => x), curencyReward.flatMap((x) => x),
this.timeUtil.getHoursAsSeconds(72), this.timeUtil.getHoursAsSeconds(72),
); );
} }
@ -317,10 +315,12 @@ export class TradeController
for (const itemToSell of itemWithChildren) for (const itemToSell of itemWithChildren)
{ {
const itemDetails = this.itemHelper.getItem(itemToSell._tpl); const itemDetails = this.itemHelper.getItem(itemToSell._tpl);
if (!(itemDetails[0] && this.itemHelper.isOfBaseclasses( if (
itemDetails[1]._id, !(
traderDetails.items_buy.category, itemDetails[0]
))) && this.itemHelper.isOfBaseclasses(itemDetails[1]._id, traderDetails.items_buy.category)
)
)
{ {
// Skip if tpl isn't item OR item doesn't fulfil match traders buy categories // Skip if tpl isn't item OR item doesn't fulfil match traders buy categories
continue; continue;

View File

@ -29,7 +29,8 @@ export class TraderController
@inject("ProfileHelper") protected profileHelper: ProfileHelper, @inject("ProfileHelper") protected profileHelper: ProfileHelper,
@inject("TraderHelper") protected traderHelper: TraderHelper, @inject("TraderHelper") protected traderHelper: TraderHelper,
@inject("TraderAssortService") protected traderAssortService: TraderAssortService, @inject("TraderAssortService") protected traderAssortService: TraderAssortService,
@inject("TraderPurchasePersisterService") protected traderPurchasePersisterService: TraderPurchasePersisterService, @inject("TraderPurchasePersisterService")
protected traderPurchasePersisterService: TraderPurchasePersisterService,
@inject("FenceService") protected fenceService: FenceService, @inject("FenceService") protected fenceService: FenceService,
@inject("FenceBaseAssortGenerator") protected fenceBaseAssortGenerator: FenceBaseAssortGenerator, @inject("FenceBaseAssortGenerator") protected fenceBaseAssortGenerator: FenceBaseAssortGenerator,
@inject("ConfigServer") protected configServer: ConfigServer, @inject("ConfigServer") protected configServer: ConfigServer,

View File

@ -28,7 +28,11 @@ export class WishlistController
} }
/** Handle RemoveFromWishList event */ /** Handle RemoveFromWishList event */
public removeFromWishList(pmcData: IPmcData, body: IWishlistActionData, sessionID: string): IItemEventRouterResponse public removeFromWishList(
pmcData: IPmcData,
body: IWishlistActionData,
sessionID: string,
): IItemEventRouterResponse
{ {
for (let i = 0; i < pmcData.WishList.length; i++) for (let i = 0; i < pmcData.WishList.length; i++)
{ {

View File

@ -791,9 +791,13 @@ export class Container
depContainer.register<CustomizationController>("CustomizationController", { depContainer.register<CustomizationController>("CustomizationController", {
useClass: CustomizationController, useClass: CustomizationController,
}); });
depContainer.register<DialogueController>("DialogueController", { useClass: DialogueController }, { depContainer.register<DialogueController>(
lifecycle: Lifecycle.Singleton, "DialogueController",
}); { useClass: DialogueController },
{
lifecycle: Lifecycle.Singleton,
},
);
depContainer.register<GameController>("GameController", { useClass: GameController }); depContainer.register<GameController>("GameController", { useClass: GameController });
depContainer.register<HandbookController>("HandbookController", { useClass: HandbookController }); depContainer.register<HandbookController>("HandbookController", { useClass: HandbookController });
depContainer.register<HealthController>("HealthController", { useClass: HealthController }); depContainer.register<HealthController>("HealthController", { useClass: HealthController });

View File

@ -29,9 +29,13 @@ export class Router
{ {
if (partialMatch) if (partialMatch)
{ {
return this.getInternalHandledRoutes().filter(r => r.dynamic).some(r => url.includes(r.route)); return this.getInternalHandledRoutes()
.filter((r) => r.dynamic)
.some((r) => url.includes(r.route));
} }
return this.getInternalHandledRoutes().filter(r => !r.dynamic).some(r => r.route === url); return this.getInternalHandledRoutes()
.filter((r) => !r.dynamic)
.some((r) => r.route === url);
} }
} }
@ -44,12 +48,12 @@ export class StaticRouter extends Router
public async handleStatic(url: string, info: any, sessionID: string, output: string): Promise<any> public async handleStatic(url: string, info: any, sessionID: string, output: string): Promise<any>
{ {
return this.routes.find(route => route.url === url).action(url, info, sessionID, output); return this.routes.find((route) => route.url === url).action(url, info, sessionID, output);
} }
public override getHandledRoutes(): HandledRoute[] public override getHandledRoutes(): HandledRoute[]
{ {
return this.routes.map(route => new HandledRoute(route.url, false)); return this.routes.map((route) => new HandledRoute(route.url, false));
} }
} }
@ -62,12 +66,12 @@ export class DynamicRouter extends Router
public async handleDynamic(url: string, info: any, sessionID: string, output: string): Promise<any> public async handleDynamic(url: string, info: any, sessionID: string, output: string): Promise<any>
{ {
return this.routes.find(r => url.includes(r.url)).action(url, info, sessionID, output); return this.routes.find((r) => url.includes(r.url)).action(url, info, sessionID, output);
} }
public override getHandledRoutes(): HandledRoute[] public override getHandledRoutes(): HandledRoute[]
{ {
return this.routes.map(route => new HandledRoute(route.url, true)); return this.routes.map((route) => new HandledRoute(route.url, true));
} }
} }
@ -97,7 +101,10 @@ export class SaveLoadRouter extends Router
export class HandledRoute export class HandledRoute
{ {
constructor(public route: string, public dynamic: boolean) constructor(
public route: string,
public dynamic: boolean,
)
{} {}
} }

View File

@ -233,8 +233,9 @@ export class BotEquipmentModGenerator
} }
// Get the front/back/side weights based on bots level // Get the front/back/side weights based on bots level
const plateSlotWeights = settings.botEquipmentConfig?.armorPlateWeighting?.find(armorWeight => const plateSlotWeights = settings.botEquipmentConfig?.armorPlateWeighting?.find(
settings.botLevel >= armorWeight.levelRange.min && settings.botLevel <= armorWeight.levelRange.max, (armorWeight) =>
settings.botLevel >= armorWeight.levelRange.min && settings.botLevel <= armorWeight.levelRange.max,
); );
if (!plateSlotWeights) if (!plateSlotWeights)
{ {
@ -260,17 +261,17 @@ export class BotEquipmentModGenerator
const chosenArmorPlateLevel = this.weightedRandomHelper.getWeightedValue<string>(plateWeights); const chosenArmorPlateLevel = this.weightedRandomHelper.getWeightedValue<string>(plateWeights);
// Convert the array of ids into database items // Convert the array of ids into database items
const platesFromDb = existingPlateTplPool.map(plateTpl => this.itemHelper.getItem(plateTpl)[1]); const platesFromDb = existingPlateTplPool.map((plateTpl) => this.itemHelper.getItem(plateTpl)[1]);
// Filter plates to the chosen level based on its armorClass property // Filter plates to the chosen level based on its armorClass property
const platesOfDesiredLevel = platesFromDb.filter(item => item._props.armorClass === chosenArmorPlateLevel); const platesOfDesiredLevel = platesFromDb.filter((item) => item._props.armorClass === chosenArmorPlateLevel);
if (platesOfDesiredLevel.length === 0) if (platesOfDesiredLevel.length === 0)
{ {
this.logger.debug( this.logger.debug(
`Plate filter was too restrictive for armor: ${armorItem._name} ${armorItem._id}, unable to find plates of level: ${chosenArmorPlateLevel}. Using mod items default plate`, `Plate filter was too restrictive for armor: ${armorItem._name} ${armorItem._id}, unable to find plates of level: ${chosenArmorPlateLevel}. Using mod items default plate`,
); );
const relatedItemDbModSlot = armorItem._props.Slots.find(slot => slot._name.toLowerCase() === modSlot); const relatedItemDbModSlot = armorItem._props.Slots.find((slot) => slot._name.toLowerCase() === modSlot);
const defaultPlate = relatedItemDbModSlot._props.filters[0].Plate; const defaultPlate = relatedItemDbModSlot._props.filters[0].Plate;
if (!defaultPlate) if (!defaultPlate)
{ {
@ -280,8 +281,8 @@ export class BotEquipmentModGenerator
const defaultPreset = this.presetHelper.getDefaultPreset(armorItem._id); const defaultPreset = this.presetHelper.getDefaultPreset(armorItem._id);
if (defaultPreset) if (defaultPreset)
{ {
const relatedPresetSlot = defaultPreset._items.find(item => const relatedPresetSlot = defaultPreset._items.find(
item.slotId?.toLowerCase() === modSlot, (item) => item.slotId?.toLowerCase() === modSlot,
); );
if (relatedPresetSlot) if (relatedPresetSlot)
{ {
@ -305,7 +306,7 @@ export class BotEquipmentModGenerator
// Only return the items ids // Only return the items ids
result.result = Result.SUCCESS; result.result = Result.SUCCESS;
result.plateModTpls = platesOfDesiredLevel.map(item => item._id); result.plateModTpls = platesOfDesiredLevel.map((item) => item._id);
return result; return result;
} }
@ -345,8 +346,11 @@ export class BotEquipmentModGenerator
const compatibleModsPool = modPool[parentTemplate._id]; const compatibleModsPool = modPool[parentTemplate._id];
if ( if (
!(parentTemplate._props.Slots.length || parentTemplate._props.Cartridges?.length !(
|| parentTemplate._props.Chambers?.length) parentTemplate._props.Slots.length
|| parentTemplate._props.Cartridges?.length
|| parentTemplate._props.Chambers?.length
)
) )
{ {
this.logger.error( this.logger.error(
@ -467,10 +471,9 @@ export class BotEquipmentModGenerator
// Handguard mod can take a sub handguard mod + weapon has no UBGL (takes same slot) // Handguard mod can take a sub handguard mod + weapon has no UBGL (takes same slot)
// Force spawn chance to be 100% to ensure it gets added // Force spawn chance to be 100% to ensure it gets added
if ( if (
modSlot === "mod_handguard" && modToAddTemplate._props.Slots.find(slot => modSlot === "mod_handguard"
slot._name === "mod_handguard", && modToAddTemplate._props.Slots.find((slot) => slot._name === "mod_handguard")
) && !weapon.find((item) => item.slotId === "mod_launcher")
&& !weapon.find(item => item.slotId === "mod_launcher")
) )
{ {
// Needed for handguards with lower // Needed for handguards with lower
@ -480,8 +483,9 @@ export class BotEquipmentModGenerator
// If stock mod can take a sub stock mod, force spawn chance to be 100% to ensure sub-stock gets added // If stock mod can take a sub stock mod, force spawn chance to be 100% to ensure sub-stock gets added
// Or if mod_stock is configured to be forced on // Or if mod_stock is configured to be forced on
if ( if (
modSlot === "mod_stock" && modToAddTemplate._props.Slots.find(slot => modSlot === "mod_stock"
slot._name.includes("mod_stock") || botEquipConfig.forceStock, && modToAddTemplate._props.Slots.find(
(slot) => slot._name.includes("mod_stock") || botEquipConfig.forceStock,
) )
) )
{ {
@ -549,8 +553,9 @@ export class BotEquipmentModGenerator
protected modIsFrontOrRearSight(modSlot: string, tpl: string): boolean protected modIsFrontOrRearSight(modSlot: string, tpl: string): boolean
{ {
// Gas block /w front sight is special case, deem it a 'front sight' too // Gas block /w front sight is special case, deem it a 'front sight' too
if (modSlot === "mod_gas_block" && tpl === "5ae30e795acfc408fb139a0b") // M4A1 front sight with gas block if (modSlot === "mod_gas_block" && tpl === "5ae30e795acfc408fb139a0b")
{ {
// M4A1 front sight with gas block
return true; return true;
} }
@ -565,15 +570,17 @@ export class BotEquipmentModGenerator
*/ */
protected modSlotCanHoldScope(modSlot: string, modsParentId: string): boolean protected modSlotCanHoldScope(modSlot: string, modsParentId: string): boolean
{ {
return [ return (
"mod_scope", [
"mod_mount", "mod_scope",
"mod_mount_000", "mod_mount",
"mod_scope_000", "mod_mount_000",
"mod_scope_001", "mod_scope_000",
"mod_scope_002", "mod_scope_001",
"mod_scope_003", "mod_scope_002",
].includes(modSlot.toLowerCase()) && modsParentId === BaseClasses.MOUNT; "mod_scope_003",
].includes(modSlot.toLowerCase()) && modsParentId === BaseClasses.MOUNT
);
} }
/** /**
@ -700,11 +707,11 @@ export class BotEquipmentModGenerator
case "patron_in_weapon": case "patron_in_weapon":
case "patron_in_weapon_000": case "patron_in_weapon_000":
case "patron_in_weapon_001": case "patron_in_weapon_001":
return parentTemplate._props.Chambers.find(chamber => chamber._name.includes(modSlotLower)); return parentTemplate._props.Chambers.find((chamber) => chamber._name.includes(modSlotLower));
case "cartridges": case "cartridges":
return parentTemplate._props.Cartridges.find(c => c._name.toLowerCase() === modSlotLower); return parentTemplate._props.Cartridges.find((c) => c._name.toLowerCase() === modSlotLower);
default: default:
return parentTemplate._props.Slots.find(s => s._name.toLowerCase() === modSlotLower); return parentTemplate._props.Slots.find((s) => s._name.toLowerCase() === modSlotLower);
} }
} }
@ -762,7 +769,7 @@ export class BotEquipmentModGenerator
): [boolean, ITemplateItem] ): [boolean, ITemplateItem]
{ {
/** Slot mod will fill */ /** Slot mod will fill */
const parentSlot = parentTemplate._props.Slots.find(i => i._name === modSlot); const parentSlot = parentTemplate._props.Slots.find((i) => i._name === modSlot);
const weaponTemplate = this.itemHelper.getItem(weapon[0]._tpl)[1]; const weaponTemplate = this.itemHelper.getItem(weapon[0]._tpl)[1];
// It's ammo, use predefined ammo parameter // It's ammo, use predefined ammo parameter
@ -837,9 +844,7 @@ export class BotEquipmentModGenerator
if (parentSlot._required) if (parentSlot._required)
{ {
this.logger.warning( this.logger.warning(
`Required slot unable to be filled, ${modSlot} on ${parentTemplate._name} ${parentTemplate._id} for weapon: ${ `Required slot unable to be filled, ${modSlot} on ${parentTemplate._name} ${parentTemplate._id} for weapon: ${weapon[0]._tpl}`,
weapon[0]._tpl
}`,
); );
} }
@ -950,8 +955,8 @@ export class BotEquipmentModGenerator
if (modSpawnResult === ModSpawn.DEFAULT_MOD) if (modSpawnResult === ModSpawn.DEFAULT_MOD)
{ {
const matchingPreset = this.getMatchingPreset(weaponTemplate, parentTemplate._id); const matchingPreset = this.getMatchingPreset(weaponTemplate, parentTemplate._id);
const matchingModFromPreset = matchingPreset?._items.find(item => const matchingModFromPreset = matchingPreset?._items.find(
item?.slotId?.toLowerCase() === modSlot.toLowerCase(), (item) => item?.slotId?.toLowerCase() === modSlot.toLowerCase(),
); );
// Only filter mods down to single default item if it already exists in existing itemModPool, OR the default item has no children // Only filter mods down to single default item if it already exists in existing itemModPool, OR the default item has no children
@ -968,8 +973,8 @@ export class BotEquipmentModGenerator
// Get an array of items that are allowed in slot from parent item // Get an array of items that are allowed in slot from parent item
// Check the filter of the slot to ensure a chosen mod fits // Check the filter of the slot to ensure a chosen mod fits
const parentSlotCompatibleItems = parentTemplate._props.Slots?.find(slot => const parentSlotCompatibleItems = parentTemplate._props.Slots?.find(
slot._name.toLowerCase() === modSlot.toLowerCase(), (slot) => slot._name.toLowerCase() === modSlot.toLowerCase(),
)._props.filters[0].Filter; )._props.filters[0].Filter;
// Mod isnt in existing pool, only add if it has no children and matches parent filter // Mod isnt in existing pool, only add if it has no children and matches parent filter
@ -985,7 +990,9 @@ export class BotEquipmentModGenerator
if (itemModPool[modSlot]?.length > 1) if (itemModPool[modSlot]?.length > 1)
{ {
this.logger.debug(`No default: ${modSlot} mod found on template: ${weaponTemplate._name} and multiple items found in existing pool`); this.logger.debug(
`No default: ${modSlot} mod found on template: ${weaponTemplate._name} and multiple items found in existing pool`,
);
} }
// Couldnt find default in globals, use existing mod pool data // Couldnt find default in globals, use existing mod pool data
@ -1178,7 +1185,7 @@ export class BotEquipmentModGenerator
botEquipBlacklist: EquipmentFilterDetails, botEquipBlacklist: EquipmentFilterDetails,
): void ): void
{ {
const desiredSlotObject = modTemplate._props.Slots.find(slot => slot._name.includes(desiredSlotName)); const desiredSlotObject = modTemplate._props.Slots.find((slot) => slot._name.includes(desiredSlotName));
if (desiredSlotObject) if (desiredSlotObject)
{ {
const supportedSubMods = desiredSlotObject._props.filters[0].Filter; const supportedSubMods = desiredSlotObject._props.filters[0].Filter;
@ -1261,10 +1268,10 @@ export class BotEquipmentModGenerator
let result: string[] = []; let result: string[] = [];
// Get item blacklist and mod equipment blacklist as one array // Get item blacklist and mod equipment blacklist as one array
const blacklist = this.itemFilterService.getBlacklistedItems().concat( const blacklist = this.itemFilterService
botEquipBlacklist.equipment[modSlot] || [], .getBlacklistedItems()
); .concat(botEquipBlacklist.equipment[modSlot] || []);
result = allowedMods.filter(tpl => !blacklist.includes(tpl)); result = allowedMods.filter((tpl) => !blacklist.includes(tpl));
return result; return result;
} }
@ -1290,7 +1297,7 @@ export class BotEquipmentModGenerator
weaponName: parentTemplate._name, weaponName: parentTemplate._name,
}), }),
); );
const camoraSlots = parentTemplate._props.Slots.filter(slot => slot._name.startsWith("camora")); const camoraSlots = parentTemplate._props.Slots.filter((slot) => slot._name.startsWith("camora"));
// Attempt to generate camora slots for item // Attempt to generate camora slots for item
modPool[parentTemplate._id] = {}; modPool[parentTemplate._id] = {};
@ -1397,9 +1404,7 @@ export class BotEquipmentModGenerator
if (!whitelistedSightTypes) if (!whitelistedSightTypes)
{ {
this.logger.debug( this.logger.debug(
`Unable to find whitelist for weapon type: ${weaponDetails[1]._parent} ${ `Unable to find whitelist for weapon type: ${weaponDetails[1]._parent} ${weaponDetails[1]._name}, skipping sight filtering`,
weaponDetails[1]._name
}, skipping sight filtering`,
); );
return scopes; return scopes;
@ -1424,16 +1429,17 @@ export class BotEquipmentModGenerator
{ {
// Check to see if mount has a scope slot (only include primary slot, ignore the rest like the backup sight slots) // Check to see if mount has a scope slot (only include primary slot, ignore the rest like the backup sight slots)
// Should only find 1 as there's currently no items with a mod_scope AND a mod_scope_000 // Should only find 1 as there's currently no items with a mod_scope AND a mod_scope_000
const scopeSlot = itemDetails._props.Slots.filter(slot => const scopeSlot = itemDetails._props.Slots.filter((slot) =>
["mod_scope", "mod_scope_000"].includes(slot._name), ["mod_scope", "mod_scope_000"].includes(slot._name),
); );
// Mods scope slot found must allow ALL whitelisted scope types OR be a mount // Mods scope slot found must allow ALL whitelisted scope types OR be a mount
if ( if (
scopeSlot?.every(slot => scopeSlot?.every((slot) =>
slot._props.filters[0].Filter.every(tpl => slot._props.filters[0].Filter.every(
this.itemHelper.isOfBaseclasses(tpl, whitelistedSightTypes) (tpl) =>
|| this.itemHelper.isOfBaseclass(tpl, BaseClasses.MOUNT), this.itemHelper.isOfBaseclasses(tpl, whitelistedSightTypes)
|| this.itemHelper.isOfBaseclass(tpl, BaseClasses.MOUNT),
), ),
) )
) )

View File

@ -232,7 +232,11 @@ export class BotGenerator
* @param appearance Appearance settings to choose from * @param appearance Appearance settings to choose from
* @param botGenerationDetails Generation details * @param botGenerationDetails Generation details
*/ */
protected setBotAppearance(bot: IBotBase, appearance: Appearance, botGenerationDetails: BotGenerationDetails): void protected setBotAppearance(
bot: IBotBase,
appearance: Appearance,
botGenerationDetails: BotGenerationDetails,
): void
{ {
bot.Customization.Head = this.weightedRandomHelper.getWeightedValue<string>(appearance.head); bot.Customization.Head = this.weightedRandomHelper.getWeightedValue<string>(appearance.head);
bot.Customization.Body = this.weightedRandomHelper.getWeightedValue<string>(appearance.body); bot.Customization.Body = this.weightedRandomHelper.getWeightedValue<string>(appearance.body);
@ -425,27 +429,29 @@ export class BotGenerator
return []; return [];
} }
return Object.keys(skills).map((skillKey): IBaseSkill => return Object.keys(skills)
{ .map((skillKey): IBaseSkill =>
// Get skill from dict, skip if not found
const skill = skills[skillKey];
if (!skill)
{ {
return null; // Get skill from dict, skip if not found
} const skill = skills[skillKey];
if (!skill)
{
return null;
}
// All skills have id and progress props // All skills have id and progress props
const skillToAdd: IBaseSkill = { Id: skillKey, Progress: this.randomUtil.getInt(skill.min, skill.max) }; const skillToAdd: IBaseSkill = { Id: skillKey, Progress: this.randomUtil.getInt(skill.min, skill.max) };
// Common skills have additional props // Common skills have additional props
if (isCommonSkills) if (isCommonSkills)
{ {
(skillToAdd as Common).PointsEarnedDuringSession = 0; (skillToAdd as Common).PointsEarnedDuringSession = 0;
(skillToAdd as Common).LastAccess = 0; (skillToAdd as Common).LastAccess = 0;
} }
return skillToAdd; return skillToAdd;
}).filter(x => x !== null); })
.filter((x) => x !== null);
} }
/** /**

View File

@ -307,8 +307,9 @@ export class BotInventoryGenerator
*/ */
protected generateEquipment(settings: IGenerateEquipmentProperties): boolean protected generateEquipment(settings: IGenerateEquipmentProperties): boolean
{ {
const spawnChance = ([EquipmentSlots.POCKETS, EquipmentSlots.SECURED_CONTAINER] as string[]) const spawnChance = ([EquipmentSlots.POCKETS, EquipmentSlots.SECURED_CONTAINER] as string[]).includes(
.includes(settings.rootEquipmentSlot) settings.rootEquipmentSlot,
)
? 100 ? 100
: settings.spawnChances.equipment[settings.rootEquipmentSlot]; : settings.spawnChances.equipment[settings.rootEquipmentSlot];
@ -442,7 +443,7 @@ export class BotInventoryGenerator
for (const modSlot of Object.keys(modPool ?? [])) for (const modSlot of Object.keys(modPool ?? []))
{ {
const blacklistedMods = equipmentBlacklist[0]?.equipment[modSlot] || []; const blacklistedMods = equipmentBlacklist[0]?.equipment[modSlot] || [];
const filteredMods = modPool[modSlot].filter(x => !blacklistedMods.includes(x)); const filteredMods = modPool[modSlot].filter((x) => !blacklistedMods.includes(x));
if (filteredMods.length > 0) if (filteredMods.length > 0)
{ {
@ -504,17 +505,21 @@ export class BotInventoryGenerator
protected getDesiredWeaponsForBot(equipmentChances: Chances): { slot: EquipmentSlots, shouldSpawn: boolean }[] protected getDesiredWeaponsForBot(equipmentChances: Chances): { slot: EquipmentSlots, shouldSpawn: boolean }[]
{ {
const shouldSpawnPrimary = this.randomUtil.getChance100(equipmentChances.equipment.FirstPrimaryWeapon); const shouldSpawnPrimary = this.randomUtil.getChance100(equipmentChances.equipment.FirstPrimaryWeapon);
return [{ slot: EquipmentSlots.FIRST_PRIMARY_WEAPON, shouldSpawn: shouldSpawnPrimary }, { return [
slot: EquipmentSlots.SECOND_PRIMARY_WEAPON, { slot: EquipmentSlots.FIRST_PRIMARY_WEAPON, shouldSpawn: shouldSpawnPrimary },
shouldSpawn: shouldSpawnPrimary {
? this.randomUtil.getChance100(equipmentChances.equipment.SecondPrimaryWeapon) slot: EquipmentSlots.SECOND_PRIMARY_WEAPON,
: false, shouldSpawn: shouldSpawnPrimary
}, { ? this.randomUtil.getChance100(equipmentChances.equipment.SecondPrimaryWeapon)
slot: EquipmentSlots.HOLSTER, : false,
shouldSpawn: shouldSpawnPrimary },
? this.randomUtil.getChance100(equipmentChances.equipment.Holster) // Primary weapon = roll for chance at pistol {
: true, // No primary = force pistol slot: EquipmentSlots.HOLSTER,
}]; shouldSpawn: shouldSpawnPrimary
? this.randomUtil.getChance100(equipmentChances.equipment.Holster) // Primary weapon = roll for chance at pistol
: true, // No primary = force pistol
},
];
} }
/** /**

View File

@ -65,9 +65,10 @@ export class BotLevelGenerator
maxLevel: number, maxLevel: number,
): number ): number
{ {
const maxPossibleLevel = botGenerationDetails.isPmc && botGenerationDetails.locationSpecificPmcLevelOverride const maxPossibleLevel
? Math.min(botGenerationDetails.locationSpecificPmcLevelOverride.max, maxLevel) // Was a PMC and they have a level override = botGenerationDetails.isPmc && botGenerationDetails.locationSpecificPmcLevelOverride
: Math.min(levelDetails.max, maxLevel); // Not pmc with override or non-pmc ? Math.min(botGenerationDetails.locationSpecificPmcLevelOverride.max, maxLevel) // Was a PMC and they have a level override
: Math.min(levelDetails.max, maxLevel); // Not pmc with override or non-pmc
let level = botGenerationDetails.playerLevel + botGenerationDetails.botRelativeLevelDeltaMax; let level = botGenerationDetails.playerLevel + botGenerationDetails.botRelativeLevelDeltaMax;
if (level > maxPossibleLevel) if (level > maxPossibleLevel)
@ -91,12 +92,13 @@ export class BotLevelGenerator
maxlevel: number, maxlevel: number,
): number ): number
{ {
const minPossibleLevel = botGenerationDetails.isPmc && botGenerationDetails.locationSpecificPmcLevelOverride const minPossibleLevel
? Math.min( = botGenerationDetails.isPmc && botGenerationDetails.locationSpecificPmcLevelOverride
Math.max(levelDetails.min, botGenerationDetails.locationSpecificPmcLevelOverride.min), // Biggest between json min and the botgen min ? Math.min(
maxlevel, // Fallback if value above is crazy (default is 79) Math.max(levelDetails.min, botGenerationDetails.locationSpecificPmcLevelOverride.min), // Biggest between json min and the botgen min
) maxlevel, // Fallback if value above is crazy (default is 79)
: Math.min(levelDetails.min, maxlevel); // Not pmc with override or non-pmc )
: Math.min(levelDetails.min, maxlevel); // Not pmc with override or non-pmc
let level = botGenerationDetails.playerLevel - botGenerationDetails.botRelativeLevelDeltaMin; let level = botGenerationDetails.playerLevel - botGenerationDetails.botRelativeLevelDeltaMin;
if (level < minPossibleLevel) if (level < minPossibleLevel)

View File

@ -85,7 +85,8 @@ export class BotLootGenerator
// Limits on item types to be added as loot // Limits on item types to be added as loot
const itemCounts = botJsonTemplate.generation.items; const itemCounts = botJsonTemplate.generation.items;
if(!itemCounts.backpackLoot.weights if (
!itemCounts.backpackLoot.weights
|| !itemCounts.pocketLoot.weights || !itemCounts.pocketLoot.weights
|| !itemCounts.vestLoot.weights || !itemCounts.vestLoot.weights
|| !itemCounts.specialItems.weights || !itemCounts.specialItems.weights
@ -98,7 +99,9 @@ export class BotLootGenerator
|| !itemCounts.grenades.weights || !itemCounts.grenades.weights
) )
{ {
this.logger.warning(`Unable to generate bot loot for ${botRole} as bot.generation.items lacks data, skipping`); this.logger.warning(
`Unable to generate bot loot for ${botRole} as bot.generation.items lacks data, skipping`,
);
return; return;
} }
@ -334,12 +337,12 @@ export class BotLootGenerator
{ {
const result = [EquipmentSlots.POCKETS]; const result = [EquipmentSlots.POCKETS];
if (botInventory.items.find(item => item.slotId === EquipmentSlots.TACTICAL_VEST)) if (botInventory.items.find((item) => item.slotId === EquipmentSlots.TACTICAL_VEST))
{ {
result.push(EquipmentSlots.TACTICAL_VEST); result.push(EquipmentSlots.TACTICAL_VEST);
} }
if (botInventory.items.find(item => item.slotId === EquipmentSlots.BACKPACK)) if (botInventory.items.find((item) => item.slotId === EquipmentSlots.BACKPACK))
{ {
result.push(EquipmentSlots.BACKPACK); result.push(EquipmentSlots.BACKPACK);
} }
@ -457,11 +460,13 @@ export class BotLootGenerator
} }
const newRootItemId = this.hashUtil.generate(); const newRootItemId = this.hashUtil.generate();
const itemWithChildrenToAdd: Item[] = [{ const itemWithChildrenToAdd: Item[] = [
_id: newRootItemId, {
_tpl: itemToAddTemplate._id, _id: newRootItemId,
...this.botGeneratorHelper.generateExtraPropertiesForItem(itemToAddTemplate, botRole), _tpl: itemToAddTemplate._id,
}]; ...this.botGeneratorHelper.generateExtraPropertiesForItem(itemToAddTemplate, botRole),
},
];
// Is Simple-Wallet / WZ wallet // Is Simple-Wallet / WZ wallet
if (this.botConfig.walletLoot.walletTplPool.includes(weightedItemTpl)) if (this.botConfig.walletLoot.walletTplPool.includes(weightedItemTpl))
@ -493,7 +498,7 @@ export class BotLootGenerator
); );
} }
itemWithChildrenToAdd.push(...itemsToAdd.flatMap(moneyStack => moneyStack)); itemWithChildrenToAdd.push(...itemsToAdd.flatMap((moneyStack) => moneyStack));
} }
} }
} }
@ -526,9 +531,9 @@ export class BotLootGenerator
if (fitItemIntoContainerAttempts >= 4) if (fitItemIntoContainerAttempts >= 4)
{ {
this.logger.debug( this.logger.debug(
`Failed to place item ${i} of ${totalItemCount} items into ${botRole} containers: ${ `Failed to place item ${i} of ${totalItemCount} items into ${botRole} containers: ${equipmentSlots.join(
equipmentSlots.join(",") ",",
}. Tried ${fitItemIntoContainerAttempts} times, reason: ${ )}. Tried ${fitItemIntoContainerAttempts} times, reason: ${
ItemAddedResult[itemAddedResult] ItemAddedResult[itemAddedResult]
}, skipping`, }, skipping`,
); );
@ -571,12 +576,14 @@ export class BotLootGenerator
const chosenStackCount = Number( const chosenStackCount = Number(
this.weightedRandomHelper.getWeightedValue<string>(this.botConfig.walletLoot.stackSizeWeight), this.weightedRandomHelper.getWeightedValue<string>(this.botConfig.walletLoot.stackSizeWeight),
); );
result.push([{ result.push([
_id: this.hashUtil.generate(), {
_tpl: this.weightedRandomHelper.getWeightedValue<string>(this.botConfig.walletLoot.currencyWeight), _id: this.hashUtil.generate(),
parentId: walletId, _tpl: this.weightedRandomHelper.getWeightedValue<string>(this.botConfig.walletLoot.currencyWeight),
upd: { StackObjectsCount: chosenStackCount }, parentId: walletId,
}]); upd: { StackObjectsCount: chosenStackCount },
},
]);
} }
return result; return result;
@ -675,9 +682,7 @@ export class BotLootGenerator
if (result !== ItemAddedResult.SUCCESS) if (result !== ItemAddedResult.SUCCESS)
{ {
this.logger.debug( this.logger.debug(
`Failed to add additional weapon ${generatedWeapon.weapon[0]._id} to bot backpack, reason: ${ `Failed to add additional weapon ${generatedWeapon.weapon[0]._id} to bot backpack, reason: ${ItemAddedResult[result]}`,
ItemAddedResult[result]
}`,
); );
} }
} }
@ -789,12 +794,13 @@ export class BotLootGenerator
*/ */
protected randomiseAmmoStackSize(isPmc: boolean, itemTemplate: ITemplateItem, ammoItem: Item): void protected randomiseAmmoStackSize(isPmc: boolean, itemTemplate: ITemplateItem, ammoItem: Item): void
{ {
const randomSize = itemTemplate._props.StackMaxSize === 1 const randomSize
? 1 = itemTemplate._props.StackMaxSize === 1
: this.randomUtil.getInt( ? 1
itemTemplate._props.StackMinRandom, : this.randomUtil.getInt(
Math.min(itemTemplate._props.StackMaxRandom, 60), itemTemplate._props.StackMinRandom,
); Math.min(itemTemplate._props.StackMaxRandom, 60),
);
this.itemHelper.addUpdObjectToItem(ammoItem); this.itemHelper.addUpdObjectToItem(ammoItem);

View File

@ -199,7 +199,7 @@ export class BotWeaponGenerator
} }
// Fill existing magazines to full and sync ammo type // Fill existing magazines to full and sync ammo type
for (const magazine of weaponWithModsArray.filter(item => item.slotId === this.modMagazineSlotId)) for (const magazine of weaponWithModsArray.filter((item) => item.slotId === this.modMagazineSlotId))
{ {
this.fillExistingMagazines(weaponWithModsArray, magazine, ammoTpl); this.fillExistingMagazines(weaponWithModsArray, magazine, ammoTpl);
} }
@ -211,12 +211,12 @@ export class BotWeaponGenerator
) )
{ {
// Guns have variety of possible Chamber ids, patron_in_weapon/patron_in_weapon_000/patron_in_weapon_001 // Guns have variety of possible Chamber ids, patron_in_weapon/patron_in_weapon_000/patron_in_weapon_001
const chamberSlotNames = weaponItemTemplate._props.Chambers.map(x => x._name); const chamberSlotNames = weaponItemTemplate._props.Chambers.map((x) => x._name);
this.addCartridgeToChamber(weaponWithModsArray, ammoTpl, chamberSlotNames); this.addCartridgeToChamber(weaponWithModsArray, ammoTpl, chamberSlotNames);
} }
// Fill UBGL if found // Fill UBGL if found
const ubglMod = weaponWithModsArray.find(x => x.slotId === "mod_launcher"); const ubglMod = weaponWithModsArray.find((x) => x.slotId === "mod_launcher");
let ubglAmmoTpl: string = undefined; let ubglAmmoTpl: string = undefined;
if (ubglMod) if (ubglMod)
{ {
@ -245,7 +245,7 @@ export class BotWeaponGenerator
{ {
for (const slotId of chamberSlotIds) for (const slotId of chamberSlotIds)
{ {
const existingItemWithSlot = weaponWithModsArray.find(x => x.slotId === slotId); const existingItemWithSlot = weaponWithModsArray.find((x) => x.slotId === slotId);
if (!existingItemWithSlot) if (!existingItemWithSlot)
{ {
// Not found, add new slot to weapon // Not found, add new slot to weapon
@ -284,13 +284,15 @@ export class BotWeaponGenerator
botRole: string, botRole: string,
): Item[] ): Item[]
{ {
return [{ return [
_id: this.hashUtil.generate(), {
_tpl: weaponTpl, _id: this.hashUtil.generate(),
parentId: weaponParentId, _tpl: weaponTpl,
slotId: equipmentSlot, parentId: weaponParentId,
...this.botGeneratorHelper.generateExtraPropertiesForItem(weaponItemTemplate, botRole), slotId: equipmentSlot,
}]; ...this.botGeneratorHelper.generateExtraPropertiesForItem(weaponItemTemplate, botRole),
},
];
} }
/** /**
@ -366,11 +368,11 @@ export class BotWeaponGenerator
} }
// Iterate over required slots in db item, check mod exists for that slot // Iterate over required slots in db item, check mod exists for that slot
for (const modSlotTemplate of modTemplate._props.Slots.filter(slot => slot._required)) for (const modSlotTemplate of modTemplate._props.Slots.filter((slot) => slot._required))
{ {
const slotName = modSlotTemplate._name; const slotName = modSlotTemplate._name;
const weaponSlotItem = weaponItemArray.find(weaponItem => const weaponSlotItem = weaponItemArray.find(
weaponItem.parentId === mod._id && weaponItem.slotId === slotName, (weaponItem) => weaponItem.parentId === mod._id && weaponItem.slotId === slotName,
); );
if (!weaponSlotItem) if (!weaponSlotItem)
{ {
@ -441,9 +443,9 @@ export class BotWeaponGenerator
ammoTemplate, ammoTemplate,
inventory, inventory,
); );
this.inventoryMagGenComponents.find(v => v.canHandleInventoryMagGen(inventoryMagGenModel)).process( this.inventoryMagGenComponents
inventoryMagGenModel, .find((v) => v.canHandleInventoryMagGen(inventoryMagGenModel))
); .process(inventoryMagGenModel);
// Add x stacks of bullets to SecuredContainer (bots use a magic mag packing skill to reload instantly) // Add x stacks of bullets to SecuredContainer (bots use a magic mag packing skill to reload instantly)
this.addAmmoToSecureContainer( this.addAmmoToSecureContainer(
@ -467,7 +469,7 @@ export class BotWeaponGenerator
): void ): void
{ {
// Find ubgl mod item + get details of it from db // Find ubgl mod item + get details of it from db
const ubglMod = weaponMods.find(x => x.slotId === "mod_launcher"); const ubglMod = weaponMods.find((x) => x.slotId === "mod_launcher");
const ubglDbTemplate = this.itemHelper.getItem(ubglMod._tpl)[1]; const ubglDbTemplate = this.itemHelper.getItem(ubglMod._tpl)[1];
// Define min/max of how many grenades bot will have // Define min/max of how many grenades bot will have
@ -488,9 +490,9 @@ export class BotWeaponGenerator
ubglAmmoDbTemplate, ubglAmmoDbTemplate,
inventory, inventory,
); );
this.inventoryMagGenComponents.find(v => v.canHandleInventoryMagGen(ubglAmmoGenModel)).process( this.inventoryMagGenComponents
ubglAmmoGenModel, .find((v) => v.canHandleInventoryMagGen(ubglAmmoGenModel))
); .process(ubglAmmoGenModel);
// Store extra grenades in secure container // Store extra grenades in secure container
this.addAmmoToSecureContainer(5, generatedWeaponResult.chosenUbglAmmoTpl, 20, inventory); this.addAmmoToSecureContainer(5, generatedWeaponResult.chosenUbglAmmoTpl, 20, inventory);
@ -536,7 +538,7 @@ export class BotWeaponGenerator
botRole: string, botRole: string,
): string ): string
{ {
const magazine = weaponMods.find(m => m.slotId === this.modMagazineSlotId); const magazine = weaponMods.find((m) => m.slotId === this.modMagazineSlotId);
if (!magazine) if (!magazine)
{ {
// Edge case - magazineless chamber loaded weapons dont have magazines, e.g. mp18 // Edge case - magazineless chamber loaded weapons dont have magazines, e.g. mp18
@ -736,8 +738,8 @@ export class BotWeaponGenerator
magazineTemplate: ITemplateItem, magazineTemplate: ITemplateItem,
): void ): void
{ {
const magazineCartridgeChildItem = weaponWithMods.find(m => const magazineCartridgeChildItem = weaponWithMods.find(
m.parentId === magazine._id && m.slotId === "cartridges", (m) => m.parentId === magazine._id && m.slotId === "cartridges",
); );
if (magazineCartridgeChildItem) if (magazineCartridgeChildItem)
{ {
@ -766,7 +768,7 @@ export class BotWeaponGenerator
// for CylinderMagazine we exchange the ammo in the "camoras". // for CylinderMagazine we exchange the ammo in the "camoras".
// This might not be necessary since we already filled the camoras with a random whitelisted and compatible ammo type, // This might not be necessary since we already filled the camoras with a random whitelisted and compatible ammo type,
// but I'm not sure whether this is also used elsewhere // but I'm not sure whether this is also used elsewhere
const camoras = weaponMods.filter(x => x.parentId === magazineId && x.slotId.startsWith("camora")); const camoras = weaponMods.filter((x) => x.parentId === magazineId && x.slotId.startsWith("camora"));
for (const camora of camoras) for (const camora of camoras)
{ {
camora._tpl = ammoTpl; camora._tpl = ammoTpl;

View File

@ -47,7 +47,7 @@ export class FenceBaseAssortGenerator
const blockedSeasonalItems = this.seasonalEventService.getInactiveSeasonalEventItems(); const blockedSeasonalItems = this.seasonalEventService.getInactiveSeasonalEventItems();
const baseFenceAssort = this.databaseServer.getTables().traders[Traders.FENCE].assort; const baseFenceAssort = this.databaseServer.getTables().traders[Traders.FENCE].assort;
for (const rootItemDb of this.itemHelper.getItems().filter(item => this.isValidFenceItem(item))) for (const rootItemDb of this.itemHelper.getItems().filter((item) => this.isValidFenceItem(item)))
{ {
// Skip blacklisted items // Skip blacklisted items
if (this.itemFilterService.isItemBlacklisted(rootItemDb._id)) if (this.itemFilterService.isItemBlacklisted(rootItemDb._id))
@ -86,13 +86,15 @@ export class FenceBaseAssortGenerator
} }
// Create item object in array // Create item object in array
const itemWithChildrenToAdd: Item[] = [{ const itemWithChildrenToAdd: Item[] = [
_id: this.hashUtil.generate(), {
_tpl: rootItemDb._id, _id: this.hashUtil.generate(),
parentId: "hideout", _tpl: rootItemDb._id,
slotId: "hideout", parentId: "hideout",
upd: { StackObjectsCount: 9999999 }, slotId: "hideout",
}]; upd: { StackObjectsCount: 9999999 },
},
];
// Ensure ammo is not above penetration limit value // Ensure ammo is not above penetration limit value
if (this.itemHelper.isOfBaseclasses(rootItemDb._id, [BaseClasses.AMMO_BOX, BaseClasses.AMMO])) if (this.itemHelper.isOfBaseclasses(rootItemDb._id, [BaseClasses.AMMO_BOX, BaseClasses.AMMO]))
@ -141,7 +143,7 @@ export class FenceBaseAssortGenerator
for (const defaultPreset of defaultPresets) for (const defaultPreset of defaultPresets)
{ {
// Skip presets we've already added // Skip presets we've already added
if (baseFenceAssort.items.some(item => item.upd && item.upd.sptPresetId === defaultPreset._id)) if (baseFenceAssort.items.some((item) => item.upd && item.upd.sptPresetId === defaultPreset._id))
{ {
continue; continue;
} }
@ -249,7 +251,7 @@ export class FenceBaseAssortGenerator
} }
// Check for and add required soft inserts to armors // Check for and add required soft inserts to armors
const requiredSlots = itemDbDetails._props.Slots.filter(slot => slot._required); const requiredSlots = itemDbDetails._props.Slots.filter((slot) => slot._required);
const hasRequiredSlots = requiredSlots.length > 0; const hasRequiredSlots = requiredSlots.length > 0;
if (hasRequiredSlots) if (hasRequiredSlots)
{ {
@ -281,7 +283,7 @@ export class FenceBaseAssortGenerator
} }
// Check for and add plate items // Check for and add plate items
const plateSlots = itemDbDetails._props.Slots.filter(slot => const plateSlots = itemDbDetails._props.Slots.filter((slot) =>
this.itemHelper.isRemovablePlateSlot(slot._name), this.itemHelper.isRemovablePlateSlot(slot._name),
); );
if (plateSlots.length > 0) if (plateSlots.length > 0)

View File

@ -92,7 +92,7 @@ export class LocationGenerator
} }
// Add mounted weapons to output loot // Add mounted weapons to output loot
result.push(...staticWeaponsOnMapClone ?? []); result.push(...(staticWeaponsOnMapClone ?? []));
const allStaticContainersOnMapClone = this.cloner.clone(mapData.staticContainers.staticContainers); const allStaticContainersOnMapClone = this.cloner.clone(mapData.staticContainers.staticContainers);
@ -137,8 +137,10 @@ export class LocationGenerator
// Randomisation is turned off globally or just turned off for this map // Randomisation is turned off globally or just turned off for this map
if ( if (
!(this.locationConfig.containerRandomisationSettings.enabled !(
&& this.locationConfig.containerRandomisationSettings.maps[locationId]) this.locationConfig.containerRandomisationSettings.enabled
&& this.locationConfig.containerRandomisationSettings.maps[locationId]
)
) )
{ {
this.logger.debug( this.logger.debug(
@ -221,15 +223,13 @@ export class LocationGenerator
for (const chosenContainerId of chosenContainerIds) for (const chosenContainerId of chosenContainerIds)
{ {
// Look up container object from full list of containers on map // Look up container object from full list of containers on map
const containerObject = staticRandomisableContainersOnMap.find(staticContainer => const containerObject = staticRandomisableContainersOnMap.find(
staticContainer.template.Id === chosenContainerId, (staticContainer) => staticContainer.template.Id === chosenContainerId,
); );
if (!containerObject) if (!containerObject)
{ {
this.logger.debug( this.logger.debug(
`Container: ${ `Container: ${chosenContainerIds[chosenContainerId]} not found in staticRandomisableContainersOnMap, this is bad`,
chosenContainerIds[chosenContainerId]
} not found in staticRandomisableContainersOnMap, this is bad`,
); );
continue; continue;
} }
@ -265,11 +265,13 @@ export class LocationGenerator
*/ */
protected getRandomisableContainersOnMap(staticContainers: IStaticContainerData[]): IStaticContainerData[] protected getRandomisableContainersOnMap(staticContainers: IStaticContainerData[]): IStaticContainerData[]
{ {
return staticContainers.filter(staticContainer => return staticContainers.filter(
staticContainer.probability !== 1 && !staticContainer.template.IsAlwaysSpawn (staticContainer) =>
&& !this.locationConfig.containerRandomisationSettings.containerTypesToNotRandomise.includes( staticContainer.probability !== 1
staticContainer.template.Items[0]._tpl, && !staticContainer.template.IsAlwaysSpawn
), && !this.locationConfig.containerRandomisationSettings.containerTypesToNotRandomise.includes(
staticContainer.template.Items[0]._tpl,
),
); );
} }
@ -280,11 +282,13 @@ export class LocationGenerator
*/ */
protected getGuaranteedContainers(staticContainersOnMap: IStaticContainerData[]): IStaticContainerData[] protected getGuaranteedContainers(staticContainersOnMap: IStaticContainerData[]): IStaticContainerData[]
{ {
return staticContainersOnMap.filter(staticContainer => return staticContainersOnMap.filter(
staticContainer.probability === 1 || staticContainer.template.IsAlwaysSpawn (staticContainer) =>
|| this.locationConfig.containerRandomisationSettings.containerTypesToNotRandomise.includes( staticContainer.probability === 1
staticContainer.template.Items[0]._tpl, || staticContainer.template.IsAlwaysSpawn
), || this.locationConfig.containerRandomisationSettings.containerTypesToNotRandomise.includes(
staticContainer.template.Items[0]._tpl,
),
); );
} }
@ -415,9 +419,9 @@ export class LocationGenerator
const containerLootPool = this.getPossibleLootItemsForContainer(containerTpl, staticLootDist); const containerLootPool = this.getPossibleLootItemsForContainer(containerTpl, staticLootDist);
// Some containers need to have items forced into it (quest keys etc) // Some containers need to have items forced into it (quest keys etc)
const tplsForced = staticForced.filter(forcedStaticProp => const tplsForced = staticForced
forcedStaticProp.containerId === containerClone.template.Id, .filter((forcedStaticProp) => forcedStaticProp.containerId === containerClone.template.Id)
).map(x => x.itemTpl); .map((x) => x.itemTpl);
// Draw random loot // Draw random loot
// Money spawn more than once in container // Money spawn more than once in container
@ -426,11 +430,9 @@ export class LocationGenerator
// Choose items to add to container, factor in weighting + lock money down // Choose items to add to container, factor in weighting + lock money down
// Filter out items picked that're already in the above `tplsForced` array // Filter out items picked that're already in the above `tplsForced` array
const chosenTpls = containerLootPool.draw( const chosenTpls = containerLootPool
itemCountToAdd, .draw(itemCountToAdd, this.locationConfig.allowDuplicateItemsInStaticContainers, locklist)
this.locationConfig.allowDuplicateItemsInStaticContainers, .filter((tpl) => !tplsForced.includes(tpl));
locklist,
).filter(tpl => !tplsForced.includes(tpl));
// Add forced loot to chosen item pool // Add forced loot to chosen item pool
const tplsToAddToContainer = tplsForced.concat(chosenTpls); const tplsToAddToContainer = tplsForced.concat(chosenTpls);
@ -495,7 +497,9 @@ export class LocationGenerator
const width = containerTemplate._props.Grids[0]._props.cellsH; const width = containerTemplate._props.Grids[0]._props.cellsH;
// Calcualte 2d array and return // Calcualte 2d array and return
return Array(height).fill(0).map(() => Array(width).fill(0)); return Array(height)
.fill(0)
.map(() => Array(width).fill(0));
} }
/** /**
@ -600,7 +604,7 @@ export class LocationGenerator
// Build the list of forced loot from both `spawnpointsForced` and any point marked `IsAlwaysSpawn` // Build the list of forced loot from both `spawnpointsForced` and any point marked `IsAlwaysSpawn`
dynamicForcedSpawnPoints.push(...dynamicLootDist.spawnpointsForced); dynamicForcedSpawnPoints.push(...dynamicLootDist.spawnpointsForced);
dynamicForcedSpawnPoints.push(...dynamicLootDist.spawnpoints.filter(point => point.template.IsAlwaysSpawn)); dynamicForcedSpawnPoints.push(...dynamicLootDist.spawnpoints.filter((point) => point.template.IsAlwaysSpawn));
// Add forced loot // Add forced loot
this.addForcedLoot(loot, dynamicForcedSpawnPoints, locationName); this.addForcedLoot(loot, dynamicForcedSpawnPoints, locationName);
@ -663,7 +667,7 @@ export class LocationGenerator
// Filter out duplicate locationIds // Filter out duplicate locationIds
chosenSpawnpoints = [ chosenSpawnpoints = [
...new Map(chosenSpawnpoints.map(spawnPoint => [spawnPoint.locationId, spawnPoint])).values(), ...new Map(chosenSpawnpoints.map((spawnPoint) => [spawnPoint.locationId, spawnPoint])).values(),
]; ];
// Do we have enough items in pool to fulfill requirement // Do we have enough items in pool to fulfill requirement
@ -706,8 +710,9 @@ export class LocationGenerator
for (const itemDist of spawnPoint.itemDistribution) for (const itemDist of spawnPoint.itemDistribution)
{ {
if ( if (
!seasonalEventActive && seasonalItemTplBlacklist.includes( !seasonalEventActive
spawnPoint.template.Items.find(item => item._id === itemDist.composedKey.key)._tpl, && seasonalItemTplBlacklist.includes(
spawnPoint.template.Items.find((item) => item._id === itemDist.composedKey.key)._tpl,
) )
) )
{ {
@ -758,8 +763,8 @@ export class LocationGenerator
for (const itemTpl of lootToForceSingleAmountOnMap) for (const itemTpl of lootToForceSingleAmountOnMap)
{ {
// Get all spawn positions for item tpl in forced loot array // Get all spawn positions for item tpl in forced loot array
const items = forcedSpawnPoints.filter(forcedSpawnPoint => const items = forcedSpawnPoints.filter(
forcedSpawnPoint.template.Items[0]._tpl === itemTpl, (forcedSpawnPoint) => forcedSpawnPoint.template.Items[0]._tpl === itemTpl,
); );
if (!items || items.length === 0) if (!items || items.length === 0)
{ {
@ -783,7 +788,7 @@ export class LocationGenerator
// Choose 1 out of all found spawn positions for spawn id and add to loot array // Choose 1 out of all found spawn positions for spawn id and add to loot array
for (const spawnPointLocationId of spawnpointArray.draw(1, false)) for (const spawnPointLocationId of spawnpointArray.draw(1, false))
{ {
const itemToAdd = items.find(item => item.locationId === spawnPointLocationId); const itemToAdd = items.find((item) => item.locationId === spawnPointLocationId);
const lootItem = itemToAdd.template; const lootItem = itemToAdd.template;
lootItem.Root = this.objectId.generate(); lootItem.Root = this.objectId.generate();
lootItem.Items[0]._id = lootItem.Root; lootItem.Items[0]._id = lootItem.Root;
@ -819,8 +824,8 @@ export class LocationGenerator
locationTemplateToAdd.Items[0]._id = locationTemplateToAdd.Root; locationTemplateToAdd.Items[0]._id = locationTemplateToAdd.Root;
// Push forced location into array as long as it doesnt exist already // Push forced location into array as long as it doesnt exist already
const existingLocation = lootLocationTemplates.find(spawnPoint => const existingLocation = lootLocationTemplates.find(
spawnPoint.Id === locationTemplateToAdd.Id, (spawnPoint) => spawnPoint.Id === locationTemplateToAdd.Id,
); );
if (!existingLocation) if (!existingLocation)
{ {
@ -848,7 +853,7 @@ export class LocationGenerator
staticAmmoDist: Record<string, IStaticAmmoDetails[]>, staticAmmoDist: Record<string, IStaticAmmoDetails[]>,
): IContainerItem ): IContainerItem
{ {
const chosenItem = spawnPoint.template.Items.find(item => item._id === chosenComposedKey); const chosenItem = spawnPoint.template.Items.find((item) => item._id === chosenComposedKey);
const chosenTpl = chosenItem._tpl; const chosenTpl = chosenItem._tpl;
const itemTemplate = this.itemHelper.getItem(chosenTpl)[1]; const itemTemplate = this.itemHelper.getItem(chosenTpl)[1];
@ -858,9 +863,10 @@ export class LocationGenerator
// Money/Ammo - don't rely on items in spawnPoint.template.Items so we can randomise it ourselves // Money/Ammo - don't rely on items in spawnPoint.template.Items so we can randomise it ourselves
if (this.itemHelper.isOfBaseclasses(chosenTpl, [BaseClasses.MONEY, BaseClasses.AMMO])) if (this.itemHelper.isOfBaseclasses(chosenTpl, [BaseClasses.MONEY, BaseClasses.AMMO]))
{ {
const stackCount = itemTemplate._props.StackMaxSize === 1 const stackCount
? 1 = itemTemplate._props.StackMaxSize === 1
: this.randomUtil.getInt(itemTemplate._props.StackMinRandom, itemTemplate._props.StackMaxRandom); ? 1
: this.randomUtil.getInt(itemTemplate._props.StackMinRandom, itemTemplate._props.StackMaxRandom);
itemWithMods.push({ itemWithMods.push({
_id: this.objectId.generate(), _id: this.objectId.generate(),
@ -948,10 +954,10 @@ export class LocationGenerator
{ {
if (this.itemHelper.isOfBaseclass(chosenTpl, BaseClasses.WEAPON)) if (this.itemHelper.isOfBaseclass(chosenTpl, BaseClasses.WEAPON))
{ {
return items.find(v => v._tpl === chosenTpl && v.parentId === undefined); return items.find((v) => v._tpl === chosenTpl && v.parentId === undefined);
} }
return items.find(item => item._tpl === chosenTpl); return items.find((item) => item._tpl === chosenTpl);
} }
// TODO: rewrite, BIG yikes // TODO: rewrite, BIG yikes
@ -979,9 +985,10 @@ export class LocationGenerator
) )
{ {
// Edge case - some ammos e.g. flares or M406 grenades shouldn't be stacked // Edge case - some ammos e.g. flares or M406 grenades shouldn't be stacked
const stackCount = itemTemplate._props.StackMaxSize === 1 const stackCount
? 1 = itemTemplate._props.StackMaxSize === 1
: this.randomUtil.getInt(itemTemplate._props.StackMinRandom, itemTemplate._props.StackMaxRandom); ? 1
: this.randomUtil.getInt(itemTemplate._props.StackMinRandom, itemTemplate._props.StackMaxRandom);
rootItem.upd = { StackObjectsCount: stackCount }; rootItem.upd = { StackObjectsCount: stackCount };
} }
@ -1055,7 +1062,7 @@ export class LocationGenerator
// it can handle revolver ammo (it's not restructured to be used here yet.) // it can handle revolver ammo (it's not restructured to be used here yet.)
// General: Make a WeaponController for Ragfair preset stuff and the generating weapons and ammo stuff from // General: Make a WeaponController for Ragfair preset stuff and the generating weapons and ammo stuff from
// BotGenerator // BotGenerator
const magazine = items.filter(item => item.slotId === "mod_magazine")[0]; const magazine = items.filter((item) => item.slotId === "mod_magazine")[0];
// some weapon presets come without magazine; only fill the mag if it exists // some weapon presets come without magazine; only fill the mag if it exists
if (magazine) if (magazine)
{ {

View File

@ -6,7 +6,6 @@ import { WeightedRandomHelper } from "@spt-aki/helpers/WeightedRandomHelper";
import { IPreset } from "@spt-aki/models/eft/common/IGlobals"; import { IPreset } from "@spt-aki/models/eft/common/IGlobals";
import { Item } from "@spt-aki/models/eft/common/tables/IItem"; import { Item } from "@spt-aki/models/eft/common/tables/IItem";
import { ITemplateItem } from "@spt-aki/models/eft/common/tables/ITemplateItem"; import { ITemplateItem } from "@spt-aki/models/eft/common/tables/ITemplateItem";
import { AddItem } from "@spt-aki/models/eft/inventory/IAddItemRequestData";
import { BaseClasses } from "@spt-aki/models/enums/BaseClasses"; import { BaseClasses } from "@spt-aki/models/enums/BaseClasses";
import { ISealedAirdropContainerSettings, RewardDetails } from "@spt-aki/models/spt/config/IInventoryConfig"; import { ISealedAirdropContainerSettings, RewardDetails } from "@spt-aki/models/spt/config/IInventoryConfig";
import { LootItem } from "@spt-aki/models/spt/services/LootItem"; import { LootItem } from "@spt-aki/models/spt/services/LootItem";
@ -71,7 +70,7 @@ export class LootGenerator
if (desiredWeaponCrateCount > 0) if (desiredWeaponCrateCount > 0)
{ {
// Get list of all sealed containers from db // Get list of all sealed containers from db
const sealedWeaponContainerPool = Object.values(tables.templates.items).filter(x => const sealedWeaponContainerPool = Object.values(tables.templates.items).filter((x) =>
x._name.includes("event_container_airdrop"), x._name.includes("event_container_airdrop"),
); );
@ -89,11 +88,12 @@ export class LootGenerator
} }
// Get items from items.json that have a type of item + not in global blacklist + basetype is in whitelist // Get items from items.json that have a type of item + not in global blacklist + basetype is in whitelist
const items = Object.entries(tables.templates.items).filter(x => const items = Object.entries(tables.templates.items).filter(
!itemBlacklist.has(x[1]._id) (x) =>
&& x[1]._type.toLowerCase() === "item" !itemBlacklist.has(x[1]._id)
&& !x[1]._props.QuestItem && x[1]._type.toLowerCase() === "item"
&& options.itemTypeWhitelist.includes(x[1]._parent), && !x[1]._props.QuestItem
&& options.itemTypeWhitelist.includes(x[1]._parent),
); );
if (items.length > 0) if (items.length > 0)
@ -119,7 +119,7 @@ export class LootGenerator
); );
if (randomisedWeaponPresetCount > 0) if (randomisedWeaponPresetCount > 0)
{ {
const weaponDefaultPresets = globalDefaultPresets.filter(preset => const weaponDefaultPresets = globalDefaultPresets.filter((preset) =>
this.itemHelper.isOfBaseclass(preset._encyclopedia, BaseClasses.WEAPON), this.itemHelper.isOfBaseclass(preset._encyclopedia, BaseClasses.WEAPON),
); );
@ -150,10 +150,10 @@ export class LootGenerator
); );
if (randomisedArmorPresetCount > 0) if (randomisedArmorPresetCount > 0)
{ {
const armorDefaultPresets = globalDefaultPresets.filter(preset => const armorDefaultPresets = globalDefaultPresets.filter((preset) =>
this.itemHelper.armorItemCanHoldMods(preset._encyclopedia), this.itemHelper.armorItemCanHoldMods(preset._encyclopedia),
); );
const levelFilteredArmorPresets = armorDefaultPresets.filter(armor => const levelFilteredArmorPresets = armorDefaultPresets.filter((armor) =>
this.armorIsDesiredProtectionLevel(armor, options), this.armorIsDesiredProtectionLevel(armor, options),
); );
@ -189,21 +189,21 @@ export class LootGenerator
*/ */
protected armorIsDesiredProtectionLevel(armor: IPreset, options: LootRequest): boolean protected armorIsDesiredProtectionLevel(armor: IPreset, options: LootRequest): boolean
{ {
const frontPlate = armor._items.find(mod => mod?.slotId?.toLowerCase() === "front_plate"); const frontPlate = armor._items.find((mod) => mod?.slotId?.toLowerCase() === "front_plate");
if (frontPlate) if (frontPlate)
{ {
const plateDb = this.itemHelper.getItem(frontPlate._tpl); const plateDb = this.itemHelper.getItem(frontPlate._tpl);
return options.armorLevelWhitelist.includes(Number.parseInt(plateDb[1]._props.armorClass as any)); return options.armorLevelWhitelist.includes(Number.parseInt(plateDb[1]._props.armorClass as any));
} }
const helmetTop = armor._items.find(mod => mod?.slotId?.toLowerCase() === "helmet_top"); const helmetTop = armor._items.find((mod) => mod?.slotId?.toLowerCase() === "helmet_top");
if (helmetTop) if (helmetTop)
{ {
const plateDb = this.itemHelper.getItem(helmetTop._tpl); const plateDb = this.itemHelper.getItem(helmetTop._tpl);
return options.armorLevelWhitelist.includes(Number.parseInt(plateDb[1]._props.armorClass as any)); return options.armorLevelWhitelist.includes(Number.parseInt(plateDb[1]._props.armorClass as any));
} }
const softArmorFront = armor._items.find(mod => mod?.slotId?.toLowerCase() === "soft_armor_front"); const softArmorFront = armor._items.find((mod) => mod?.slotId?.toLowerCase() === "soft_armor_front");
if (softArmorFront) if (softArmorFront)
{ {
const plateDb = this.itemHelper.getItem(softArmorFront._tpl); const plateDb = this.itemHelper.getItem(softArmorFront._tpl);
@ -470,7 +470,7 @@ export class LootGenerator
// Need to find boxes that matches weapons caliber // Need to find boxes that matches weapons caliber
const weaponCaliber = weaponDetailsDb._props.ammoCaliber; const weaponCaliber = weaponDetailsDb._props.ammoCaliber;
const ammoBoxesMatchingCaliber = ammoBoxesDetails.filter(x => x._props.ammoCaliber === weaponCaliber); const ammoBoxesMatchingCaliber = ammoBoxesDetails.filter((x) => x._props.ammoCaliber === weaponCaliber);
if (ammoBoxesMatchingCaliber.length === 0) if (ammoBoxesMatchingCaliber.length === 0)
{ {
this.logger.debug(`No ammo box with caliber ${weaponCaliber} found, skipping`); this.logger.debug(`No ammo box with caliber ${weaponCaliber} found, skipping`);
@ -490,12 +490,13 @@ export class LootGenerator
} }
// Get all items of the desired type + not quest items + not globally blacklisted // Get all items of the desired type + not quest items + not globally blacklisted
const rewardItemPool = Object.values(this.databaseServer.getTables().templates.items).filter(x => const rewardItemPool = Object.values(this.databaseServer.getTables().templates.items).filter(
x._parent === rewardTypeId (x) =>
&& x._type.toLowerCase() === "item" x._parent === rewardTypeId
&& !this.itemFilterService.isItemBlacklisted(x._id) && x._type.toLowerCase() === "item"
&& !(containerSettings.allowBossItems || this.itemFilterService.isBossItem(x._id)) && !this.itemFilterService.isItemBlacklisted(x._id)
&& !x._props.QuestItem, && !(containerSettings.allowBossItems || this.itemFilterService.isBossItem(x._id))
&& !x._props.QuestItem,
); );
if (rewardItemPool.length === 0) if (rewardItemPool.length === 0)
@ -544,8 +545,8 @@ export class LootGenerator
} }
// Get items that fulfil reward type criteria from items that fit on gun // Get items that fulfil reward type criteria from items that fit on gun
const relatedItems = linkedItemsToWeapon.filter(x => const relatedItems = linkedItemsToWeapon.filter(
x._parent === rewardTypeId && !this.itemFilterService.isItemBlacklisted(x._id), (x) => x._parent === rewardTypeId && !this.itemFilterService.isItemBlacklisted(x._id),
); );
if (!relatedItems || relatedItems.length === 0) if (!relatedItems || relatedItems.length === 0)
{ {

View File

@ -58,13 +58,14 @@ export class PMCLootGenerator
// Blacklist inactive seasonal items // Blacklist inactive seasonal items
itemBlacklist.push(...this.seasonalEventService.getInactiveSeasonalEventItems()); itemBlacklist.push(...this.seasonalEventService.getInactiveSeasonalEventItems());
const itemsToAdd = Object.values(items).filter(item => const itemsToAdd = Object.values(items).filter(
allowedItemTypes.includes(item._parent) (item) =>
&& this.itemHelper.isValidItem(item._id) allowedItemTypes.includes(item._parent)
&& !pmcItemBlacklist.includes(item._id) && this.itemHelper.isValidItem(item._id)
&& !itemBlacklist.includes(item._id) && !pmcItemBlacklist.includes(item._id)
&& item._props.Width === 1 && !itemBlacklist.includes(item._id)
&& item._props.Height === 1, && item._props.Width === 1
&& item._props.Height === 1,
); );
for (const itemToAdd of itemsToAdd) for (const itemToAdd of itemsToAdd)
@ -87,7 +88,7 @@ export class PMCLootGenerator
{ {
// Invert price so cheapest has a larger weight // Invert price so cheapest has a larger weight
// Times by highest price so most expensive item has weight of 1 // Times by highest price so most expensive item has weight of 1
this.pocketLootPool[key] = Math.round(1 / this.pocketLootPool[key] * highestPrice); this.pocketLootPool[key] = Math.round((1 / this.pocketLootPool[key]) * highestPrice);
} }
this.weightedRandomHelper.reduceWeightValues(this.pocketLootPool); this.weightedRandomHelper.reduceWeightValues(this.pocketLootPool);
@ -117,12 +118,13 @@ export class PMCLootGenerator
// Blacklist seasonal items // Blacklist seasonal items
itemBlacklist.push(...this.seasonalEventService.getInactiveSeasonalEventItems()); itemBlacklist.push(...this.seasonalEventService.getInactiveSeasonalEventItems());
const itemsToAdd = Object.values(items).filter(item => const itemsToAdd = Object.values(items).filter(
allowedItemTypes.includes(item._parent) (item) =>
&& this.itemHelper.isValidItem(item._id) allowedItemTypes.includes(item._parent)
&& !pmcItemBlacklist.includes(item._id) && this.itemHelper.isValidItem(item._id)
&& !itemBlacklist.includes(item._id) && !pmcItemBlacklist.includes(item._id)
&& this.itemFitsInto2By2Slot(item), && !itemBlacklist.includes(item._id)
&& this.itemFitsInto2By2Slot(item),
); );
for (const itemToAdd of itemsToAdd) for (const itemToAdd of itemsToAdd)
@ -145,7 +147,7 @@ export class PMCLootGenerator
{ {
// Invert price so cheapest has a larger weight // Invert price so cheapest has a larger weight
// Times by highest price so most expensive item has weight of 1 // Times by highest price so most expensive item has weight of 1
this.vestLootPool[key] = Math.round(1 / this.vestLootPool[key] * highestPrice); this.vestLootPool[key] = Math.round((1 / this.vestLootPool[key]) * highestPrice);
} }
this.weightedRandomHelper.reduceWeightValues(this.vestLootPool); this.weightedRandomHelper.reduceWeightValues(this.vestLootPool);
@ -186,11 +188,12 @@ export class PMCLootGenerator
// Blacklist seasonal items // Blacklist seasonal items
itemBlacklist.push(...this.seasonalEventService.getInactiveSeasonalEventItems()); itemBlacklist.push(...this.seasonalEventService.getInactiveSeasonalEventItems());
const itemsToAdd = Object.values(items).filter(item => const itemsToAdd = Object.values(items).filter(
allowedItemTypes.includes(item._parent) (item) =>
&& this.itemHelper.isValidItem(item._id) allowedItemTypes.includes(item._parent)
&& !pmcItemBlacklist.includes(item._id) && this.itemHelper.isValidItem(item._id)
&& !itemBlacklist.includes(item._id), && !pmcItemBlacklist.includes(item._id)
&& !itemBlacklist.includes(item._id),
); );
for (const itemToAdd of itemsToAdd) for (const itemToAdd of itemsToAdd)
@ -213,7 +216,7 @@ export class PMCLootGenerator
{ {
// Invert price so cheapest has a larger weight // Invert price so cheapest has a larger weight
// Times by highest price so most expensive item has weight of 1 // Times by highest price so most expensive item has weight of 1
this.backpackLootPool[key] = Math.round(1 / this.backpackLootPool[key] * highestPrice); this.backpackLootPool[key] = Math.round((1 / this.backpackLootPool[key]) * highestPrice);
} }
this.weightedRandomHelper.reduceWeightValues(this.backpackLootPool); this.weightedRandomHelper.reduceWeightValues(this.backpackLootPool);

View File

@ -162,11 +162,13 @@ export class PlayerScavGenerator
} }
const itemTemplate = itemResult[1]; const itemTemplate = itemResult[1];
const itemsToAdd: Item[] = [{ const itemsToAdd: Item[] = [
_id: this.hashUtil.generate(), {
_tpl: itemTemplate._id, _id: this.hashUtil.generate(),
...this.botGeneratorHelper.generateExtraPropertiesForItem(itemTemplate), _tpl: itemTemplate._id,
}]; ...this.botGeneratorHelper.generateExtraPropertiesForItem(itemTemplate),
},
];
const result = this.botGeneratorHelper.addItemWithChildrenToEquipmentSlot( const result = this.botGeneratorHelper.addItemWithChildrenToEquipmentSlot(
containersToAddTo, containersToAddTo,

View File

@ -72,7 +72,7 @@ export class RagfairAssortGenerator
const results: Item[][] = []; const results: Item[][] = [];
/** Get cloned items from db */ /** Get cloned items from db */
const dbItemsClone = this.itemHelper.getItems().filter(item => item._type !== "Node"); const dbItemsClone = this.itemHelper.getItems().filter((item) => item._type !== "Node");
/** Store processed preset tpls so we dont add them when procesing non-preset items */ /** Store processed preset tpls so we dont add them when procesing non-preset items */
const processedArmorItems: string[] = []; const processedArmorItems: string[] = [];
@ -105,7 +105,8 @@ export class RagfairAssortGenerator
// Skip seasonal items when not in-season // Skip seasonal items when not in-season
if ( if (
this.ragfairConfig.dynamic.removeSeasonalItemsWhenNotInEvent && !seasonalEventActive this.ragfairConfig.dynamic.removeSeasonalItemsWhenNotInEvent
&& !seasonalEventActive
&& seasonalItemTplBlacklist.includes(item._id) && seasonalItemTplBlacklist.includes(item._id)
) )
{ {

View File

@ -135,7 +135,7 @@ export class RagfairOfferGenerator
} }
} }
const itemCount = items.filter(x => x.slotId === "hideout").length; const itemCount = items.filter((x) => x.slotId === "hideout").length;
const roublePrice = Math.round(this.convertOfferRequirementsIntoRoubles(offerRequirements)); const roublePrice = Math.round(this.convertOfferRequirementsIntoRoubles(offerRequirements));
const offer: IRagfairOffer = { const offer: IRagfairOffer = {
@ -143,9 +143,8 @@ export class RagfairOfferGenerator
intId: this.offerCounter, intId: this.offerCounter,
user: { user: {
id: this.getTraderId(userID), id: this.getTraderId(userID),
memberType: userID === "ragfair" memberType:
? MemberCategory.DEFAULT userID === "ragfair" ? MemberCategory.DEFAULT : this.ragfairServerHelper.getMemberType(userID),
: this.ragfairServerHelper.getMemberType(userID),
nickname: this.ragfairServerHelper.getNickname(userID), nickname: this.ragfairServerHelper.getNickname(userID),
rating: this.getRating(userID), rating: this.getRating(userID),
isRatingGrowing: this.getRatingGrowing(userID), isRatingGrowing: this.getRatingGrowing(userID),
@ -410,7 +409,7 @@ export class RagfairOfferGenerator
return false; return false;
} }
const plateSlots = presetWithChildren.filter(item => const plateSlots = presetWithChildren.filter((item) =>
this.itemHelper.getRemovablePlateSlotIds().includes(item.slotId?.toLowerCase()), this.itemHelper.getRemovablePlateSlotIds().includes(item.slotId?.toLowerCase()),
); );
if (plateSlots.length === 0) if (plateSlots.length === 0)
@ -459,13 +458,14 @@ export class RagfairOfferGenerator
); );
const isBarterOffer = this.randomUtil.getChance100(this.ragfairConfig.dynamic.barter.chancePercent); const isBarterOffer = this.randomUtil.getChance100(this.ragfairConfig.dynamic.barter.chancePercent);
const isPackOffer = this.randomUtil.getChance100(this.ragfairConfig.dynamic.pack.chancePercent) const isPackOffer
&& !isBarterOffer = this.randomUtil.getChance100(this.ragfairConfig.dynamic.pack.chancePercent)
&& itemWithChildren.length === 1 && !isBarterOffer
&& this.itemHelper.isOfBaseclasses( && itemWithChildren.length === 1
itemWithChildren[0]._tpl, && this.itemHelper.isOfBaseclasses(
this.ragfairConfig.dynamic.pack.itemTypeWhitelist, itemWithChildren[0]._tpl,
); this.ragfairConfig.dynamic.pack.itemTypeWhitelist,
);
const randomUserId = this.hashUtil.generate(); const randomUserId = this.hashUtil.generate();
@ -477,7 +477,7 @@ export class RagfairOfferGenerator
const shouldRemovePlates = this.randomUtil.getChance100(armorConfig.removeRemovablePlateChance); const shouldRemovePlates = this.randomUtil.getChance100(armorConfig.removeRemovablePlateChance);
if (shouldRemovePlates && this.itemHelper.armorItemHasRemovablePlateSlots(itemWithChildren[0]._tpl)) if (shouldRemovePlates && this.itemHelper.armorItemHasRemovablePlateSlots(itemWithChildren[0]._tpl))
{ {
const offerItemPlatesToRemove = itemWithChildren.filter(item => const offerItemPlatesToRemove = itemWithChildren.filter((item) =>
armorConfig.plateSlotIdToRemovePool.includes(item.slotId?.toLowerCase()), armorConfig.plateSlotIdToRemovePool.includes(item.slotId?.toLowerCase()),
); );
@ -683,8 +683,8 @@ export class RagfairOfferGenerator
this.randomiseArmorDurabilityValues(itemWithMods, currentMultiplier, maxMultiplier); this.randomiseArmorDurabilityValues(itemWithMods, currentMultiplier, maxMultiplier);
// Add hits to visor // Add hits to visor
const visorMod = itemWithMods.find(item => const visorMod = itemWithMods.find(
item.parentId === BaseClasses.ARMORED_EQUIPMENT && item.slotId === "mod_equipment_000", (item) => item.parentId === BaseClasses.ARMORED_EQUIPMENT && item.slotId === "mod_equipment_000",
); );
if (this.randomUtil.getChance100(25) && visorMod) if (this.randomUtil.getChance100(25) && visorMod)
{ {
@ -715,8 +715,8 @@ export class RagfairOfferGenerator
if (rootItem.upd.Key && itemDetails._props.MaximumNumberOfUsage > 1) if (rootItem.upd.Key && itemDetails._props.MaximumNumberOfUsage > 1)
{ {
// randomize key uses // randomize key uses
rootItem.upd.Key.NumberOfUsages = Math.round(itemDetails._props.MaximumNumberOfUsage * (1 - maxMultiplier)) rootItem.upd.Key.NumberOfUsages
|| 0; = Math.round(itemDetails._props.MaximumNumberOfUsage * (1 - maxMultiplier)) || 0;
return; return;
} }
@ -792,8 +792,8 @@ export class RagfairOfferGenerator
{ {
this.itemHelper.addUpdObjectToItem(armorItem); this.itemHelper.addUpdObjectToItem(armorItem);
const lowestMaxDurability = this.randomUtil.getFloat(maxMultiplier, 1) const lowestMaxDurability
* itemDbDetails._props.MaxDurability; = this.randomUtil.getFloat(maxMultiplier, 1) * itemDbDetails._props.MaxDurability;
const chosenMaxDurability = Math.round( const chosenMaxDurability = Math.round(
this.randomUtil.getFloat(lowestMaxDurability, itemDbDetails._props.MaxDurability), this.randomUtil.getFloat(lowestMaxDurability, itemDbDetails._props.MaxDurability),
); );
@ -882,14 +882,16 @@ export class RagfairOfferGenerator
const desiredItemCost = Math.round(priceOfItemOffer / barterItemCount); const desiredItemCost = Math.round(priceOfItemOffer / barterItemCount);
// amount to go above/below when looking for an item (Wiggle cost of item a little) // amount to go above/below when looking for an item (Wiggle cost of item a little)
const offerCostVariance = desiredItemCost * this.ragfairConfig.dynamic.barter.priceRangeVariancePercent / 100; const offerCostVariance = (desiredItemCost * this.ragfairConfig.dynamic.barter.priceRangeVariancePercent) / 100;
const fleaPrices = this.getFleaPricesAsArray(); const fleaPrices = this.getFleaPricesAsArray();
// Filter possible barters to items that match the price range + not itself // Filter possible barters to items that match the price range + not itself
const filtered = fleaPrices.filter(x => const filtered = fleaPrices.filter(
x.price >= desiredItemCost - offerCostVariance && x.price <= desiredItemCost + offerCostVariance (x) =>
&& x.tpl !== offerItems[0]._tpl, x.price >= desiredItemCost - offerCostVariance
&& x.price <= desiredItemCost + offerCostVariance
&& x.tpl !== offerItems[0]._tpl,
); );
// No items on flea have a matching price, fall back to currency // No items on flea have a matching price, fall back to currency
@ -917,10 +919,10 @@ export class RagfairOfferGenerator
const fleaArray = Object.entries(fleaPrices).map(([tpl, price]) => ({ tpl: tpl, price: price })); const fleaArray = Object.entries(fleaPrices).map(([tpl, price]) => ({ tpl: tpl, price: price }));
// Only get item prices for items that also exist in items.json // Only get item prices for items that also exist in items.json
const filteredItems = fleaArray.filter(x => this.itemHelper.getItem(x.tpl)[0]); const filteredItems = fleaArray.filter((x) => this.itemHelper.getItem(x.tpl)[0]);
this.allowedFleaPriceItemsForBarter = filteredItems.filter(x => this.allowedFleaPriceItemsForBarter = filteredItems.filter(
!this.itemHelper.isOfBaseclasses(x.tpl, this.ragfairConfig.dynamic.barter.itemTypeBlacklist), (x) => !this.itemHelper.isOfBaseclasses(x.tpl, this.ragfairConfig.dynamic.barter.itemTypeBlacklist),
); );
} }
@ -941,8 +943,8 @@ export class RagfairOfferGenerator
): IBarterScheme[] ): IBarterScheme[]
{ {
const currency = this.ragfairServerHelper.getDynamicOfferCurrency(); const currency = this.ragfairServerHelper.getDynamicOfferCurrency();
const price = this.ragfairPriceService.getDynamicOfferPriceForOffer(offerWithChildren, currency, isPackOffer) const price
* multipler; = this.ragfairPriceService.getDynamicOfferPriceForOffer(offerWithChildren, currency, isPackOffer) * multipler;
return [{ count: price, _tpl: currency }]; return [{ count: price, _tpl: currency }];
} }

View File

@ -39,8 +39,8 @@ export class RepeatableQuestGenerator
@inject("LocalisationService") protected localisationService: LocalisationService, @inject("LocalisationService") protected localisationService: LocalisationService,
@inject("ObjectId") protected objectId: ObjectId, @inject("ObjectId") protected objectId: ObjectId,
@inject("RepeatableQuestHelper") protected repeatableQuestHelper: RepeatableQuestHelper, @inject("RepeatableQuestHelper") protected repeatableQuestHelper: RepeatableQuestHelper,
@inject("RepeatableQuestRewardGenerator") protected repeatableQuestRewardGenerator: @inject("RepeatableQuestRewardGenerator")
RepeatableQuestRewardGenerator, protected repeatableQuestRewardGenerator: RepeatableQuestRewardGenerator,
@inject("ConfigServer") protected configServer: ConfigServer, @inject("ConfigServer") protected configServer: ConfigServer,
@inject("RecursiveCloner") protected cloner: ICloner, @inject("RecursiveCloner") protected cloner: ICloner,
) )
@ -67,11 +67,11 @@ export class RepeatableQuestGenerator
const questType = this.randomUtil.drawRandomFromList<string>(questTypePool.types)[0]; const questType = this.randomUtil.drawRandomFromList<string>(questTypePool.types)[0];
// get traders from whitelist and filter by quest type availability // get traders from whitelist and filter by quest type availability
let traders = repeatableConfig.traderWhitelist.filter(x => x.questTypes.includes(questType)).map(x => let traders = repeatableConfig.traderWhitelist
x.traderId, .filter((x) => x.questTypes.includes(questType))
); .map((x) => x.traderId);
// filter out locked traders // filter out locked traders
traders = traders.filter(x => pmcTraderInfo[x].unlocked); traders = traders.filter((x) => pmcTraderInfo[x].unlocked);
const traderId = this.randomUtil.drawRandomFromList(traders)[0]; const traderId = this.randomUtil.drawRandomFromList(traders)[0];
switch (questType) switch (questType)
@ -158,15 +158,15 @@ export class RepeatableQuestGenerator
return Math.sqrt(Math.sqrt(target) + bodyPart + dist + weaponRequirement) * kill; return Math.sqrt(Math.sqrt(target) + bodyPart + dist + weaponRequirement) * kill;
} }
targetsConfig = targetsConfig.filter(x => targetsConfig = targetsConfig.filter((x) =>
Object.keys(questTypePool.pool.Elimination.targets).includes(x.key), Object.keys(questTypePool.pool.Elimination.targets).includes(x.key),
); );
if (targetsConfig.length === 0 || targetsConfig.every(x => x.data.isBoss)) if (targetsConfig.length === 0 || targetsConfig.every((x) => x.data.isBoss))
{ {
// There are no more targets left for elimination; delete it as a possible quest type // There are no more targets left for elimination; delete it as a possible quest type
// also if only bosses are left we need to leave otherwise it's a guaranteed boss elimination // also if only bosses are left we need to leave otherwise it's a guaranteed boss elimination
// -> then it would not be a quest with low probability anymore // -> then it would not be a quest with low probability anymore
questTypePool.types = questTypePool.types.filter(t => t !== "Elimination"); questTypePool.types = questTypePool.types.filter((t) => t !== "Elimination");
return null; return null;
} }
@ -188,12 +188,12 @@ export class RepeatableQuestGenerator
} }
else else
{ {
locations = locations.filter(l => l !== "any"); locations = locations.filter((l) => l !== "any");
if (locations.length > 0) if (locations.length > 0)
{ {
locationKey = this.randomUtil.drawRandomFromList<string>(locations)[0]; locationKey = this.randomUtil.drawRandomFromList<string>(locations)[0];
questTypePool.pool.Elimination.targets[targetKey].locations = locations.filter(l => questTypePool.pool.Elimination.targets[targetKey].locations = locations.filter(
l !== locationKey, (l) => l !== locationKey,
); );
if (questTypePool.pool.Elimination.targets[targetKey].locations.length === 0) if (questTypePool.pool.Elimination.targets[targetKey].locations.length === 0)
{ {
@ -238,16 +238,18 @@ export class RepeatableQuestGenerator
if (targetsConfig.data(targetKey).isBoss) if (targetsConfig.data(targetKey).isBoss)
{ {
// Get all boss spawn information // Get all boss spawn information
const bossSpawns = Object.values(this.databaseServer.getTables().locations).filter(x => const bossSpawns = Object.values(this.databaseServer.getTables().locations)
"base" in x && "Id" in x.base, .filter((x) => "base" in x && "Id" in x.base)
).map(x => ({ Id: x.base.Id, BossSpawn: x.base.BossLocationSpawn })); .map((x) => ({ Id: x.base.Id, BossSpawn: x.base.BossLocationSpawn }));
// filter for the current boss to spawn on map // filter for the current boss to spawn on map
const thisBossSpawns = bossSpawns.map(x => ({ const thisBossSpawns = bossSpawns
Id: x.Id, .map((x) => ({
BossSpawn: x.BossSpawn.filter(e => e.BossName === targetKey), Id: x.Id,
})).filter(x => x.BossSpawn.length > 0); BossSpawn: x.BossSpawn.filter((e) => e.BossName === targetKey),
}))
.filter((x) => x.BossSpawn.length > 0);
// remove blacklisted locations // remove blacklisted locations
const allowedSpawns = thisBossSpawns.filter(x => !eliminationConfig.distLocationBlacklist.includes(x.Id)); const allowedSpawns = thisBossSpawns.filter((x) => !eliminationConfig.distLocationBlacklist.includes(x.Id));
// if the boss spawns on nom-blacklisted locations and the current location is allowed we can generate a distance kill requirement // if the boss spawns on nom-blacklisted locations and the current location is allowed we can generate a distance kill requirement
isDistanceRequirementAllowed = isDistanceRequirementAllowed && allowedSpawns.length > 0; isDistanceRequirementAllowed = isDistanceRequirementAllowed && allowedSpawns.length > 0;
} }
@ -260,7 +262,7 @@ export class RepeatableQuestGenerator
+ eliminationConfig.minDist, + eliminationConfig.minDist,
); );
distance = Math.ceil(distance / 5) * 5; distance = Math.ceil(distance / 5) * 5;
distanceDifficulty = maxDistDifficulty * distance / eliminationConfig.maxDist; distanceDifficulty = (maxDistDifficulty * distance) / eliminationConfig.maxDist;
} }
let allowedWeaponsCategory: string = undefined; let allowedWeaponsCategory: string = undefined;
@ -269,13 +271,14 @@ export class RepeatableQuestGenerator
// Filter out close range weapons from far distance requirement // Filter out close range weapons from far distance requirement
if (distance > 50) if (distance > 50)
{ {
weaponCategoryRequirementConfig = weaponCategoryRequirementConfig.filter(category => weaponCategoryRequirementConfig = weaponCategoryRequirementConfig.filter((category) =>
["Shotgun", "Pistol"].includes(category.key), ["Shotgun", "Pistol"].includes(category.key),
); );
} }
else if (distance < 20) else if (distance < 20)
{ // Filter out far range weapons from close distance requirement {
weaponCategoryRequirementConfig = weaponCategoryRequirementConfig.filter(category => // Filter out far range weapons from close distance requirement
weaponCategoryRequirementConfig = weaponCategoryRequirementConfig.filter((category) =>
["MarksmanRifle", "DMR"].includes(category.key), ["MarksmanRifle", "DMR"].includes(category.key),
); );
} }
@ -497,8 +500,8 @@ export class RepeatableQuestGenerator
this.mathUtil.interp1(pmcLevel, levelsConfig, roublesConfig) * this.randomUtil.getFloat(0.5, 1), this.mathUtil.interp1(pmcLevel, levelsConfig, roublesConfig) * this.randomUtil.getFloat(0.5, 1),
); );
roublesBudget = Math.max(roublesBudget, 5000); roublesBudget = Math.max(roublesBudget, 5000);
let itemSelection = possibleItemsToRetrievePool.filter(x => let itemSelection = possibleItemsToRetrievePool.filter(
this.itemHelper.getItemPrice(x[0]) < roublesBudget, (x) => this.itemHelper.getItemPrice(x[0]) < roublesBudget,
); );
// We also have the option to use whitelist and/or blacklist which is defined in repeatableQuests.json as // We also have the option to use whitelist and/or blacklist which is defined in repeatableQuests.json as
@ -509,15 +512,16 @@ export class RepeatableQuestGenerator
= this.databaseServer.getTables().templates.repeatableQuests.data.Completion.itemsWhitelist; = this.databaseServer.getTables().templates.repeatableQuests.data.Completion.itemsWhitelist;
// Filter and concatenate the arrays according to current player level // Filter and concatenate the arrays according to current player level
const itemIdsWhitelisted = itemWhitelist.filter(p => p.minPlayerLevel <= pmcLevel).reduce( const itemIdsWhitelisted = itemWhitelist
(a, p) => a.concat(p.itemIds), .filter((p) => p.minPlayerLevel <= pmcLevel)
[], .reduce((a, p) => a.concat(p.itemIds), []);
);
itemSelection = itemSelection.filter((x) => itemSelection = itemSelection.filter((x) =>
{ {
// Whitelist can contain item tpls and item base type ids // Whitelist can contain item tpls and item base type ids
return itemIdsWhitelisted.some(v => this.itemHelper.isOfBaseclass(x[0], v)) return (
|| itemIdsWhitelisted.includes(x[0]); itemIdsWhitelisted.some((v) => this.itemHelper.isOfBaseclass(x[0], v))
|| itemIdsWhitelisted.includes(x[0])
);
}); });
// check if items are missing // check if items are missing
// const flatList = itemSelection.reduce((a, il) => a.concat(il[0]), []); // const flatList = itemSelection.reduce((a, il) => a.concat(il[0]), []);
@ -530,15 +534,16 @@ export class RepeatableQuestGenerator
= this.databaseServer.getTables().templates.repeatableQuests.data.Completion.itemsBlacklist; = this.databaseServer.getTables().templates.repeatableQuests.data.Completion.itemsBlacklist;
// we filter and concatenate the arrays according to current player level // we filter and concatenate the arrays according to current player level
const itemIdsBlacklisted = itemBlacklist.filter(p => p.minPlayerLevel <= pmcLevel).reduce( const itemIdsBlacklisted = itemBlacklist
(a, p) => a.concat(p.itemIds), .filter((p) => p.minPlayerLevel <= pmcLevel)
[], .reduce((a, p) => a.concat(p.itemIds), []);
);
itemSelection = itemSelection.filter((x) => itemSelection = itemSelection.filter((x) =>
{ {
return itemIdsBlacklisted.every(v => !this.itemHelper.isOfBaseclass(x[0], v)) return (
|| !itemIdsBlacklisted.includes(x[0]); itemIdsBlacklisted.every((v) => !this.itemHelper.isOfBaseclass(x[0], v))
|| !itemIdsBlacklisted.includes(x[0])
);
}); });
} }
@ -601,7 +606,7 @@ export class RepeatableQuestGenerator
if (roublesBudget > 0) if (roublesBudget > 0)
{ {
// reduce the list possible items to fulfill the new budget constraint // reduce the list possible items to fulfill the new budget constraint
itemSelection = itemSelection.filter(x => this.itemHelper.getItemPrice(x[0]) < roublesBudget); itemSelection = itemSelection.filter((x) => this.itemHelper.getItemPrice(x[0]) < roublesBudget);
if (itemSelection.length === 0) if (itemSelection.length === 0)
{ {
break; break;
@ -692,7 +697,7 @@ export class RepeatableQuestGenerator
if (Object.keys(questTypePool.pool.Exploration.locations).length === 0) if (Object.keys(questTypePool.pool.Exploration.locations).length === 0)
{ {
// there are no more locations left for exploration; delete it as a possible quest type // there are no more locations left for exploration; delete it as a possible quest type
questTypePool.types = questTypePool.types.filter(t => t !== "Exploration"); questTypePool.types = questTypePool.types.filter((t) => t !== "Exploration");
return null; return null;
} }
@ -737,13 +742,15 @@ export class RepeatableQuestGenerator
const mapExits = this.getLocationExitsForSide(locationKey, repeatableConfig.side); const mapExits = this.getLocationExitsForSide(locationKey, repeatableConfig.side);
// Only get exits that have a greater than 0% chance to spawn // Only get exits that have a greater than 0% chance to spawn
const exitPool = mapExits.filter(exit => exit.Chance > 0); const exitPool = mapExits.filter((exit) => exit.Chance > 0);
// Exclude exits with a requirement to leave (e.g. car extracts) // Exclude exits with a requirement to leave (e.g. car extracts)
const possibleExits = exitPool.filter(exit => !("PassageRequirement" in exit) const possibleExits = exitPool.filter(
|| repeatableConfig.questConfig.Exploration.specificExits.passageRequirementWhitelist.includes( (exit) =>
exit.PassageRequirement, !("PassageRequirement" in exit)
), || repeatableConfig.questConfig.Exploration.specificExits.passageRequirementWhitelist.includes(
exit.PassageRequirement,
),
); );
if (possibleExits.length === 0) if (possibleExits.length === 0)
@ -788,7 +795,7 @@ export class RepeatableQuestGenerator
const mapExtracts = this.databaseServer.getTables().locations[locationKey.toLocaleLowerCase()] const mapExtracts = this.databaseServer.getTables().locations[locationKey.toLocaleLowerCase()]
.allExtracts as Exit[]; .allExtracts as Exit[];
return mapExtracts.filter(exit => exit.Side === playerSide); return mapExtracts.filter((exit) => exit.Side === playerSide);
} }
protected generatePickupQuest( protected generatePickupQuest(
@ -811,18 +818,18 @@ export class RepeatableQuestGenerator
// const locationKey: string = this.randomUtil.drawRandomFromDict(questTypePool.pool.Pickup.locations)[0]; // const locationKey: string = this.randomUtil.drawRandomFromDict(questTypePool.pool.Pickup.locations)[0];
// const locationTarget = questTypePool.pool.Pickup.locations[locationKey]; // const locationTarget = questTypePool.pool.Pickup.locations[locationKey];
const findCondition = quest.conditions.AvailableForFinish.find(x => x.conditionType === "FindItem"); const findCondition = quest.conditions.AvailableForFinish.find((x) => x.conditionType === "FindItem");
findCondition.target = [itemTypeToFetchWithCount.itemType]; findCondition.target = [itemTypeToFetchWithCount.itemType];
findCondition.value = itemCountToFetch; findCondition.value = itemCountToFetch;
const counterCreatorCondition = quest.conditions.AvailableForFinish.find(x => const counterCreatorCondition = quest.conditions.AvailableForFinish.find(
x.conditionType === "CounterCreator", (x) => x.conditionType === "CounterCreator",
); );
// const locationCondition = counterCreatorCondition._props.counter.conditions.find(x => x._parent === "Location"); // const locationCondition = counterCreatorCondition._props.counter.conditions.find(x => x._parent === "Location");
// (locationCondition._props as ILocationConditionProps).target = [...locationTarget]; // (locationCondition._props as ILocationConditionProps).target = [...locationTarget];
const equipmentCondition = counterCreatorCondition.counter.conditions.find(x => const equipmentCondition = counterCreatorCondition.counter.conditions.find(
x.conditionType === "Equipment", (x) => x.conditionType === "Equipment",
); );
equipmentCondition.equipmentInclusive = [[itemTypeToFetchWithCount.itemType]]; equipmentCondition.equipmentInclusive = [[itemTypeToFetchWithCount.itemType]];
@ -887,46 +894,36 @@ export class RepeatableQuestGenerator
// Get template id from config based on side and type of quest // Get template id from config based on side and type of quest
questClone.templateId = this.questConfig.questTemplateIds[side.toLowerCase()][type.toLowerCase()]; questClone.templateId = this.questConfig.questTemplateIds[side.toLowerCase()][type.toLowerCase()];
questClone.name = questClone.name.replace("{traderId}", traderId).replace( questClone.name = questClone.name
"{templateId}", .replace("{traderId}", traderId)
questClone.templateId, .replace("{templateId}", questClone.templateId);
); questClone.note = questClone.note
questClone.note = questClone.note.replace("{traderId}", traderId).replace( .replace("{traderId}", traderId)
"{templateId}", .replace("{templateId}", questClone.templateId);
questClone.templateId, questClone.description = questClone.description
); .replace("{traderId}", traderId)
questClone.description = questClone.description.replace("{traderId}", traderId).replace( .replace("{templateId}", questClone.templateId);
"{templateId}", questClone.successMessageText = questClone.successMessageText
questClone.templateId, .replace("{traderId}", traderId)
); .replace("{templateId}", questClone.templateId);
questClone.successMessageText = questClone.successMessageText.replace("{traderId}", traderId).replace( questClone.failMessageText = questClone.failMessageText
"{templateId}", .replace("{traderId}", traderId)
questClone.templateId, .replace("{templateId}", questClone.templateId);
); questClone.startedMessageText = questClone.startedMessageText
questClone.failMessageText = questClone.failMessageText.replace("{traderId}", traderId).replace( .replace("{traderId}", traderId)
"{templateId}", .replace("{templateId}", questClone.templateId);
questClone.templateId, questClone.changeQuestMessageText = questClone.changeQuestMessageText
); .replace("{traderId}", traderId)
questClone.startedMessageText = questClone.startedMessageText.replace("{traderId}", traderId).replace( .replace("{templateId}", questClone.templateId);
"{templateId}", questClone.acceptPlayerMessage = questClone.acceptPlayerMessage
questClone.templateId, .replace("{traderId}", traderId)
); .replace("{templateId}", questClone.templateId);
questClone.changeQuestMessageText = questClone.changeQuestMessageText.replace("{traderId}", traderId).replace( questClone.declinePlayerMessage = questClone.declinePlayerMessage
"{templateId}", .replace("{traderId}", traderId)
questClone.templateId, .replace("{templateId}", questClone.templateId);
); questClone.completePlayerMessage = questClone.completePlayerMessage
questClone.acceptPlayerMessage = questClone.acceptPlayerMessage.replace("{traderId}", traderId).replace( .replace("{traderId}", traderId)
"{templateId}", .replace("{templateId}", questClone.templateId);
questClone.templateId,
);
questClone.declinePlayerMessage = questClone.declinePlayerMessage.replace("{traderId}", traderId).replace(
"{templateId}",
questClone.templateId,
);
questClone.completePlayerMessage = questClone.completePlayerMessage.replace("{traderId}", traderId).replace(
"{templateId}",
questClone.templateId,
);
return questClone; return questClone;
} }

View File

@ -94,11 +94,13 @@ export class RepeatableQuestRewardGenerator
// rewards are generated based on pmcLevel, difficulty and a random spread // rewards are generated based on pmcLevel, difficulty and a random spread
const rewardXP = Math.floor( const rewardXP = Math.floor(
effectiveDifficulty * this.mathUtil.interp1(pmcLevel, levelsConfig, xpConfig) effectiveDifficulty
* this.mathUtil.interp1(pmcLevel, levelsConfig, xpConfig)
* this.randomUtil.getFloat(1 - rewardSpreadConfig, 1 + rewardSpreadConfig), * this.randomUtil.getFloat(1 - rewardSpreadConfig, 1 + rewardSpreadConfig),
); );
const rewardRoubles = Math.floor( const rewardRoubles = Math.floor(
effectiveDifficulty * this.mathUtil.interp1(pmcLevel, levelsConfig, roublesConfig) effectiveDifficulty
* this.mathUtil.interp1(pmcLevel, levelsConfig, roublesConfig)
* this.randomUtil.getFloat(1 - rewardSpreadConfig, 1 + rewardSpreadConfig), * this.randomUtil.getFloat(1 - rewardSpreadConfig, 1 + rewardSpreadConfig),
); );
const rewardNumItems = this.randomUtil.randInt( const rewardNumItems = this.randomUtil.randInt(
@ -107,7 +109,9 @@ export class RepeatableQuestRewardGenerator
); );
const rewardReputation const rewardReputation
= Math.round( = Math.round(
100 * effectiveDifficulty * this.mathUtil.interp1(pmcLevel, levelsConfig, reputationConfig) 100
* effectiveDifficulty
* this.mathUtil.interp1(pmcLevel, levelsConfig, reputationConfig)
* this.randomUtil.getFloat(1 - rewardSpreadConfig, 1 + rewardSpreadConfig), * this.randomUtil.getFloat(1 - rewardSpreadConfig, 1 + rewardSpreadConfig),
) / 100; ) / 100;
const skillRewardChance = this.mathUtil.interp1(pmcLevel, levelsConfig, skillRewardChanceConfig); const skillRewardChance = this.mathUtil.interp1(pmcLevel, levelsConfig, skillRewardChanceConfig);
@ -134,7 +138,7 @@ export class RepeatableQuestRewardGenerator
this.addMoneyReward(traderId, rewards, rewardRoubles, rewardIndex); this.addMoneyReward(traderId, rewards, rewardRoubles, rewardIndex);
rewardIndex++; rewardIndex++;
const traderWhitelistDetails = repeatableConfig.traderWhitelist.find(x => x.traderId === traderId); const traderWhitelistDetails = repeatableConfig.traderWhitelist.find((x) => x.traderId === traderId);
if ( if (
traderWhitelistDetails.rewardCanBeWeapon traderWhitelistDetails.rewardCanBeWeapon
&& this.randomUtil.getChance100(traderWhitelistDetails.weaponRewardChancePercent) && this.randomUtil.getChance100(traderWhitelistDetails.weaponRewardChancePercent)
@ -150,7 +154,7 @@ export class RepeatableQuestRewardGenerator
while (defaultPresetPool.hasValues()) while (defaultPresetPool.hasValues())
{ {
const randomPreset = defaultPresetPool.getRandomValue(); const randomPreset = defaultPresetPool.getRandomValue();
const tpls = randomPreset._items.map(item => item._tpl); const tpls = randomPreset._items.map((item) => item._tpl);
const presetPrice = this.itemHelper.getItemAndChildrenPrice(tpls); const presetPrice = this.itemHelper.getItemAndChildrenPrice(tpls);
if (presetPrice <= roublesBudget) if (presetPrice <= roublesBudget)
{ {
@ -307,14 +311,16 @@ export class RepeatableQuestRewardGenerator
*/ */
protected canIncreaseRewardItemStackSize(item: ITemplateItem, maxRoublePriceToStack: number): boolean protected canIncreaseRewardItemStackSize(item: ITemplateItem, maxRoublePriceToStack: number): boolean
{ {
return this.presetHelper.getDefaultPresetOrItemPrice(item._id) < maxRoublePriceToStack return (
&& !this.itemHelper.isOfBaseclasses(item._id, [ this.presetHelper.getDefaultPresetOrItemPrice(item._id) < maxRoublePriceToStack
BaseClasses.WEAPON, && !this.itemHelper.isOfBaseclasses(item._id, [
BaseClasses.ARMORED_EQUIPMENT, BaseClasses.WEAPON,
BaseClasses.AMMO, BaseClasses.ARMORED_EQUIPMENT,
]) BaseClasses.AMMO,
&& !this.itemHelper.itemRequiresSoftInserts(item._id) ])
&& this.randomUtil.getChance100(25); && !this.itemHelper.itemRequiresSoftInserts(item._id)
&& this.randomUtil.getChance100(25)
);
} }
protected calculateAmmoStackSizeThatFitsBudget( protected calculateAmmoStackSizeThatFitsBudget(
@ -354,7 +360,7 @@ export class RepeatableQuestRewardGenerator
const rewardableItemPool = this.getRewardableItems(repeatableConfig, traderId); const rewardableItemPool = this.getRewardableItems(repeatableConfig, traderId);
const minPrice = Math.min(25000, 0.5 * roublesBudget); const minPrice = Math.min(25000, 0.5 * roublesBudget);
let rewardableItemPoolWithinBudget = rewardableItemPool.map(x => x[1]); let rewardableItemPoolWithinBudget = rewardableItemPool.map((x) => x[1]);
rewardableItemPoolWithinBudget = this.filterRewardPoolWithinBudget( rewardableItemPoolWithinBudget = this.filterRewardPoolWithinBudget(
rewardableItemPoolWithinBudget, rewardableItemPoolWithinBudget,
roublesBudget, roublesBudget,
@ -369,9 +375,9 @@ export class RepeatableQuestRewardGenerator
}), }),
); );
// In case we don't find any items in the price range // In case we don't find any items in the price range
rewardableItemPoolWithinBudget = rewardableItemPool.filter(x => rewardableItemPoolWithinBudget = rewardableItemPool
this.itemHelper.getItemPrice(x[0]) < roublesBudget, .filter((x) => this.itemHelper.getItemPrice(x[0]) < roublesBudget)
).map(x => x[1]); .map((x) => x[1]);
} }
return rewardableItemPoolWithinBudget; return rewardableItemPoolWithinBudget;
@ -392,7 +398,7 @@ export class RepeatableQuestRewardGenerator
if (preset) if (preset)
{ {
const rootItem = preset.find(x => x._tpl === tpl); const rootItem = preset.find((x) => x._tpl === tpl);
rewardItem.items = this.itemHelper.reparentItemAndChildren(rootItem, preset); rewardItem.items = this.itemHelper.reparentItemAndChildren(rootItem, preset);
rewardItem.target = rootItem._id; // Target property and root items id must match rewardItem.target = rootItem._id; // Target property and root items id must match
} }
@ -435,8 +441,8 @@ export class RepeatableQuestRewardGenerator
return false; return false;
} }
const traderWhitelist = repeatableQuestConfig.traderWhitelist.find(trader => const traderWhitelist = repeatableQuestConfig.traderWhitelist.find(
trader.traderId === traderId, (trader) => trader.traderId === traderId,
); );
return this.isValidRewardItem(tpl, repeatableQuestConfig, traderWhitelist?.rewardBaseWhitelist); return this.isValidRewardItem(tpl, repeatableQuestConfig, traderWhitelist?.rewardBaseWhitelist);
}, },
@ -502,7 +508,12 @@ export class RepeatableQuestRewardGenerator
return true; return true;
} }
protected addMoneyReward(traderId: string, rewards: IQuestRewards, rewardRoubles: number, rewardIndex: number): void protected addMoneyReward(
traderId: string,
rewards: IQuestRewards,
rewardRoubles: number,
rewardIndex: number,
): void
{ {
// PK and Fence use euros // PK and Fence use euros
if (traderId === Traders.PEACEKEEPER || traderId === Traders.FENCE) if (traderId === Traders.PEACEKEEPER || traderId === Traders.FENCE)

View File

@ -1,8 +1,7 @@
import { inject, injectable } from "tsyringe"; import { inject, injectable } from "tsyringe";
import { ItemHelper } from "@spt-aki/helpers/ItemHelper"; import { ItemHelper } from "@spt-aki/helpers/ItemHelper";
import { PresetHelper } from "@spt-aki/helpers/PresetHelper"; import { PresetHelper } from "@spt-aki/helpers/PresetHelper";
import { Product } from "@spt-aki/models/eft/common/tables/IBotBase"; import { Item } from "@spt-aki/models/eft/common/tables/IItem";
import { Item, Upd } from "@spt-aki/models/eft/common/tables/IItem";
import { ITemplateItem } from "@spt-aki/models/eft/common/tables/ITemplateItem"; import { ITemplateItem } from "@spt-aki/models/eft/common/tables/ITemplateItem";
import { IHideoutScavCase } from "@spt-aki/models/eft/hideout/IHideoutScavCase"; import { IHideoutScavCase } from "@spt-aki/models/eft/hideout/IHideoutScavCase";
import { BaseClasses } from "@spt-aki/models/enums/BaseClasses"; import { BaseClasses } from "@spt-aki/models/enums/BaseClasses";
@ -58,7 +57,7 @@ export class ScavCaseRewardGenerator
this.cacheDbItems(); this.cacheDbItems();
// Get scavcase details from hideout/scavcase.json // Get scavcase details from hideout/scavcase.json
const scavCaseDetails = this.databaseServer.getTables().hideout.scavcase.find(r => r._id === recipeId); const scavCaseDetails = this.databaseServer.getTables().hideout.scavcase.find((r) => r._id === recipeId);
const rewardItemCounts = this.getScavCaseRewardCountsAndPrices(scavCaseDetails); const rewardItemCounts = this.getScavCaseRewardCountsAndPrices(scavCaseDetails);
// Get items that fit the price criteria as set by the scavCase config // Get items that fit the price criteria as set by the scavCase config
@ -231,7 +230,8 @@ export class ScavCaseRewardGenerator
for (let i = 0; i < randomCount; i++) for (let i = 0; i < randomCount; i++)
{ {
if (this.rewardShouldBeMoney() && !rewardWasMoney) if (this.rewardShouldBeMoney() && !rewardWasMoney)
{ // Only allow one reward to be money {
// Only allow one reward to be money
result.push(this.getRandomMoney()); result.push(this.getRandomMoney());
if (!this.scavCaseConfig.allowMultipleMoneyRewardsPerRarity) if (!this.scavCaseConfig.allowMultipleMoneyRewardsPerRarity)
{ {
@ -239,7 +239,8 @@ export class ScavCaseRewardGenerator
} }
} }
else if (this.rewardShouldBeAmmo() && !rewardWasAmmo) else if (this.rewardShouldBeAmmo() && !rewardWasAmmo)
{ // Only allow one reward to be ammo {
// Only allow one reward to be ammo
result.push(this.getRandomAmmo(rarity)); result.push(this.getRandomAmmo(rarity));
if (!this.scavCaseConfig.allowMultipleAmmoRewardsPerRarity) if (!this.scavCaseConfig.allowMultipleAmmoRewardsPerRarity)
{ {

View File

@ -1,10 +1,8 @@
import { inject, injectable } from "tsyringe"; import { inject, injectable } from "tsyringe";
import { ApplicationContext } from "@spt-aki/context/ApplicationContext"; import { ApplicationContext } from "@spt-aki/context/ApplicationContext";
import { ContextVariableType } from "@spt-aki/context/ContextVariableType";
import { WeightedRandomHelper } from "@spt-aki/helpers/WeightedRandomHelper"; import { WeightedRandomHelper } from "@spt-aki/helpers/WeightedRandomHelper";
import { IWeather, IWeatherData } from "@spt-aki/models/eft/weather/IWeatherData"; import { IWeather, IWeatherData } from "@spt-aki/models/eft/weather/IWeatherData";
import { ConfigTypes } from "@spt-aki/models/enums/ConfigTypes"; import { ConfigTypes } from "@spt-aki/models/enums/ConfigTypes";
import { Season } from "@spt-aki/models/enums/Season";
import { WindDirection } from "@spt-aki/models/enums/WindDirection"; import { WindDirection } from "@spt-aki/models/enums/WindDirection";
import { IWeatherConfig } from "@spt-aki/models/spt/config/IWeatherConfig"; import { IWeatherConfig } from "@spt-aki/models/spt/config/IWeatherConfig";
import { ILogger } from "@spt-aki/models/spt/utils/ILogger"; import { ILogger } from "@spt-aki/models/spt/utils/ILogger";
@ -181,7 +179,8 @@ export class WeatherGenerator
protected getRandomFloat(node: string): number protected getRandomFloat(node: string): number
{ {
return Number.parseFloat( return Number.parseFloat(
this.randomUtil.getFloat(this.weatherConfig.weather[node].min, this.weatherConfig.weather[node].max) this.randomUtil
.getFloat(this.weatherConfig.weather[node].min, this.weatherConfig.weather[node].max)
.toPrecision(3), .toPrecision(3),
); );
} }

View File

@ -85,8 +85,8 @@ export class ExternalInventoryMagGen implements IInventoryMagGen
} }
/* We were unable to fit at least the minimum amount of magazines, /* We were unable to fit at least the minimum amount of magazines,
* so we fallback to default magazine and try again. * so we fallback to default magazine and try again.
* Temporary workaround to Killa spawning with no extra mags if he spawns with a drum mag */ * Temporary workaround to Killa spawning with no extra mags if he spawns with a drum mag */
if (magazineTpl === defaultMagazineTpl) if (magazineTpl === defaultMagazineTpl)
{ {
@ -153,15 +153,15 @@ export class ExternalInventoryMagGen implements IInventoryMagGen
): ITemplateItem ): ITemplateItem
{ {
// The mag Slot data for the weapon // The mag Slot data for the weapon
const magSlot = this.itemHelper.getItem(weaponTpl)[1]._props.Slots.find(x => x._name === "mod_magazine"); const magSlot = this.itemHelper.getItem(weaponTpl)[1]._props.Slots.find((x) => x._name === "mod_magazine");
if (!magSlot) if (!magSlot)
{ {
return null; return null;
} }
// All possible mags that fit into the weapon excluding blacklisted // All possible mags that fit into the weapon excluding blacklisted
const magazinePool = magSlot._props.filters[0].Filter.filter(x => !magazineBlacklist.includes(x)).map(x => const magazinePool = magSlot._props.filters[0].Filter.filter((x) => !magazineBlacklist.includes(x)).map(
this.itemHelper.getItem(x)[1], (x) => this.itemHelper.getItem(x)[1],
); );
if (!magazinePool) if (!magazinePool)
{ {
@ -169,7 +169,7 @@ export class ExternalInventoryMagGen implements IInventoryMagGen
} }
// Non-internal magazines that fit into the weapon // Non-internal magazines that fit into the weapon
const externalMagazineOnlyPool = magazinePool.filter(x => x._props.ReloadMagType !== "InternalMagazine"); const externalMagazineOnlyPool = magazinePool.filter((x) => x._props.ReloadMagType !== "InternalMagazine");
if (!externalMagazineOnlyPool || externalMagazineOnlyPool?.length === 0) if (!externalMagazineOnlyPool || externalMagazineOnlyPool?.length === 0)
{ {
return null; return null;

View File

@ -92,9 +92,10 @@ export class BotDifficultyHelper
*/ */
protected getDifficultySettings(type: string, difficulty: string): Difficulty protected getDifficultySettings(type: string, difficulty: string): Difficulty
{ {
let difficultySetting = this.pmcConfig.difficulty.toLowerCase() === "asonline" let difficultySetting
? difficulty = this.pmcConfig.difficulty.toLowerCase() === "asonline"
: this.pmcConfig.difficulty.toLowerCase(); ? difficulty
: this.pmcConfig.difficulty.toLowerCase();
difficultySetting = this.convertBotDifficultyDropdownToBotDifficulty(difficultySetting); difficultySetting = this.convertBotDifficultyDropdownToBotDifficulty(difficultySetting);

View File

@ -54,9 +54,9 @@ export class BotGeneratorHelper
public generateExtraPropertiesForItem(itemTemplate: ITemplateItem, botRole?: string): { upd?: Upd } public generateExtraPropertiesForItem(itemTemplate: ITemplateItem, botRole?: string): { upd?: Upd }
{ {
// Get raid settings, if no raid, default to day // Get raid settings, if no raid, default to day
const raidSettings = this.applicationContext.getLatestValue(ContextVariableType.RAID_CONFIGURATION)?.getValue< const raidSettings = this.applicationContext
IGetRaidConfigurationRequestData .getLatestValue(ContextVariableType.RAID_CONFIGURATION)
>(); ?.getValue<IGetRaidConfigurationRequestData>();
const raidIsNight = raidSettings?.timeVariant === "PAST"; const raidIsNight = raidSettings?.timeVariant === "PAST";
const itemProperties: Upd = {}; const itemProperties: Upd = {};
@ -64,11 +64,13 @@ export class BotGeneratorHelper
if (itemTemplate._props.MaxDurability) if (itemTemplate._props.MaxDurability)
{ {
if (itemTemplate._props.weapClass) if (itemTemplate._props.weapClass)
{ // Is weapon {
// Is weapon
itemProperties.Repairable = this.generateWeaponRepairableProperties(itemTemplate, botRole); itemProperties.Repairable = this.generateWeaponRepairableProperties(itemTemplate, botRole);
} }
else if (itemTemplate._props.armorClass) else if (itemTemplate._props.armorClass)
{ // Is armor {
// Is armor
itemProperties.Repairable = this.generateArmorRepairableProperties(itemTemplate, botRole); itemProperties.Repairable = this.generateArmorRepairableProperties(itemTemplate, botRole);
} }
} }
@ -287,7 +289,7 @@ export class BotGeneratorHelper
): IChooseRandomCompatibleModResult ): IChooseRandomCompatibleModResult
{ {
// TODO: Can probably be optimized to cache itemTemplates as items are added to inventory // TODO: Can probably be optimized to cache itemTemplates as items are added to inventory
const equippedItemsDb = itemsEquipped.map(item => this.databaseServer.getTables().templates.items[item._tpl]); const equippedItemsDb = itemsEquipped.map((item) => this.databaseServer.getTables().templates.items[item._tpl]);
const itemToEquipDb = this.itemHelper.getItem(tplToCheck); const itemToEquipDb = this.itemHelper.getItem(tplToCheck);
const itemToEquip = itemToEquipDb[1]; const itemToEquip = itemToEquipDb[1];
@ -318,27 +320,25 @@ export class BotGeneratorHelper
} }
// Check if any of the current weapon mod templates have the incoming item defined as incompatible // Check if any of the current weapon mod templates have the incoming item defined as incompatible
const blockingItem = equippedItemsDb.find(x => x._props.ConflictingItems?.includes(tplToCheck)); const blockingItem = equippedItemsDb.find((x) => x._props.ConflictingItems?.includes(tplToCheck));
if (blockingItem) if (blockingItem)
{ {
return { return {
incompatible: true, incompatible: true,
found: false, found: false,
reason: reason: `Cannot add: ${tplToCheck} ${itemToEquip._name} to slot: ${modSlot}. Blocked by: ${blockingItem._id} ${blockingItem._name}`,
`Cannot add: ${tplToCheck} ${itemToEquip._name} to slot: ${modSlot}. Blocked by: ${blockingItem._id} ${blockingItem._name}`,
slotBlocked: true, slotBlocked: true,
}; };
} }
// Check inverse to above, if the incoming item has any existing mods in its conflicting items array // Check inverse to above, if the incoming item has any existing mods in its conflicting items array
const blockingModItem = itemsEquipped.find(item => itemToEquip._props.ConflictingItems?.includes(item._tpl)); const blockingModItem = itemsEquipped.find((item) => itemToEquip._props.ConflictingItems?.includes(item._tpl));
if (blockingModItem) if (blockingModItem)
{ {
return { return {
incompatible: true, incompatible: true,
found: false, found: false,
reason: reason: ` Cannot add: ${tplToCheck} to slot: ${modSlot}. Would block existing item: ${blockingModItem._tpl} in slot: ${blockingModItem.slotId}`,
` Cannot add: ${tplToCheck} to slot: ${modSlot}. Would block existing item: ${blockingModItem._tpl} in slot: ${blockingModItem.slotId}`,
}; };
} }
@ -365,7 +365,7 @@ export class BotGeneratorHelper
} }
// TODO: Can probably be optimized to cache itemTemplates as items are added to inventory // TODO: Can probably be optimized to cache itemTemplates as items are added to inventory
const equippedItemsDb = itemsEquipped.map(i => this.databaseServer.getTables().templates.items[i._tpl]); const equippedItemsDb = itemsEquipped.map((i) => this.databaseServer.getTables().templates.items[i._tpl]);
const itemToEquipDb = this.itemHelper.getItem(tplToCheck); const itemToEquipDb = this.itemHelper.getItem(tplToCheck);
const itemToEquip = itemToEquipDb[1]; const itemToEquip = itemToEquipDb[1];
@ -395,29 +395,27 @@ export class BotGeneratorHelper
} }
// Does an equipped item have a property that blocks the desired item - check for prop "BlocksX" .e.g BlocksEarpiece / BlocksFaceCover // Does an equipped item have a property that blocks the desired item - check for prop "BlocksX" .e.g BlocksEarpiece / BlocksFaceCover
let blockingItem = equippedItemsDb.find(x => x._props[`Blocks${equipmentSlot}`]); let blockingItem = equippedItemsDb.find((x) => x._props[`Blocks${equipmentSlot}`]);
if (blockingItem) if (blockingItem)
{ {
// this.logger.warning(`1 incompatibility found between - ${itemToEquip[1]._name} and ${blockingItem._name} - ${equipmentSlot}`); // this.logger.warning(`1 incompatibility found between - ${itemToEquip[1]._name} and ${blockingItem._name} - ${equipmentSlot}`);
return { return {
incompatible: true, incompatible: true,
found: false, found: false,
reason: reason: `${tplToCheck} ${itemToEquip._name} in slot: ${equipmentSlot} blocked by: ${blockingItem._id} ${blockingItem._name}`,
`${tplToCheck} ${itemToEquip._name} in slot: ${equipmentSlot} blocked by: ${blockingItem._id} ${blockingItem._name}`,
slotBlocked: true, slotBlocked: true,
}; };
} }
// Check if any of the current inventory templates have the incoming item defined as incompatible // Check if any of the current inventory templates have the incoming item defined as incompatible
blockingItem = equippedItemsDb.find(x => x._props.ConflictingItems?.includes(tplToCheck)); blockingItem = equippedItemsDb.find((x) => x._props.ConflictingItems?.includes(tplToCheck));
if (blockingItem) if (blockingItem)
{ {
// this.logger.warning(`2 incompatibility found between - ${itemToEquip[1]._name} and ${blockingItem._props.Name} - ${equipmentSlot}`); // this.logger.warning(`2 incompatibility found between - ${itemToEquip[1]._name} and ${blockingItem._props.Name} - ${equipmentSlot}`);
return { return {
incompatible: true, incompatible: true,
found: false, found: false,
reason: reason: `${tplToCheck} ${itemToEquip._name} in slot: ${equipmentSlot} blocked by: ${blockingItem._id} ${blockingItem._name}`,
`${tplToCheck} ${itemToEquip._name} in slot: ${equipmentSlot} blocked by: ${blockingItem._id} ${blockingItem._name}`,
slotBlocked: true, slotBlocked: true,
}; };
} }
@ -425,14 +423,13 @@ export class BotGeneratorHelper
// Does item being checked get blocked/block existing item // Does item being checked get blocked/block existing item
if (itemToEquip._props.BlocksHeadwear) if (itemToEquip._props.BlocksHeadwear)
{ {
const existingHeadwear = itemsEquipped.find(x => x.slotId === "Headwear"); const existingHeadwear = itemsEquipped.find((x) => x.slotId === "Headwear");
if (existingHeadwear) if (existingHeadwear)
{ {
return { return {
incompatible: true, incompatible: true,
found: false, found: false,
reason: reason: `${tplToCheck} ${itemToEquip._name} is blocked by: ${existingHeadwear._tpl} in slot: ${existingHeadwear.slotId}`,
`${tplToCheck} ${itemToEquip._name} is blocked by: ${existingHeadwear._tpl} in slot: ${existingHeadwear.slotId}`,
slotBlocked: true, slotBlocked: true,
}; };
} }
@ -441,14 +438,13 @@ export class BotGeneratorHelper
// Does item being checked get blocked/block existing item // Does item being checked get blocked/block existing item
if (itemToEquip._props.BlocksFaceCover) if (itemToEquip._props.BlocksFaceCover)
{ {
const existingFaceCover = itemsEquipped.find(item => item.slotId === "FaceCover"); const existingFaceCover = itemsEquipped.find((item) => item.slotId === "FaceCover");
if (existingFaceCover) if (existingFaceCover)
{ {
return { return {
incompatible: true, incompatible: true,
found: false, found: false,
reason: reason: `${tplToCheck} ${itemToEquip._name} is blocked by: ${existingFaceCover._tpl} in slot: ${existingFaceCover.slotId}`,
`${tplToCheck} ${itemToEquip._name} is blocked by: ${existingFaceCover._tpl} in slot: ${existingFaceCover.slotId}`,
slotBlocked: true, slotBlocked: true,
}; };
} }
@ -457,14 +453,13 @@ export class BotGeneratorHelper
// Does item being checked get blocked/block existing item // Does item being checked get blocked/block existing item
if (itemToEquip._props.BlocksEarpiece) if (itemToEquip._props.BlocksEarpiece)
{ {
const existingEarpiece = itemsEquipped.find(item => item.slotId === "Earpiece"); const existingEarpiece = itemsEquipped.find((item) => item.slotId === "Earpiece");
if (existingEarpiece) if (existingEarpiece)
{ {
return { return {
incompatible: true, incompatible: true,
found: false, found: false,
reason: reason: `${tplToCheck} ${itemToEquip._name} is blocked by: ${existingEarpiece._tpl} in slot: ${existingEarpiece.slotId}`,
`${tplToCheck} ${itemToEquip._name} is blocked by: ${existingEarpiece._tpl} in slot: ${existingEarpiece.slotId}`,
slotBlocked: true, slotBlocked: true,
}; };
} }
@ -473,29 +468,27 @@ export class BotGeneratorHelper
// Does item being checked get blocked/block existing item // Does item being checked get blocked/block existing item
if (itemToEquip._props.BlocksArmorVest) if (itemToEquip._props.BlocksArmorVest)
{ {
const existingArmorVest = itemsEquipped.find(item => item.slotId === "ArmorVest"); const existingArmorVest = itemsEquipped.find((item) => item.slotId === "ArmorVest");
if (existingArmorVest) if (existingArmorVest)
{ {
return { return {
incompatible: true, incompatible: true,
found: false, found: false,
reason: reason: `${tplToCheck} ${itemToEquip._name} is blocked by: ${existingArmorVest._tpl} in slot: ${existingArmorVest.slotId}`,
`${tplToCheck} ${itemToEquip._name} is blocked by: ${existingArmorVest._tpl} in slot: ${existingArmorVest.slotId}`,
slotBlocked: true, slotBlocked: true,
}; };
} }
} }
// Check if the incoming item has any inventory items defined as incompatible // Check if the incoming item has any inventory items defined as incompatible
const blockingInventoryItem = itemsEquipped.find(x => itemToEquip._props.ConflictingItems?.includes(x._tpl)); const blockingInventoryItem = itemsEquipped.find((x) => itemToEquip._props.ConflictingItems?.includes(x._tpl));
if (blockingInventoryItem) if (blockingInventoryItem)
{ {
// this.logger.warning(`3 incompatibility found between - ${itemToEquip[1]._name} and ${blockingInventoryItem._tpl} - ${equipmentSlot}`) // this.logger.warning(`3 incompatibility found between - ${itemToEquip[1]._name} and ${blockingInventoryItem._tpl} - ${equipmentSlot}`)
return { return {
incompatible: true, incompatible: true,
found: false, found: false,
reason: reason: `${tplToCheck} blocks existing item ${blockingInventoryItem._tpl} in slot ${blockingInventoryItem.slotId}`,
`${tplToCheck} blocks existing item ${blockingInventoryItem._tpl} in slot ${blockingInventoryItem.slotId}`,
}; };
} }
@ -543,7 +536,7 @@ export class BotGeneratorHelper
continue; continue;
} }
// Get container to put item into // Get container to put item into
const container = inventory.items.find(item => item.slotId === equipmentSlotId); const container = inventory.items.find((item) => item.slotId === equipmentSlotId);
if (!container) if (!container)
{ {
missingContainerCount++; missingContainerCount++;
@ -551,9 +544,9 @@ export class BotGeneratorHelper
{ {
// Bot doesnt have any containers we want to add item to // Bot doesnt have any containers we want to add item to
this.logger.debug( this.logger.debug(
`Unable to add item: ${itemWithChildren[0]._tpl} to bot as it lacks the following containers: ${ `Unable to add item: ${itemWithChildren[0]._tpl} to bot as it lacks the following containers: ${equipmentSlots.join(
equipmentSlots.join(",") ",",
}`, )}`,
); );
return ItemAddedResult.NO_CONTAINERS; return ItemAddedResult.NO_CONTAINERS;
@ -589,7 +582,8 @@ export class BotGeneratorHelper
{ {
// Grid is empty, skip or item size is bigger than grid // Grid is empty, skip or item size is bigger than grid
if ( if (
slotGrid._props.cellsH === 0 || slotGrid._props.cellsV === 0 slotGrid._props.cellsH === 0
|| slotGrid._props.cellsV === 0
|| itemSize[0] * itemSize[1] > slotGrid._props.cellsV * slotGrid._props.cellsH || itemSize[0] * itemSize[1] > slotGrid._props.cellsV * slotGrid._props.cellsH
) )
{ {
@ -604,12 +598,12 @@ export class BotGeneratorHelper
} }
// Get all root items in found container // Get all root items in found container
const existingContainerItems = inventory.items.filter(item => const existingContainerItems = inventory.items.filter(
item.parentId === container._id && item.slotId === slotGrid._name, (item) => item.parentId === container._id && item.slotId === slotGrid._name,
); );
// Get root items in container we can iterate over to find out what space is free // Get root items in container we can iterate over to find out what space is free
const containerItemsToCheck = existingContainerItems.filter(x => x.slotId === slotGrid._name); const containerItemsToCheck = existingContainerItems.filter((x) => x.slotId === slotGrid._name);
for (const item of containerItemsToCheck) for (const item of containerItemsToCheck)
{ {
// Look for children on items, insert into array if found // Look for children on items, insert into array if found
@ -635,7 +629,7 @@ export class BotGeneratorHelper
// Open slot found, add item to inventory // Open slot found, add item to inventory
if (findSlotResult.success) if (findSlotResult.success)
{ {
const parentItem = itemWithChildren.find(i => i._id === rootItemId); const parentItem = itemWithChildren.find((i) => i._id === rootItemId);
// Set items parent to container id // Set items parent to container id
parentItem.parentId = container._id; parentItem.parentId = container._id;

View File

@ -66,7 +66,7 @@ export class BotHelper
public isBotBoss(botRole: string): boolean public isBotBoss(botRole: string): boolean
{ {
return this.botConfig.bosses.some(x => x.toLowerCase() === botRole?.toLowerCase()); return this.botConfig.bosses.some((x) => x.toLowerCase() === botRole?.toLowerCase());
} }
public isBotFollower(botRole: string): boolean public isBotFollower(botRole: string): boolean
@ -182,8 +182,10 @@ export class BotHelper
public rollChanceToBePmc(role: string, botConvertMinMax: MinMax): boolean public rollChanceToBePmc(role: string, botConvertMinMax: MinMax): boolean
{ {
return role.toLowerCase() in this.pmcConfig.convertIntoPmcChance return (
&& this.randomUtil.getChance100(this.randomUtil.getInt(botConvertMinMax.min, botConvertMinMax.max)); role.toLowerCase() in this.pmcConfig.convertIntoPmcChance
&& this.randomUtil.getChance100(this.randomUtil.getInt(botConvertMinMax.min, botConvertMinMax.max))
);
} }
public botRoleIsPmc(botRole: string): boolean public botRoleIsPmc(botRole: string): boolean
@ -207,7 +209,7 @@ export class BotHelper
return null; return null;
} }
return botEquipConfig.randomisation.find(x => botLevel >= x.levelRange.min && botLevel <= x.levelRange.max); return botEquipConfig.randomisation.find((x) => botLevel >= x.levelRange.min && botLevel <= x.levelRange.max);
} }
/** /**
@ -216,9 +218,7 @@ export class BotHelper
*/ */
public getRandomizedPmcRole(): string public getRandomizedPmcRole(): string
{ {
return this.randomUtil.getChance100(this.pmcConfig.isUsec) return this.randomUtil.getChance100(this.pmcConfig.isUsec) ? this.pmcConfig.usecType : this.pmcConfig.bearType;
? this.pmcConfig.usecType
: this.pmcConfig.bearType;
} }
/** /**

View File

@ -45,9 +45,10 @@ export class BotWeaponGeneratorHelper
{ {
const firstSlotAmmoTpl = magTemplate._props.Cartridges[0]._props.filters[0].Filter[0]; const firstSlotAmmoTpl = magTemplate._props.Cartridges[0]._props.filters[0].Filter[0];
const ammoMaxStackSize = this.itemHelper.getItem(firstSlotAmmoTpl)[1]?._props?.StackMaxSize ?? 1; const ammoMaxStackSize = this.itemHelper.getItem(firstSlotAmmoTpl)[1]?._props?.StackMaxSize ?? 1;
chamberBulletCount = ammoMaxStackSize === 1 chamberBulletCount
? 1 // Rotating grenade launcher = ammoMaxStackSize === 1
: magTemplate._props.Slots.length; // Shotguns/revolvers. We count the number of camoras as the _max_count of the magazine is 0 ? 1 // Rotating grenade launcher
: magTemplate._props.Slots.length; // Shotguns/revolvers. We count the number of camoras as the _max_count of the magazine is 0
} }
else if (parentItem._id === BaseClasses.UBGL) else if (parentItem._id === BaseClasses.UBGL)
{ {
@ -60,7 +61,7 @@ export class BotWeaponGeneratorHelper
} }
/* Get the amount of bullets that would fit in the internal magazine /* Get the amount of bullets that would fit in the internal magazine
* and multiply by how many magazines were supposed to be created */ * and multiply by how many magazines were supposed to be created */
return chamberBulletCount * randomizedMagazineCount; return chamberBulletCount * randomizedMagazineCount;
} }

View File

@ -35,7 +35,7 @@ export class ContainerHelper
const limitX = containerX - minVolume; const limitX = containerX - minVolume;
// Every x+y slot taken up in container, exit // Every x+y slot taken up in container, exit
if (container2D.every(x => x.every(y => y === 1))) if (container2D.every((x) => x.every((y) => y === 1)))
{ {
return new FindSlotResult(false); return new FindSlotResult(false);
} }
@ -44,7 +44,7 @@ export class ContainerHelper
for (let y = 0; y < limitY; y++) for (let y = 0; y < limitY; y++)
{ {
// Across // Across
if (container2D[y].every(x => x === 1)) if (container2D[y].every((x) => x === 1))
{ {
// Every item in row is full, skip row // Every item in row is full, skip row
continue; continue;

View File

@ -12,8 +12,7 @@ export abstract class AbstractDialogueChatBot implements IDialogueChatBot
protected mailSendService: MailSendService, protected mailSendService: MailSendService,
protected chatCommands: IChatCommand[] | ICommandoCommand[], protected chatCommands: IChatCommand[] | ICommandoCommand[],
) )
{ {}
}
/** /**
* @deprecated As of v3.7.6. Use registerChatCommand. * @deprecated As of v3.7.6. Use registerChatCommand.
@ -26,7 +25,7 @@ export abstract class AbstractDialogueChatBot implements IDialogueChatBot
public registerChatCommand(chatCommand: IChatCommand | ICommandoCommand): void public registerChatCommand(chatCommand: IChatCommand | ICommandoCommand): void
{ {
if (this.chatCommands.some(cc => cc.getCommandPrefix() === chatCommand.getCommandPrefix())) if (this.chatCommands.some((cc) => cc.getCommandPrefix() === chatCommand.getCommandPrefix()))
{ {
throw new Error( throw new Error(
`The command "${chatCommand.getCommandPrefix()}" attempting to be registered already exists.`, `The command "${chatCommand.getCommandPrefix()}" attempting to be registered already exists.`,
@ -49,7 +48,7 @@ export abstract class AbstractDialogueChatBot implements IDialogueChatBot
const splitCommand = request.text.split(" "); const splitCommand = request.text.split(" ");
const commandos = this.chatCommands.filter(c => c.getCommandPrefix() === splitCommand[0]); const commandos = this.chatCommands.filter((c) => c.getCommandPrefix() === splitCommand[0]);
if (commandos[0]?.getCommands().has(splitCommand[1])) if (commandos[0]?.getCommands().has(splitCommand[1]))
{ {
return commandos[0].handle(splitCommand[1], this.getChatBot(), sessionId, request); return commandos[0].handle(splitCommand[1], this.getChatBot(), sessionId, request);

View File

@ -18,18 +18,20 @@ export class SptCommandoCommands implements IChatCommand
const coreConfigs = this.configServer.getConfig<ICoreConfig>(ConfigTypes.CORE); const coreConfigs = this.configServer.getConfig<ICoreConfig>(ConfigTypes.CORE);
// if give command is disabled or commando commands are disabled // if give command is disabled or commando commands are disabled
if ( if (
!(coreConfigs.features?.chatbotFeatures?.commandoFeatures?.giveCommandEnabled !(
&& coreConfigs.features?.chatbotFeatures?.commandoEnabled) coreConfigs.features?.chatbotFeatures?.commandoFeatures?.giveCommandEnabled
&& coreConfigs.features?.chatbotFeatures?.commandoEnabled
)
) )
{ {
const giveCommand = this.sptCommands.find(c => c.getCommand().toLocaleLowerCase() === "give"); const giveCommand = this.sptCommands.find((c) => c.getCommand().toLocaleLowerCase() === "give");
this.sptCommands.splice(this.sptCommands.indexOf(giveCommand), 1); this.sptCommands.splice(this.sptCommands.indexOf(giveCommand), 1);
} }
} }
public registerSptCommandoCommand(command: ISptCommand): void public registerSptCommandoCommand(command: ISptCommand): void
{ {
if (this.sptCommands.some(c => c.getCommand() === command.getCommand())) if (this.sptCommands.some((c) => c.getCommand() === command.getCommand()))
{ {
throw new Error(`The command "${command.getCommand()}" attempting to be registered already exists.`); throw new Error(`The command "${command.getCommand()}" attempting to be registered already exists.`);
} }
@ -38,7 +40,7 @@ export class SptCommandoCommands implements IChatCommand
public getCommandHelp(command: string): string public getCommandHelp(command: string): string
{ {
return this.sptCommands.find(c => c.getCommand() === command)?.getCommandHelp(); return this.sptCommands.find((c) => c.getCommand() === command)?.getCommandHelp();
} }
public getCommandPrefix(): string public getCommandPrefix(): string
@ -48,7 +50,7 @@ export class SptCommandoCommands implements IChatCommand
public getCommands(): Set<string> public getCommands(): Set<string>
{ {
return new Set(this.sptCommands.map(c => c.getCommand())); return new Set(this.sptCommands.map((c) => c.getCommand()));
} }
public handle( public handle(
@ -58,10 +60,8 @@ export class SptCommandoCommands implements IChatCommand
request: ISendMessageRequest, request: ISendMessageRequest,
): string ): string
{ {
return this.sptCommands.find(c => c.getCommand() === command).performAction( return this.sptCommands
commandHandler, .find((c) => c.getCommand() === command)
sessionId, .performAction(commandHandler, sessionId, request);
request,
);
} }
} }

View File

@ -31,7 +31,12 @@ export class GiveSptCommand implements ISptCommand
private static commandRegex = /^spt give (((([a-z]{2,5}) )?"(.+)"|\w+) )?([0-9]+)$/; private static commandRegex = /^spt give (((([a-z]{2,5}) )?"(.+)"|\w+) )?([0-9]+)$/;
private static acceptableConfidence = 0.9; private static acceptableConfidence = 0.9;
// exception for flares // exception for flares
private static excludedPresetItems = new Set<string>(["62178c4d4ecf221597654e3d", "6217726288ed9f0845317459", "624c0b3340357b5f566e8766"]); private static excludedPresetItems = new Set<string>([
"62178c4d4ecf221597654e3d",
"6217726288ed9f0845317459",
"624c0b3340357b5f566e8766",
]);
protected savedCommand: Map<string, SavedCommand> = new Map<string, SavedCommand>(); protected savedCommand: Map<string, SavedCommand> = new Map<string, SavedCommand>();
public constructor( public constructor(
@ -45,8 +50,7 @@ export class GiveSptCommand implements ISptCommand
@inject("ItemFilterService") protected itemFilterService: ItemFilterService, @inject("ItemFilterService") protected itemFilterService: ItemFilterService,
@inject("RecursiveCloner") protected cloner: ICloner, @inject("RecursiveCloner") protected cloner: ICloner,
) )
{ {}
}
public getCommand(): string public getCommand(): string
{ {
@ -55,7 +59,7 @@ export class GiveSptCommand implements ISptCommand
public getCommandHelp(): string public getCommandHelp(): string
{ {
return "spt give\n========\nSends items to the player through the message system.\n\n\tspt give [template ID] [quantity]\n\t\tEx: spt give 544fb25a4bdc2dfb738b4567 2\n\n\tspt give [\"item name\"] [quantity]\n\t\tEx: spt give \"pack of sugar\" 10\n\n\tspt give [locale] [\"item name\"] [quantity]\n\t\tEx: spt give fr \"figurine de chat\" 3"; return 'spt give\n========\nSends items to the player through the message system.\n\n\tspt give [template ID] [quantity]\n\t\tEx: spt give 544fb25a4bdc2dfb738b4567 2\n\n\tspt give ["item name"] [quantity]\n\t\tEx: spt give "pack of sugar" 10\n\n\tspt give [locale] ["item name"] [quantity]\n\t\tEx: spt give fr "figurine de chat" 3';
} }
public performAction(commandHandler: IUserDialogInfo, sessionId: string, request: ISendMessageRequest): string public performAction(commandHandler: IUserDialogInfo, sessionId: string, request: ISendMessageRequest): string
@ -65,7 +69,7 @@ export class GiveSptCommand implements ISptCommand
this.mailSendService.sendUserMessageToPlayer( this.mailSendService.sendUserMessageToPlayer(
sessionId, sessionId,
commandHandler, commandHandler,
"Invalid use of give command. Use \"help\" for more information.", 'Invalid use of give command. Use "help" for more information.',
); );
return request.dialogId; return request.dialogId;
} }
@ -85,7 +89,7 @@ export class GiveSptCommand implements ISptCommand
this.mailSendService.sendUserMessageToPlayer( this.mailSendService.sendUserMessageToPlayer(
sessionId, sessionId,
commandHandler, commandHandler,
"Invalid use of give command. Use \"help\" for more information.", 'Invalid use of give command. Use "help" for more information.',
); );
return request.dialogId; return request.dialogId;
} }
@ -95,7 +99,7 @@ export class GiveSptCommand implements ISptCommand
this.mailSendService.sendUserMessageToPlayer( this.mailSendService.sendUserMessageToPlayer(
sessionId, sessionId,
commandHandler, commandHandler,
"Invalid selection. Outside of bounds! Use \"help\" for more information.", 'Invalid selection. Outside of bounds! Use "help" for more information.',
); );
return request.dialogId; return request.dialogId;
} }
@ -120,7 +124,7 @@ export class GiveSptCommand implements ISptCommand
this.mailSendService.sendUserMessageToPlayer( this.mailSendService.sendUserMessageToPlayer(
sessionId, sessionId,
commandHandler, commandHandler,
"Invalid quantity! Must be 1 or higher. Use \"help\" for more information.", 'Invalid quantity! Must be 1 or higher. Use "help" for more information.',
); );
return request.dialogId; return request.dialogId;
} }
@ -142,17 +146,19 @@ export class GiveSptCommand implements ISptCommand
locale = "en"; locale = "en";
} }
const localizedGlobal = this.databaseServer.getTables().locales.global[locale] const localizedGlobal
?? this.databaseServer.getTables().locales.global.en; = this.databaseServer.getTables().locales.global[locale]
?? this.databaseServer.getTables().locales.global.en;
const closestItemsMatchedByName = this.itemHelper.getItems() const closestItemsMatchedByName = this.itemHelper
.filter(i => this.isItemAllowed(i)) .getItems()
.map(i => localizedGlobal[`${i?._id} Name`]?.toLowerCase() ?? i._props.Name) .filter((i) => this.isItemAllowed(i))
.filter(i => i !== undefined && i !== "") .map((i) => localizedGlobal[`${i?._id} Name`]?.toLowerCase() ?? i._props.Name)
.map(i => ({ match: stringSimilarity( .filter((i) => i !== undefined && i !== "")
item.toLocaleLowerCase(), .map((i) => ({
i.toLocaleLowerCase(), match: stringSimilarity(item.toLocaleLowerCase(), i.toLocaleLowerCase()),
), itemName: i })) itemName: i,
}))
.sort((a1, a2) => a2.match - a1.match); .sort((a1, a2) => a2.match - a1.match);
if (closestItemsMatchedByName[0].match >= GiveSptCommand.acceptableConfidence) if (closestItemsMatchedByName[0].match >= GiveSptCommand.acceptableConfidence)
@ -164,11 +170,16 @@ export class GiveSptCommand implements ISptCommand
let i = 1; let i = 1;
const slicedItems = closestItemsMatchedByName.slice(0, 10); const slicedItems = closestItemsMatchedByName.slice(0, 10);
// max 10 item names and map them // max 10 item names and map them
const itemList = slicedItems.map(match => `${i++}. ${match.itemName} (conf: ${(match.match * 100).toFixed(2)})`) const itemList = slicedItems
.map((match) => `${i++}. ${match.itemName} (conf: ${(match.match * 100).toFixed(2)})`)
.join("\n"); .join("\n");
this.savedCommand.set( this.savedCommand.set(
sessionId, sessionId,
new SavedCommand(quantity, slicedItems.map(i => i.itemName), locale), new SavedCommand(
quantity,
slicedItems.map((i) => i.itemName),
locale,
),
); );
this.mailSendService.sendUserMessageToPlayer( this.mailSendService.sendUserMessageToPlayer(
sessionId, sessionId,
@ -180,14 +191,15 @@ export class GiveSptCommand implements ISptCommand
} }
} }
const localizedGlobal = this.databaseServer.getTables().locales.global[locale] const localizedGlobal
?? this.databaseServer.getTables().locales.global.en; = this.databaseServer.getTables().locales.global[locale] ?? this.databaseServer.getTables().locales.global.en;
// If item is an item name, we need to search using that item name and the locale which one we want otherwise // If item is an item name, we need to search using that item name and the locale which one we want otherwise
// item is just the tplId. // item is just the tplId.
const tplId = isItemName const tplId = isItemName
? this.itemHelper.getItems() ? this.itemHelper
.filter(i => this.isItemAllowed(i)) .getItems()
.find(i => (localizedGlobal[`${i?._id} Name`]?.toLowerCase() ?? i._props.Name) === item)._id .filter((i) => this.isItemAllowed(i))
.find((i) => (localizedGlobal[`${i?._id} Name`]?.toLowerCase() ?? i._props.Name) === item)._id
: item; : item;
const checkedItem = this.itemHelper.getItem(tplId); const checkedItem = this.itemHelper.getItem(tplId);
@ -274,13 +286,15 @@ export class GiveSptCommand implements ISptCommand
*/ */
protected isItemAllowed(templateItem: ITemplateItem): boolean protected isItemAllowed(templateItem: ITemplateItem): boolean
{ {
return templateItem._type !== "Node" return (
&& !this.itemHelper.isQuestItem(templateItem._id) templateItem._type !== "Node"
&& !this.itemFilterService.isItemBlacklisted(templateItem._id) && !this.itemHelper.isQuestItem(templateItem._id)
&& (templateItem._props?.Prefab?.path ?? "") !== "" && !this.itemFilterService.isItemBlacklisted(templateItem._id)
&& !this.itemHelper.isOfBaseclass(templateItem._id, BaseClasses.HIDEOUT_AREA_CONTAINER) && (templateItem._props?.Prefab?.path ?? "") !== ""
&& !this.itemHelper.isOfBaseclass(templateItem._id, BaseClasses.LOOT_CONTAINER) && !this.itemHelper.isOfBaseclass(templateItem._id, BaseClasses.HIDEOUT_AREA_CONTAINER)
&& !this.itemHelper.isOfBaseclass(templateItem._id, BaseClasses.RANDOM_LOOT_CONTAINER) && !this.itemHelper.isOfBaseclass(templateItem._id, BaseClasses.LOOT_CONTAINER)
&& !this.itemHelper.isOfBaseclass(templateItem._id, BaseClasses.MOB_CONTAINER); && !this.itemHelper.isOfBaseclass(templateItem._id, BaseClasses.RANDOM_LOOT_CONTAINER)
&& !this.itemHelper.isOfBaseclass(templateItem._id, BaseClasses.MOB_CONTAINER)
);
} }
} }

View File

@ -1,6 +1,9 @@
export class SavedCommand export class SavedCommand
{ {
public constructor(public quantity: number, public potentialItemNames: string[], public locale: string) public constructor(
{ public quantity: number,
} public potentialItemNames: string[],
public locale: string,
)
{}
} }

View File

@ -37,8 +37,7 @@ export class ProfileSptCommand implements ISptCommand
@inject("DatabaseServer") protected databaseServer: DatabaseServer, @inject("DatabaseServer") protected databaseServer: DatabaseServer,
@inject("ProfileHelper") protected profileHelper: ProfileHelper, @inject("ProfileHelper") protected profileHelper: ProfileHelper,
) )
{ {}
}
public getCommand(): string public getCommand(): string
{ {
@ -57,7 +56,7 @@ export class ProfileSptCommand implements ISptCommand
this.mailSendService.sendUserMessageToPlayer( this.mailSendService.sendUserMessageToPlayer(
sessionId, sessionId,
commandHandler, commandHandler,
"Invalid use of trader command. Use \"help\" for more information.", 'Invalid use of trader command. Use "help" for more information.',
); );
return request.dialogId; return request.dialogId;
} }
@ -77,7 +76,7 @@ export class ProfileSptCommand implements ISptCommand
this.mailSendService.sendUserMessageToPlayer( this.mailSendService.sendUserMessageToPlayer(
sessionId, sessionId,
commandHandler, commandHandler,
"Invalid use of profile command, the level was outside bounds: 1 to 70. Use \"help\" for more information.", 'Invalid use of profile command, the level was outside bounds: 1 to 70. Use "help" for more information.',
); );
return request.dialogId; return request.dialogId;
} }
@ -85,8 +84,8 @@ export class ProfileSptCommand implements ISptCommand
break; break;
case "skill": case "skill":
{ {
const enumSkill = Object.values(SkillTypes).find(t => const enumSkill = Object.values(SkillTypes).find(
t.toLocaleLowerCase() === skill.toLocaleLowerCase(), (t) => t.toLocaleLowerCase() === skill.toLocaleLowerCase(),
); );
if (enumSkill === undefined) if (enumSkill === undefined)
@ -94,7 +93,7 @@ export class ProfileSptCommand implements ISptCommand
this.mailSendService.sendUserMessageToPlayer( this.mailSendService.sendUserMessageToPlayer(
sessionId, sessionId,
commandHandler, commandHandler,
"Invalid use of profile command, the skill was not found. Use \"help\" for more information.", 'Invalid use of profile command, the skill was not found. Use "help" for more information.',
); );
return request.dialogId; return request.dialogId;
} }
@ -104,7 +103,7 @@ export class ProfileSptCommand implements ISptCommand
this.mailSendService.sendUserMessageToPlayer( this.mailSendService.sendUserMessageToPlayer(
sessionId, sessionId,
commandHandler, commandHandler,
"Invalid use of profile command, the skill level was outside bounds: 1 to 51. Use \"help\" for more information.", 'Invalid use of profile command, the skill level was outside bounds: 1 to 51. Use "help" for more information.',
); );
return request.dialogId; return request.dialogId;
} }
@ -123,13 +122,15 @@ export class ProfileSptCommand implements ISptCommand
this.mailSendService.sendSystemMessageToPlayer( this.mailSendService.sendSystemMessageToPlayer(
sessionId, sessionId,
"A single ruble is being attached, required by BSG logic.", "A single ruble is being attached, required by BSG logic.",
[{ [
_id: this.hashUtil.generate(), {
_tpl: "5449016a4bdc2d6f028b456f", _id: this.hashUtil.generate(),
upd: { StackObjectsCount: 1 }, _tpl: "5449016a4bdc2d6f028b456f",
parentId: this.hashUtil.generate(), upd: { StackObjectsCount: 1 },
slotId: "main", parentId: this.hashUtil.generate(),
}], slotId: "main",
},
],
undefined, undefined,
[event], [event],
); );

View File

@ -33,8 +33,7 @@ export class TraderSptCommand implements ISptCommand
@inject("LocaleService") protected localeService: LocaleService, @inject("LocaleService") protected localeService: LocaleService,
@inject("DatabaseServer") protected databaseServer: DatabaseServer, @inject("DatabaseServer") protected databaseServer: DatabaseServer,
) )
{ {}
}
public getCommand(): string public getCommand(): string
{ {
@ -53,7 +52,7 @@ export class TraderSptCommand implements ISptCommand
this.mailSendService.sendUserMessageToPlayer( this.mailSendService.sendUserMessageToPlayer(
sessionId, sessionId,
commandHandler, commandHandler,
"Invalid use of trader command. Use \"help\" for more information.", 'Invalid use of trader command. Use "help" for more information.',
); );
return request.dialogId; return request.dialogId;
} }
@ -64,15 +63,15 @@ export class TraderSptCommand implements ISptCommand
const command: string = result.groups.command; const command: string = result.groups.command;
const quantity: number = +result.groups.quantity; const quantity: number = +result.groups.quantity;
const dbTrader = Object.values(this.databaseServer.getTables().traders).find(t => const dbTrader = Object.values(this.databaseServer.getTables().traders).find(
t.base.nickname.toLocaleLowerCase() === trader.toLocaleLowerCase(), (t) => t.base.nickname.toLocaleLowerCase() === trader.toLocaleLowerCase(),
); );
if (dbTrader === undefined) if (dbTrader === undefined)
{ {
this.mailSendService.sendUserMessageToPlayer( this.mailSendService.sendUserMessageToPlayer(
sessionId, sessionId,
commandHandler, commandHandler,
"Invalid use of trader command, the trader was not found. Use \"help\" for more information.", 'Invalid use of trader command, the trader was not found. Use "help" for more information.',
); );
return request.dialogId; return request.dialogId;
} }
@ -97,13 +96,15 @@ export class TraderSptCommand implements ISptCommand
this.mailSendService.sendSystemMessageToPlayer( this.mailSendService.sendSystemMessageToPlayer(
sessionId, sessionId,
"A single ruble is being attached, required by BSG logic.", "A single ruble is being attached, required by BSG logic.",
[{ [
_id: this.hashUtil.generate(), {
_tpl: "5449016a4bdc2d6f028b456f", _id: this.hashUtil.generate(),
upd: { StackObjectsCount: 1 }, _tpl: "5449016a4bdc2d6f028b456f",
parentId: this.hashUtil.generate(), upd: { StackObjectsCount: 1 },
slotId: "main", parentId: this.hashUtil.generate(),
}], slotId: "main",
},
],
undefined, undefined,
[event], [event],
); );

View File

@ -66,7 +66,7 @@ export class DialogueHelper
const dialogueData = this.saveServer.getProfile(sessionID).dialogues; const dialogueData = this.saveServer.getProfile(sessionID).dialogues;
for (const dialogueId in dialogueData) for (const dialogueId in dialogueData)
{ {
const message = dialogueData[dialogueId].messages.find(x => x._id === messageID); const message = dialogueData[dialogueId].messages.find((x) => x._id === messageID);
if (!message) if (!message)
{ {
continue; continue;
@ -87,7 +87,7 @@ export class DialogueHelper
message.items.data = []; message.items.data = [];
} }
const rewardItemCount = message.items.data?.filter(item => item._id !== itemId); const rewardItemCount = message.items.data?.filter((item) => item._id !== itemId);
if (rewardItemCount.length === 0) if (rewardItemCount.length === 0)
{ {
message.rewardCollected = true; message.rewardCollected = true;

View File

@ -172,7 +172,7 @@ export class DurabilityLimitsHelper
const delta = this.randomUtil.getInt(minDelta, maxDelta); const delta = this.randomUtil.getInt(minDelta, maxDelta);
const result = Number((maxDurability - delta).toFixed(2)); const result = Number((maxDurability - delta).toFixed(2));
const durabilityValueMinLimit = Math.round( const durabilityValueMinLimit = Math.round(
this.getMinWeaponLimitPercentFromConfig(botRole) / 100 * maxDurability, (this.getMinWeaponLimitPercentFromConfig(botRole) / 100) * maxDurability,
); );
// Dont let weapon dura go below the percent defined in config // Dont let weapon dura go below the percent defined in config
@ -186,7 +186,7 @@ export class DurabilityLimitsHelper
const delta = this.randomUtil.getInt(minDelta, maxDelta); const delta = this.randomUtil.getInt(minDelta, maxDelta);
const result = Number((maxDurability - delta).toFixed(2)); const result = Number((maxDurability - delta).toFixed(2));
const durabilityValueMinLimit = Math.round( const durabilityValueMinLimit = Math.round(
this.getMinArmorLimitPercentFromConfig(botRole) / 100 * maxDurability, (this.getMinArmorLimitPercentFromConfig(botRole) / 100) * maxDurability,
); );
// Dont let armor dura go below the percent defined in config // Dont let armor dura go below the percent defined in config

View File

@ -56,9 +56,9 @@ export class HandbookHelper
// Add handbook overrides found in items.json config into db // Add handbook overrides found in items.json config into db
for (const itemTpl in this.itemConfig.handbookPriceOverride) for (const itemTpl in this.itemConfig.handbookPriceOverride)
{ {
let itemToUpdate = this.databaseServer.getTables().templates.handbook.Items.find(item => let itemToUpdate = this.databaseServer
item.Id === itemTpl, .getTables()
); .templates.handbook.Items.find((item) => item.Id === itemTpl);
if (!itemToUpdate) if (!itemToUpdate)
{ {
this.databaseServer.getTables().templates.handbook.Items.push({ this.databaseServer.getTables().templates.handbook.Items.push({
@ -66,9 +66,9 @@ export class HandbookHelper
ParentId: this.databaseServer.getTables().templates.items[itemTpl]._parent, ParentId: this.databaseServer.getTables().templates.items[itemTpl]._parent,
Price: this.itemConfig.handbookPriceOverride[itemTpl], Price: this.itemConfig.handbookPriceOverride[itemTpl],
}); });
itemToUpdate = this.databaseServer.getTables().templates.handbook.Items.find(item => itemToUpdate = this.databaseServer
item.Id === itemTpl, .getTables()
); .templates.handbook.Items.find((item) => item.Id === itemTpl);
} }
itemToUpdate.Price = this.itemConfig.handbookPriceOverride[itemTpl]; itemToUpdate.Price = this.itemConfig.handbookPriceOverride[itemTpl];
@ -118,7 +118,7 @@ export class HandbookHelper
return this.handbookPriceCache.items.byId.get(tpl); return this.handbookPriceCache.items.byId.get(tpl);
} }
const handbookItem = this.databaseServer.getTables().templates.handbook.Items.find(x => x.Id === tpl); const handbookItem = this.databaseServer.getTables().templates.handbook.Items.find((x) => x.Id === tpl);
if (!handbookItem) if (!handbookItem)
{ {
const newValue = 0; const newValue = 0;
@ -207,6 +207,6 @@ export class HandbookHelper
public getCategoryById(handbookId: string): Category public getCategoryById(handbookId: string): Category
{ {
return this.databaseServer.getTables().templates.handbook.Categories.find(x => x.Id === handbookId); return this.databaseServer.getTables().templates.handbook.Categories.find((x) => x.Id === handbookId);
} }
} }

View File

@ -36,7 +36,8 @@ export class HealthHelper
const profile = this.saveServer.getProfile(sessionID); const profile = this.saveServer.getProfile(sessionID);
if (!profile.vitality) if (!profile.vitality)
{ // Occurs on newly created profiles {
// Occurs on newly created profiles
profile.vitality = { health: null, effects: null }; profile.vitality = { health: null, effects: null };
} }
profile.vitality.health = { profile.vitality.health = {
@ -99,13 +100,14 @@ export class HealthHelper
profileEffects[bodyPart] = postRaidBodyParts[bodyPart].Effects; profileEffects[bodyPart] = postRaidBodyParts[bodyPart].Effects;
} }
if (request.IsAlive === true) if (request.IsAlive === true)
{ // is player alive, not is limb alive {
// is player alive, not is limb alive
profileHealth[bodyPart] = postRaidBodyParts[bodyPart].Current; profileHealth[bodyPart] = postRaidBodyParts[bodyPart].Current;
} }
else else
{ {
profileHealth[bodyPart] = pmcData.Health.BodyParts[bodyPart].Health.Maximum profileHealth[bodyPart]
* this.healthConfig.healthMultipliers.death; = pmcData.Health.BodyParts[bodyPart].Health.Maximum * this.healthConfig.healthMultipliers.death;
} }
} }

View File

@ -72,9 +72,9 @@ export class HideoutHelper
sessionID: string, sessionID: string,
): IItemEventRouterResponse ): IItemEventRouterResponse
{ {
const recipe = this.databaseServer.getTables().hideout.production.find(production => const recipe = this.databaseServer
production._id === body.recipeId, .getTables()
); .hideout.production.find((production) => production._id === body.recipeId);
if (!recipe) if (!recipe)
{ {
this.logger.error(this.localisationService.getText("hideout-missing_recipe_in_db", body.recipeId)); this.logger.error(this.localisationService.getText("hideout-missing_recipe_in_db", body.recipeId));
@ -106,7 +106,7 @@ export class HideoutHelper
for (const tool of bodyAsSingle.tools) for (const tool of bodyAsSingle.tools)
{ {
const toolItem = this.cloner.clone(pmcData.Inventory.items.find(x => x._id === tool.id)); const toolItem = this.cloner.clone(pmcData.Inventory.items.find((x) => x._id === tool.id));
// Make sure we only return as many as we took // Make sure we only return as many as we took
this.itemHelper.addUpdObjectToItem(toolItem); this.itemHelper.addUpdObjectToItem(toolItem);
@ -168,7 +168,7 @@ export class HideoutHelper
case BonusType.STASH_SIZE: case BonusType.STASH_SIZE:
{ {
// Find stash item and adjust tpl to new tpl from bonus // Find stash item and adjust tpl to new tpl from bonus
const stashItem = pmcData.Inventory.items.find(x => x._id === pmcData.Inventory.stash); const stashItem = pmcData.Inventory.items.find((x) => x._id === pmcData.Inventory.stash);
if (!stashItem) if (!stashItem)
{ {
this.logger.warning( this.logger.warning(
@ -216,18 +216,20 @@ export class HideoutHelper
* @param pmcData Player profile * @param pmcData Player profile
* @returns Properties * @returns Properties
*/ */
protected getHideoutProperties( protected getHideoutProperties(pmcData: IPmcData): {
pmcData: IPmcData, btcFarmCGs: number
): { btcFarmCGs: number, isGeneratorOn: boolean, waterCollectorHasFilter: boolean } isGeneratorOn: boolean
waterCollectorHasFilter: boolean
}
{ {
const bitcoinFarm = pmcData.Hideout.Areas.find(area => area.type === HideoutAreas.BITCOIN_FARM); const bitcoinFarm = pmcData.Hideout.Areas.find((area) => area.type === HideoutAreas.BITCOIN_FARM);
const bitcoinCount = bitcoinFarm?.slots.filter(slot => slot.item).length ?? 0; // Get slots with an item property const bitcoinCount = bitcoinFarm?.slots.filter((slot) => slot.item).length ?? 0; // Get slots with an item property
const hideoutProperties = { const hideoutProperties = {
btcFarmCGs: bitcoinCount, btcFarmCGs: bitcoinCount,
isGeneratorOn: pmcData.Hideout.Areas.find(area => area.type === HideoutAreas.GENERATOR)?.active ?? false, isGeneratorOn: pmcData.Hideout.Areas.find((area) => area.type === HideoutAreas.GENERATOR)?.active ?? false,
waterCollectorHasFilter: this.doesWaterCollectorHaveFilter( waterCollectorHasFilter: this.doesWaterCollectorHaveFilter(
pmcData.Hideout.Areas.find(area => area.type === HideoutAreas.WATER_COLLECTOR), pmcData.Hideout.Areas.find((area) => area.type === HideoutAreas.WATER_COLLECTOR),
), ),
}; };
@ -240,7 +242,7 @@ export class HideoutHelper
if (waterCollector.level === 3) if (waterCollector.level === 3)
{ {
// Has filter in at least one slot // Has filter in at least one slot
return waterCollector.slots.some(slot => slot.item); return waterCollector.slots.some((slot) => slot.item);
} }
// No Filter // No Filter
@ -308,7 +310,7 @@ export class HideoutHelper
} }
// Other recipes not covered by above // Other recipes not covered by above
const recipe = recipes.find(r => r._id === prodId); const recipe = recipes.find((r) => r._id === prodId);
if (!recipe) if (!recipe)
{ {
this.logger.error(this.localisationService.getText("hideout-missing_recipe_for_area", prodId)); this.logger.error(this.localisationService.getText("hideout-missing_recipe_for_area", prodId));
@ -364,9 +366,8 @@ export class HideoutHelper
// Increment progress by time passed // Increment progress by time passed
const production = pmcData.Hideout.Production[prodId]; const production = pmcData.Hideout.Production[prodId];
production.Progress += production.needFuelForAllProductionTime && !hideoutProperties.isGeneratorOn production.Progress
? 0 += production.needFuelForAllProductionTime && !hideoutProperties.isGeneratorOn ? 0 : timeElapsed; // Some items NEED power to craft (e.g. DSP)
: timeElapsed; // Some items NEED power to craft (e.g. DSP)
// Limit progress to total production time if progress is over (dont run for continious crafts)) // Limit progress to total production time if progress is over (dont run for continious crafts))
if (!recipe.continuous) if (!recipe.continuous)
@ -395,8 +396,10 @@ export class HideoutHelper
*/ */
protected updateScavCaseProductionTimer(pmcData: IPmcData, productionId: string): void protected updateScavCaseProductionTimer(pmcData: IPmcData, productionId: string): void
{ {
const timeElapsed = this.timeUtil.getTimestamp() - pmcData.Hideout.Production[productionId].StartTimestamp const timeElapsed
- pmcData.Hideout.Production[productionId].Progress; = this.timeUtil.getTimestamp()
- pmcData.Hideout.Production[productionId].StartTimestamp
- pmcData.Hideout.Production[productionId].Progress;
pmcData.Hideout.Production[productionId].Progress += timeElapsed; pmcData.Hideout.Production[productionId].Progress += timeElapsed;
} }
@ -446,10 +449,11 @@ export class HideoutHelper
{ {
// 1 resource last 14 min 27 sec, 1/14.45/60 = 0.00115 // 1 resource last 14 min 27 sec, 1/14.45/60 = 0.00115
// 10-10-2021 From wiki, 1 resource last 12 minutes 38 seconds, 1/12.63333/60 = 0.00131 // 10-10-2021 From wiki, 1 resource last 12 minutes 38 seconds, 1/12.63333/60 = 0.00131
let fuelUsedSinceLastTick = this.databaseServer.getTables().hideout.settings.generatorFuelFlowRate let fuelUsedSinceLastTick
* this.getTimeElapsedSinceLastServerTick(pmcData, isGeneratorOn); = this.databaseServer.getTables().hideout.settings.generatorFuelFlowRate
* this.getTimeElapsedSinceLastServerTick(pmcData, isGeneratorOn);
const profileFuelConsumptionBonus = pmcData.Bonuses.find(bonus => bonus.type === BonusType.FUEL_CONSUMPTION); const profileFuelConsumptionBonus = pmcData.Bonuses.find((bonus) => bonus.type === BonusType.FUEL_CONSUMPTION);
// 0 to 1 // 0 to 1
const fuelConsumptionBonusMultipler const fuelConsumptionBonusMultipler
@ -602,9 +606,9 @@ export class HideoutHelper
{ {
const globalSkillsDb = this.databaseServer.getTables().globals.config.SkillsSettings; const globalSkillsDb = this.databaseServer.getTables().globals.config.SkillsSettings;
const recipe = this.databaseServer.getTables().hideout.production.find(production => const recipe = this.databaseServer
production._id === recipeId, .getTables()
); .hideout.production.find((production) => production._id === recipeId);
if (!recipe) if (!recipe)
{ {
this.logger.error(this.localisationService.getText("hideout-missing_recipe_in_db", recipeId)); this.logger.error(this.localisationService.getText("hideout-missing_recipe_in_db", recipeId));
@ -754,9 +758,10 @@ export class HideoutHelper
baseFilterDrainRate: number, baseFilterDrainRate: number,
): number ): number
{ {
const drainTimeSeconds = secondsSinceServerTick > totalProductionTime const drainTimeSeconds
? totalProductionTime - productionProgress // More time passed than prod time, get total minus the current progress = secondsSinceServerTick > totalProductionTime
: secondsSinceServerTick; ? totalProductionTime - productionProgress // More time passed than prod time, get total minus the current progress
: secondsSinceServerTick;
// Multiply base drain rate by time passed // Multiply base drain rate by time passed
return baseFilterDrainRate * drainTimeSeconds; return baseFilterDrainRate * drainTimeSeconds;
@ -786,9 +791,10 @@ export class HideoutHelper
); );
// Never let bonus become 0 // Never let bonus become 0
const reductionBonus = hideoutManagementConsumptionBonus + craftSkillTimeReductionMultipler === 0 const reductionBonus
? 1 = hideoutManagementConsumptionBonus + craftSkillTimeReductionMultipler === 0
: 1 - (hideoutManagementConsumptionBonus + craftSkillTimeReductionMultipler); ? 1
: 1 - (hideoutManagementConsumptionBonus + craftSkillTimeReductionMultipler);
return filterDrainRate * reductionBonus; return filterDrainRate * reductionBonus;
} }
@ -800,7 +806,7 @@ export class HideoutHelper
*/ */
protected getTotalProductionTimeSeconds(prodId: string): number protected getTotalProductionTimeSeconds(prodId: string): number
{ {
const recipe = this.databaseServer.getTables().hideout.production.find(prod => prod._id === prodId); const recipe = this.databaseServer.getTables().hideout.production.find((prod) => prod._id === prodId);
return recipe.productionTime || 0; return recipe.productionTime || 0;
} }
@ -833,8 +839,9 @@ export class HideoutHelper
Lasts for 17 hours 38 minutes and 49 seconds (23 hours 31 minutes and 45 seconds with elite hideout management skill), Lasts for 17 hours 38 minutes and 49 seconds (23 hours 31 minutes and 45 seconds with elite hideout management skill),
300/17.64694/60/60 = 0.004722 300/17.64694/60/60 = 0.004722
*/ */
let filterDrainRate = this.databaseServer.getTables().hideout.settings.airFilterUnitFlowRate let filterDrainRate
* this.getTimeElapsedSinceLastServerTick(pmcData, isGeneratorOn); = this.databaseServer.getTables().hideout.settings.airFilterUnitFlowRate
* this.getTimeElapsedSinceLastServerTick(pmcData, isGeneratorOn);
// Hideout management resource consumption bonus: // Hideout management resource consumption bonus:
const hideoutManagementConsumptionBonus = 1.0 - this.getHideoutManagementConsumptionBonus(pmcData); const hideoutManagementConsumptionBonus = 1.0 - this.getHideoutManagementConsumptionBonus(pmcData);
@ -888,9 +895,9 @@ export class HideoutHelper
protected updateBitcoinFarm(pmcData: IPmcData, btcFarmCGs: number, isGeneratorOn: boolean): Production protected updateBitcoinFarm(pmcData: IPmcData, btcFarmCGs: number, isGeneratorOn: boolean): Production
{ {
const btcProd = pmcData.Hideout.Production[HideoutHelper.bitcoinFarm]; const btcProd = pmcData.Hideout.Production[HideoutHelper.bitcoinFarm];
const bitcoinProdData = this.databaseServer.getTables().hideout.production.find(production => const bitcoinProdData = this.databaseServer
production._id === HideoutHelper.bitcoinProductionId, .getTables()
); .hideout.production.find((production) => production._id === HideoutHelper.bitcoinProductionId);
const coinSlotCount = this.getBTCSlots(pmcData); const coinSlotCount = this.getBTCSlots(pmcData);
// Full on bitcoins, halt progress // Full on bitcoins, halt progress
@ -1024,9 +1031,9 @@ export class HideoutHelper
*/ */
protected getBTCSlots(pmcData: IPmcData): number protected getBTCSlots(pmcData: IPmcData): number
{ {
const bitcoinProductions = this.databaseServer.getTables().hideout.production.find(production => const bitcoinProductions = this.databaseServer
production._id === HideoutHelper.bitcoinFarm, .getTables()
); .hideout.production.find((production) => production._id === HideoutHelper.bitcoinFarm);
const productionSlots = bitcoinProductions?.productionLimitCount || 3; // Default to 3 if none found const productionSlots = bitcoinProductions?.productionLimitCount || 3; // Default to 3 if none found
const hasManagementSkillSlots = this.profileHelper.hasEliteSkillLevel(SkillTypes.HIDEOUT_MANAGEMENT, pmcData); const hasManagementSkillSlots = this.profileHelper.hasEliteSkillLevel(SkillTypes.HIDEOUT_MANAGEMENT, pmcData);
const managementSlotsCount = this.getEliteSkillAdditionalBitcoinSlotCount() || 2; const managementSlotsCount = this.getEliteSkillAdditionalBitcoinSlotCount() || 2;
@ -1063,9 +1070,12 @@ export class HideoutHelper
let roundedLevel = Math.floor(hideoutManagementSkill.Progress / 100); let roundedLevel = Math.floor(hideoutManagementSkill.Progress / 100);
roundedLevel = roundedLevel === 51 ? roundedLevel - 1 : roundedLevel; roundedLevel = roundedLevel === 51 ? roundedLevel - 1 : roundedLevel;
return roundedLevel return (
* this.databaseServer.getTables().globals.config.SkillsSettings.HideoutManagement (roundedLevel
.ConsumptionReductionPerLevel / 100; * this.databaseServer.getTables().globals.config.SkillsSettings.HideoutManagement
.ConsumptionReductionPerLevel)
/ 100
);
} }
/** /**
@ -1089,7 +1099,7 @@ export class HideoutHelper
let roundedLevel = Math.floor(profileSkill.Progress / 100); let roundedLevel = Math.floor(profileSkill.Progress / 100);
roundedLevel = roundedLevel === 51 ? roundedLevel - 1 : roundedLevel; roundedLevel = roundedLevel === 51 ? roundedLevel - 1 : roundedLevel;
return roundedLevel * valuePerLevel / 100; return (roundedLevel * valuePerLevel) / 100;
} }
/** /**
@ -1146,11 +1156,13 @@ export class HideoutHelper
const itemsToAdd: Item[][] = []; const itemsToAdd: Item[][] = [];
for (let index = 0; index < craftedCoinCount; index++) for (let index = 0; index < craftedCoinCount; index++)
{ {
itemsToAdd.push([{ itemsToAdd.push([
_id: this.hashUtil.generate(), {
_tpl: HideoutHelper.bitcoinTpl, _id: this.hashUtil.generate(),
upd: { StackObjectsCount: 1 }, _tpl: HideoutHelper.bitcoinTpl,
}]); upd: { StackObjectsCount: 1 },
},
]);
} }
// Create request for what we want to add to stash // Create request for what we want to add to stash
@ -1187,9 +1199,9 @@ export class HideoutHelper
*/ */
public unlockHideoutWallInProfile(pmcProfile: IPmcData): void public unlockHideoutWallInProfile(pmcProfile: IPmcData): void
{ {
const waterCollector = pmcProfile.Hideout.Areas.find(x => x.type === HideoutAreas.WATER_COLLECTOR); const waterCollector = pmcProfile.Hideout.Areas.find((x) => x.type === HideoutAreas.WATER_COLLECTOR);
const medStation = pmcProfile.Hideout.Areas.find(x => x.type === HideoutAreas.MEDSTATION); const medStation = pmcProfile.Hideout.Areas.find((x) => x.type === HideoutAreas.MEDSTATION);
const wall = pmcProfile.Hideout.Areas.find(x => x.type === HideoutAreas.EMERGENCY_WALL); const wall = pmcProfile.Hideout.Areas.find((x) => x.type === HideoutAreas.EMERGENCY_WALL);
// No collector or med station, skip // No collector or med station, skip
if (!(waterCollector && medStation)) if (!(waterCollector && medStation))
@ -1239,29 +1251,29 @@ export class HideoutHelper
*/ */
public applyPlaceOfFameDogtagBonus(pmcData: IPmcData): void public applyPlaceOfFameDogtagBonus(pmcData: IPmcData): void
{ {
const fameAreaProfile = pmcData.Hideout.Areas.find(area => area.type === HideoutAreas.PLACE_OF_FAME); const fameAreaProfile = pmcData.Hideout.Areas.find((area) => area.type === HideoutAreas.PLACE_OF_FAME);
// Get hideout area 16 bonus array // Get hideout area 16 bonus array
const fameAreaDb = this.databaseServer.getTables().hideout.areas.find(area => const fameAreaDb = this.databaseServer
area.type === HideoutAreas.PLACE_OF_FAME, .getTables()
); .hideout.areas.find((area) => area.type === HideoutAreas.PLACE_OF_FAME);
// Get SkillGroupLevelingBoost object // Get SkillGroupLevelingBoost object
const combatBoostBonusDb = fameAreaDb.stages[fameAreaProfile.level].bonuses.find(bonus => const combatBoostBonusDb = fameAreaDb.stages[fameAreaProfile.level].bonuses.find(
bonus.type === "SkillGroupLevelingBoost", (bonus) => bonus.type === "SkillGroupLevelingBoost",
); );
// Get SkillGroupLevelingBoost object in profile // Get SkillGroupLevelingBoost object in profile
const combatBonusProfile = pmcData.Bonuses.find(bonus => bonus.id === combatBoostBonusDb.id); const combatBonusProfile = pmcData.Bonuses.find((bonus) => bonus.id === combatBoostBonusDb.id);
// Get all slotted dogtag items // Get all slotted dogtag items
const activeDogtags = pmcData.Inventory.items.filter(item => item?.slotId?.startsWith("dogtag")); const activeDogtags = pmcData.Inventory.items.filter((item) => item?.slotId?.startsWith("dogtag"));
// Calculate bonus percent (apply hideoutManagement bonus) // Calculate bonus percent (apply hideoutManagement bonus)
const hideoutManagementSkill = this.profileHelper.getSkillFromProfile(pmcData, SkillTypes.HIDEOUT_MANAGEMENT); const hideoutManagementSkill = this.profileHelper.getSkillFromProfile(pmcData, SkillTypes.HIDEOUT_MANAGEMENT);
const hideoutManagementSkillBonusPercent = 1 + hideoutManagementSkill.Progress / 10000; // 5100 becomes 0.51, add 1 to it, 1.51 const hideoutManagementSkillBonusPercent = 1 + hideoutManagementSkill.Progress / 10000; // 5100 becomes 0.51, add 1 to it, 1.51
const bonus = this.getDogtagCombatSkillBonusPercent(pmcData, activeDogtags) const bonus
* hideoutManagementSkillBonusPercent; = this.getDogtagCombatSkillBonusPercent(pmcData, activeDogtags) * hideoutManagementSkillBonusPercent;
// Update bonus value to above calcualted value // Update bonus value to above calcualted value
combatBonusProfile.value = Number.parseFloat(bonus.toFixed(2)); combatBonusProfile.value = Number.parseFloat(bonus.toFixed(2));

View File

@ -4,7 +4,7 @@ import { ItemHelper } from "@spt-aki/helpers/ItemHelper";
import { PaymentHelper } from "@spt-aki/helpers/PaymentHelper"; import { PaymentHelper } from "@spt-aki/helpers/PaymentHelper";
import { QuestHelper } from "@spt-aki/helpers/QuestHelper"; import { QuestHelper } from "@spt-aki/helpers/QuestHelper";
import { IPmcData, IPostRaidPmcData } from "@spt-aki/models/eft/common/IPmcData"; import { IPmcData, IPostRaidPmcData } from "@spt-aki/models/eft/common/IPmcData";
import { IQuestStatus, TraderInfo, Victim } from "@spt-aki/models/eft/common/tables/IBotBase"; import { IQuestStatus, TraderInfo } from "@spt-aki/models/eft/common/tables/IBotBase";
import { Item } from "@spt-aki/models/eft/common/tables/IItem"; import { Item } from "@spt-aki/models/eft/common/tables/IItem";
import { ISaveProgressRequestData } from "@spt-aki/models/eft/inRaid/ISaveProgressRequestData"; import { ISaveProgressRequestData } from "@spt-aki/models/eft/inRaid/ISaveProgressRequestData";
import { IFailQuestRequestData } from "@spt-aki/models/eft/quests/IFailQuestRequestData"; import { IFailQuestRequestData } from "@spt-aki/models/eft/quests/IFailQuestRequestData";
@ -67,7 +67,7 @@ export class InRaidHelper
*/ */
public addUpdToMoneyFromRaid(items: Item[]): void public addUpdToMoneyFromRaid(items: Item[]): void
{ {
for (const moneyItem of items.filter(item => this.paymentHelper.isMoneyTpl(item._tpl))) for (const moneyItem of items.filter((item) => this.paymentHelper.isMoneyTpl(item._tpl)))
{ {
this.itemHelper.addUpdObjectToItem(moneyItem); this.itemHelper.addUpdObjectToItem(moneyItem);
@ -128,7 +128,10 @@ export class InRaidHelper
} }
/** Check counters are correct in profile */ /** Check counters are correct in profile */
protected validateTaskConditionCounters(saveProgressRequest: ISaveProgressRequestData, profileData: IPmcData): void protected validateTaskConditionCounters(
saveProgressRequest: ISaveProgressRequestData,
profileData: IPmcData,
): void
{ {
for (const backendCounterKey in saveProgressRequest.profile.TaskConditionCounters) for (const backendCounterKey in saveProgressRequest.profile.TaskConditionCounters)
{ {
@ -203,12 +206,12 @@ export class InRaidHelper
): void ): void
{ {
// Only copy active quests into scav profile // Progress will later to copied over to PMC profile // Only copy active quests into scav profile // Progress will later to copied over to PMC profile
const existingActiveQuestIds = scavData.Quests?.filter( const existingActiveQuestIds = scavData.Quests?.filter((x) => x.status !== QuestStatus.AvailableForStart).map(
x => x.status !== QuestStatus.AvailableForStart, (x) => x.qid,
).map(x => x.qid); );
if (existingActiveQuestIds) if (existingActiveQuestIds)
{ {
scavData.Quests = saveProgressRequest.profile.Quests.filter(x => existingActiveQuestIds.includes(x.qid)); scavData.Quests = saveProgressRequest.profile.Quests.filter((x) => existingActiveQuestIds.includes(x.qid));
} }
this.profileFixerService.checkForAndFixScavProfileIssues(scavData); this.profileFixerService.checkForAndFixScavProfileIssues(scavData);
@ -240,10 +243,10 @@ export class InRaidHelper
for (const postRaidQuest of postRaidProfile.Quests) for (const postRaidQuest of postRaidProfile.Quests)
{ {
// postRaidQuest.status has a weird value, need to do some nasty casting to compare it // postRaidQuest.status has a weird value, need to do some nasty casting to compare it
const postRaidQuestStatus = <string><unknown>postRaidQuest.status; const postRaidQuestStatus = <string>(<unknown>postRaidQuest.status);
// Find matching pre-raid quest, skip if we can't // Find matching pre-raid quest, skip if we can't
const preRaidQuest = preRaidQuests?.find(preRaidQuest => preRaidQuest.qid === postRaidQuest.qid); const preRaidQuest = preRaidQuests?.find((preRaidQuest) => preRaidQuest.qid === postRaidQuest.qid);
if (!preRaidQuest) if (!preRaidQuest)
{ {
continue; continue;
@ -269,7 +272,8 @@ export class InRaidHelper
// Quest with time-gate has unlocked // Quest with time-gate has unlocked
if ( if (
postRaidQuestStatus === "AvailableAfter" && postRaidQuest.availableAfter <= this.timeUtil.getTimestamp() postRaidQuestStatus === "AvailableAfter"
&& postRaidQuest.availableAfter <= this.timeUtil.getTimestamp()
) )
{ {
// Flag as ready to start // Flag as ready to start
@ -298,8 +302,8 @@ export class InRaidHelper
// Does failed quest have requirement to collect items from raid // Does failed quest have requirement to collect items from raid
const questDbData = this.questHelper.getQuestFromDb(postRaidQuest.qid, pmcData); const questDbData = this.questHelper.getQuestFromDb(postRaidQuest.qid, pmcData);
// AvailableForFinish // AvailableForFinish
const matchingAffFindConditions = questDbData.conditions.AvailableForFinish.filter(condition => const matchingAffFindConditions = questDbData.conditions.AvailableForFinish.filter(
condition.conditionType === "FindItem", (condition) => condition.conditionType === "FindItem",
); );
const itemsToCollect: string[] = []; const itemsToCollect: string[] = [];
if (matchingAffFindConditions) if (matchingAffFindConditions)
@ -314,7 +318,7 @@ export class InRaidHelper
// Remove quest items from profile as quest has failed and may still be alive // Remove quest items from profile as quest has failed and may still be alive
// Required as restarting the quest from main menu does not remove value from CarriedQuestItems array // Required as restarting the quest from main menu does not remove value from CarriedQuestItems array
postRaidProfile.Stats.Eft.CarriedQuestItems = postRaidProfile.Stats.Eft.CarriedQuestItems.filter( postRaidProfile.Stats.Eft.CarriedQuestItems = postRaidProfile.Stats.Eft.CarriedQuestItems.filter(
carriedQuestItem => !itemsToCollect.includes(carriedQuestItem), (carriedQuestItem) => !itemsToCollect.includes(carriedQuestItem),
); );
// Remove quest item from profile now quest is failed // Remove quest item from profile now quest is failed
@ -322,8 +326,8 @@ export class InRaidHelper
for (const itemTpl of itemsToCollect) for (const itemTpl of itemsToCollect)
{ {
// Look for sessioncounter and remove it // Look for sessioncounter and remove it
const counterIndex = postRaidProfile.Stats.Eft.SessionCounters.Items.findIndex(x => const counterIndex = postRaidProfile.Stats.Eft.SessionCounters.Items.findIndex(
x.Key.includes(itemTpl) && x.Key.includes("LootItem"), (x) => x.Key.includes(itemTpl) && x.Key.includes("LootItem"),
); );
if (counterIndex > -1) if (counterIndex > -1)
{ {
@ -331,7 +335,7 @@ export class InRaidHelper
} }
// Look for quest item and remove it // Look for quest item and remove it
const inventoryItemIndex = postRaidProfile.Inventory.items.findIndex(x => x._tpl === itemTpl); const inventoryItemIndex = postRaidProfile.Inventory.items.findIndex((x) => x._tpl === itemTpl);
if (inventoryItemIndex > -1) if (inventoryItemIndex > -1)
{ {
postRaidProfile.Inventory.items.splice(inventoryItemIndex, 1); postRaidProfile.Inventory.items.splice(inventoryItemIndex, 1);
@ -454,10 +458,15 @@ export class InRaidHelper
const itemsToRemovePropertyFrom = postRaidProfile.Inventory.items.filter((x) => const itemsToRemovePropertyFrom = postRaidProfile.Inventory.items.filter((x) =>
{ {
// Has upd object + upd.SpawnedInSession property + not a quest item // Has upd object + upd.SpawnedInSession property + not a quest item
return "upd" in x && "SpawnedInSession" in x.upd return (
&& !dbItems[x._tpl]._props.QuestItem "upd" in x
&& !(this.inRaidConfig.keepFiRSecureContainerOnDeath && "SpawnedInSession" in x.upd
&& this.itemHelper.itemIsInsideContainer(x, "SecuredContainer", postRaidProfile.Inventory.items)); && !dbItems[x._tpl]._props.QuestItem
&& !(
this.inRaidConfig.keepFiRSecureContainerOnDeath
&& this.itemHelper.itemIsInsideContainer(x, "SecuredContainer", postRaidProfile.Inventory.items)
)
);
}); });
for (const item of itemsToRemovePropertyFrom) for (const item of itemsToRemovePropertyFrom)
@ -502,7 +511,7 @@ export class InRaidHelper
public deleteInventory(pmcData: IPmcData, sessionId: string): void public deleteInventory(pmcData: IPmcData, sessionId: string): void
{ {
// Get inventory item ids to remove from players profile // Get inventory item ids to remove from players profile
const itemIdsToDeleteFromProfile = this.getInventoryItemsLostOnDeath(pmcData).map(item => item._id); const itemIdsToDeleteFromProfile = this.getInventoryItemsLostOnDeath(pmcData).map((item) => item._id);
for (const itemIdToDelete of itemIdsToDeleteFromProfile) for (const itemIdToDelete of itemIdsToDeleteFromProfile)
{ {
// Items inside containers are handled as part of function // Items inside containers are handled as part of function
@ -555,13 +564,13 @@ export class InRaidHelper
*/ */
protected getBaseItemsInRigPocketAndBackpack(pmcData: IPmcData): Item[] protected getBaseItemsInRigPocketAndBackpack(pmcData: IPmcData): Item[]
{ {
const rig = pmcData.Inventory.items.find(x => x.slotId === "TacticalVest"); const rig = pmcData.Inventory.items.find((x) => x.slotId === "TacticalVest");
const pockets = pmcData.Inventory.items.find(x => x.slotId === "Pockets"); const pockets = pmcData.Inventory.items.find((x) => x.slotId === "Pockets");
const backpack = pmcData.Inventory.items.find(x => x.slotId === "Backpack"); const backpack = pmcData.Inventory.items.find((x) => x.slotId === "Backpack");
const baseItemsInRig = pmcData.Inventory.items.filter(x => x.parentId === rig?._id); const baseItemsInRig = pmcData.Inventory.items.filter((x) => x.parentId === rig?._id);
const baseItemsInPockets = pmcData.Inventory.items.filter(x => x.parentId === pockets?._id); const baseItemsInPockets = pmcData.Inventory.items.filter((x) => x.parentId === pockets?._id);
const baseItemsInBackpack = pmcData.Inventory.items.filter(x => x.parentId === backpack?._id); const baseItemsInBackpack = pmcData.Inventory.items.filter((x) => x.parentId === backpack?._id);
return [...baseItemsInRig, ...baseItemsInPockets, ...baseItemsInBackpack]; return [...baseItemsInRig, ...baseItemsInPockets, ...baseItemsInBackpack];
} }

View File

@ -23,7 +23,6 @@ import { BackendErrorCodes } from "@spt-aki/models/enums/BackendErrorCodes";
import { BaseClasses } from "@spt-aki/models/enums/BaseClasses"; import { BaseClasses } from "@spt-aki/models/enums/BaseClasses";
import { BonusType } from "@spt-aki/models/enums/BonusType"; import { BonusType } from "@spt-aki/models/enums/BonusType";
import { ConfigTypes } from "@spt-aki/models/enums/ConfigTypes"; import { ConfigTypes } from "@spt-aki/models/enums/ConfigTypes";
import { Traders } from "@spt-aki/models/enums/Traders";
import { IInventoryConfig, RewardDetails } from "@spt-aki/models/spt/config/IInventoryConfig"; import { IInventoryConfig, RewardDetails } from "@spt-aki/models/spt/config/IInventoryConfig";
import { ILogger } from "@spt-aki/models/spt/utils/ILogger"; import { ILogger } from "@spt-aki/models/spt/utils/ILogger";
import { ConfigServer } from "@spt-aki/servers/ConfigServer"; import { ConfigServer } from "@spt-aki/servers/ConfigServer";
@ -167,9 +166,8 @@ export class InventoryHelper
catch (err) catch (err)
{ {
// Callback failed // Callback failed
const message = typeof err?.message === "string" const message
? err.message = typeof err?.message === "string" ? err.message : this.localisationService.getText("http-unknown_error");
: this.localisationService.getText("http-unknown_error");
this.httpResponse.appendErrorToOutput(output, message); this.httpResponse.appendErrorToOutput(output, message);
@ -520,13 +518,15 @@ export class InventoryHelper
if (requestItem.count > itemDetails._props.StackMaxSize) if (requestItem.count > itemDetails._props.StackMaxSize)
{ {
let remainingCountOfItemToAdd = requestItem.count; let remainingCountOfItemToAdd = requestItem.count;
const calc = requestItem.count const calc
- Math.floor(requestItem.count / itemDetails._props.StackMaxSize) = requestItem.count
* itemDetails._props.StackMaxSize; - Math.floor(requestItem.count / itemDetails._props.StackMaxSize)
* itemDetails._props.StackMaxSize;
maxStackCount = calc > 0 maxStackCount
? maxStackCount + Math.floor(remainingCountOfItemToAdd / itemDetails._props.StackMaxSize) = calc > 0
: Math.floor(remainingCountOfItemToAdd / itemDetails._props.StackMaxSize); ? maxStackCount + Math.floor(remainingCountOfItemToAdd / itemDetails._props.StackMaxSize)
: Math.floor(remainingCountOfItemToAdd / itemDetails._props.StackMaxSize);
// Iterate until totalCountOfPurchasedItem is 0 // Iterate until totalCountOfPurchasedItem is 0
for (let i = 0; i < maxStackCount; i++) for (let i = 0; i < maxStackCount; i++)
@ -597,7 +597,7 @@ export class InventoryHelper
{ {
// We expect that each inventory item and each insured item has unique "_id", respective "itemId". // We expect that each inventory item and each insured item has unique "_id", respective "itemId".
// Therefore we want to use a NON-Greedy function and escape the iteration as soon as we find requested item. // Therefore we want to use a NON-Greedy function and escape the iteration as soon as we find requested item.
const inventoryIndex = inventoryItems.findIndex(item => item._id === childId); const inventoryIndex = inventoryItems.findIndex((item) => item._id === childId);
if (inventoryIndex > -1) if (inventoryIndex > -1)
{ {
inventoryItems.splice(inventoryIndex, 1); inventoryItems.splice(inventoryIndex, 1);
@ -610,7 +610,7 @@ export class InventoryHelper
); );
} }
const insuredIndex = insuredItems.findIndex(item => item.itemId === childId); const insuredIndex = insuredItems.findIndex((item) => item.itemId === childId);
if (insuredIndex > -1) if (insuredIndex > -1)
{ {
insuredItems.splice(insuredIndex, 1); insuredItems.splice(insuredIndex, 1);
@ -636,7 +636,7 @@ export class InventoryHelper
const dialogs = Object.values(fullProfile.dialogues); const dialogs = Object.values(fullProfile.dialogues);
for (const dialog of dialogs) for (const dialog of dialogs)
{ {
const messageWithReward = dialog.messages.find(x => x._id === removeRequest.fromOwner.id); const messageWithReward = dialog.messages.find((x) => x._id === removeRequest.fromOwner.id);
if (messageWithReward) if (messageWithReward)
{ {
// Find item + any possible children and remove them from mails items array // Find item + any possible children and remove them from mails items array
@ -876,7 +876,9 @@ export class InventoryHelper
*/ */
protected getBlankContainerMap(containerH: number, containerY: number): number[][] protected getBlankContainerMap(containerH: number, containerY: number): number[][]
{ {
return Array(containerY).fill(0).map(() => Array(containerH).fill(0)); return Array(containerY)
.fill(0)
.map(() => Array(containerH).fill(0));
} }
/** /**
@ -909,12 +911,14 @@ export class InventoryHelper
const iW = tmpSize[0]; // x const iW = tmpSize[0]; // x
const iH = tmpSize[1]; // y const iH = tmpSize[1]; // y
const fH const fH
= (item.location as Location).r === 1 || (item.location as Location).r === "Vertical" = (item.location as Location).r === 1
|| (item.location as Location).r === "Vertical"
|| (item.location as Location).rotation === "Vertical" || (item.location as Location).rotation === "Vertical"
? iW ? iW
: iH; : iH;
const fW const fW
= (item.location as Location).r === 1 || (item.location as Location).r === "Vertical" = (item.location as Location).r === 1
|| (item.location as Location).r === "Vertical"
|| (item.location as Location).rotation === "Vertical" || (item.location as Location).rotation === "Vertical"
? iH ? iH
: iW; : iW;
@ -1078,7 +1082,7 @@ export class InventoryHelper
protected getPlayerStashSize(sessionID: string): Record<number, number> protected getPlayerStashSize(sessionID: string): Record<number, number>
{ {
const profile = this.profileHelper.getPmcProfile(sessionID); const profile = this.profileHelper.getPmcProfile(sessionID);
const stashRowBonus = profile.Bonuses.find(bonus => bonus.type === BonusType.STASH_ROWS); const stashRowBonus = profile.Bonuses.find((bonus) => bonus.type === BonusType.STASH_ROWS);
// this sets automatically a stash size from items.json (its not added anywhere yet cause we still use base stash) // this sets automatically a stash size from items.json (its not added anywhere yet cause we still use base stash)
const stashTPL = this.getStashType(sessionID); const stashTPL = this.getStashType(sessionID);
@ -1118,7 +1122,7 @@ export class InventoryHelper
protected getStashType(sessionID: string): string protected getStashType(sessionID: string): string
{ {
const pmcData = this.profileHelper.getPmcProfile(sessionID); const pmcData = this.profileHelper.getPmcProfile(sessionID);
const stashObj = pmcData.Inventory.items.find(item => item._id === pmcData.Inventory.stash); const stashObj = pmcData.Inventory.items.find((item) => item._id === pmcData.Inventory.stash);
if (!stashObj) if (!stashObj)
{ {
this.logger.error(this.localisationService.getText("inventory-unable_to_find_stash")); this.logger.error(this.localisationService.getText("inventory-unable_to_find_stash"));
@ -1140,7 +1144,7 @@ export class InventoryHelper
const idsToMove = this.itemHelper.findAndReturnChildrenByItems(fromItems, body.item); const idsToMove = this.itemHelper.findAndReturnChildrenByItems(fromItems, body.item);
for (const itemId of idsToMove) for (const itemId of idsToMove)
{ {
const itemToMove = fromItems.find(x => x._id === itemId); const itemToMove = fromItems.find((x) => x._id === itemId);
if (!itemToMove) if (!itemToMove)
{ {
this.logger.error(`Unable to find item to move: ${itemId}`); this.logger.error(`Unable to find item to move: ${itemId}`);
@ -1188,7 +1192,7 @@ export class InventoryHelper
this.handleCartridges(inventoryItems, moveRequest); this.handleCartridges(inventoryItems, moveRequest);
// Find item we want to 'move' // Find item we want to 'move'
const matchingInventoryItem = inventoryItems.find(x => x._id === moveRequest.item); const matchingInventoryItem = inventoryItems.find((x) => x._id === moveRequest.item);
if (!matchingInventoryItem) if (!matchingInventoryItem)
{ {
const errorMesage = `Unable to move item: ${moveRequest.item}, cannot find in inventory`; const errorMesage = `Unable to move item: ${moveRequest.item}, cannot find in inventory`;
@ -1248,7 +1252,7 @@ export class InventoryHelper
if (pmcData.Inventory.fastPanel[itemKey] === itemBeingMoved._id) if (pmcData.Inventory.fastPanel[itemKey] === itemBeingMoved._id)
{ {
// Get moved items parent // Get moved items parent
const itemParent = pmcData.Inventory.items.find(x => x._id === itemBeingMoved.parentId); const itemParent = pmcData.Inventory.items.find((x) => x._id === itemBeingMoved.parentId);
// Empty out id if item is moved to a container other than pocket/rig // Empty out id if item is moved to a container other than pocket/rig
if (itemParent && !(itemParent.slotId?.startsWith("Pockets") || itemParent.slotId === "TacticalVest")) if (itemParent && !(itemParent.slotId?.startsWith("Pockets") || itemParent.slotId === "TacticalVest"))
@ -1317,7 +1321,7 @@ export class InventoryHelper
return true; return true;
} }
container = pmcData.Inventory.items.find(item => item._id === container.parentId); container = pmcData.Inventory.items.find((item) => item._id === container.parentId);
if (!container) if (!container)
{ {
break; break;

View File

@ -68,7 +68,7 @@ export class ItemHelper
} }
for (const itemOf1 of item1) for (const itemOf1 of item1)
{ {
const itemOf2 = item2.find(i2 => i2._tpl === itemOf1._tpl); const itemOf2 = item2.find((i2) => i2._tpl === itemOf1._tpl);
if (itemOf2 === undefined) if (itemOf2 === undefined)
{ {
return false; return false;
@ -98,7 +98,7 @@ export class ItemHelper
if (compareUpdProperties) if (compareUpdProperties)
{ {
return Array.from(compareUpdProperties.values()).every(p => return Array.from(compareUpdProperties.values()).every((p) =>
this.compareUtil.recursiveCompare(item1.upd?.[p], item2.upd?.[p]), this.compareUtil.recursiveCompare(item1.upd?.[p], item2.upd?.[p]),
); );
} }
@ -194,11 +194,13 @@ export class ItemHelper
return false; return false;
} }
return !itemDetails[1]._props.QuestItem return (
&& itemDetails[1]._type === "Item" !itemDetails[1]._props.QuestItem
&& baseTypes.every(x => !this.isOfBaseclass(tpl, x)) && itemDetails[1]._type === "Item"
&& this.getItemPrice(tpl) > 0 && baseTypes.every((x) => !this.isOfBaseclass(tpl, x))
&& !this.itemFilterService.isItemBlacklisted(tpl); && this.getItemPrice(tpl) > 0
&& !this.itemFilterService.isItemBlacklisted(tpl)
);
} }
/** /**
@ -261,7 +263,7 @@ export class ItemHelper
const itemTemplate = this.getItem(itemTpl); const itemTemplate = this.getItem(itemTpl);
const plateSlotIds = this.getRemovablePlateSlotIds(); const plateSlotIds = this.getRemovablePlateSlotIds();
return itemTemplate[1]._props.Slots.some(slot => plateSlotIds.includes(slot._name.toLowerCase())); return itemTemplate[1]._props.Slots.some((slot) => plateSlotIds.includes(slot._name.toLowerCase()));
} }
/** /**
@ -292,7 +294,7 @@ export class ItemHelper
// Check if item has slots that match soft insert name ids // Check if item has slots that match soft insert name ids
const softInsertIds = this.getSoftInsertSlotIds(); const softInsertIds = this.getSoftInsertSlotIds();
if (itemDbDetails[1]._props.Slots.find(slot => softInsertIds.includes(slot._name.toLowerCase()))) if (itemDbDetails[1]._props.Slots.find((slot) => softInsertIds.includes(slot._name.toLowerCase())))
{ {
return true; return true;
} }
@ -629,7 +631,7 @@ export class ItemHelper
} }
// Items parentid matches root item AND returned items doesnt contain current child // Items parentid matches root item AND returned items doesnt contain current child
if (childItem.parentId === baseItemId && !list.find(item => childItem._id === item._id)) if (childItem.parentId === baseItemId && !list.find((item) => childItem._id === item._id))
{ {
list.push(...this.findAndReturnChildrenAsItems(items, childItem._id)); list.push(...this.findAndReturnChildrenAsItems(items, childItem._id));
} }
@ -650,7 +652,7 @@ export class ItemHelper
for (const itemFromAssort of assort) for (const itemFromAssort of assort)
{ {
if (itemFromAssort.parentId === itemIdToFind && !list.find(item => itemFromAssort._id === item._id)) if (itemFromAssort.parentId === itemIdToFind && !list.find((item) => itemFromAssort._id === item._id))
{ {
list.push(itemFromAssort); list.push(itemFromAssort);
list = list.concat(this.findAndReturnChildrenByAssort(itemFromAssort._id, assort)); list = list.concat(this.findAndReturnChildrenByAssort(itemFromAssort._id, assort));
@ -798,9 +800,8 @@ export class ItemHelper
public findBarterItems(by: "tpl" | "id", itemsToSearch: Item[], desiredBarterItemIds: string | string[]): Item[] public findBarterItems(by: "tpl" | "id", itemsToSearch: Item[], desiredBarterItemIds: string | string[]): Item[]
{ {
// Find required items to take after buying (handles multiple items) // Find required items to take after buying (handles multiple items)
const desiredBarterIds = typeof desiredBarterItemIds === "string" const desiredBarterIds
? [desiredBarterItemIds] = typeof desiredBarterItemIds === "string" ? [desiredBarterItemIds] : desiredBarterItemIds;
: desiredBarterItemIds;
const matchingItems: Item[] = []; const matchingItems: Item[] = [];
for (const barterId of desiredBarterIds) for (const barterId of desiredBarterIds)
@ -846,7 +847,7 @@ export class ItemHelper
if (pmcData !== null) if (pmcData !== null)
{ {
// Insured items should not be renamed. Only works for PMCs. // Insured items should not be renamed. Only works for PMCs.
if (insuredItems?.find(insuredItem => insuredItem.itemId === item._id)) if (insuredItems?.find((insuredItem) => insuredItem.itemId === item._id))
{ {
continue; continue;
} }
@ -1054,8 +1055,8 @@ export class ItemHelper
let isRequiredSlot = false; let isRequiredSlot = false;
if (parentTemplate[0] && parentTemplate[1]?._props?.Slots) if (parentTemplate[0] && parentTemplate[1]?._props?.Slots)
{ {
isRequiredSlot = parentTemplate[1]._props.Slots.some(slot => isRequiredSlot = parentTemplate[1]._props.Slots.some(
slot._name === item.slotId && slot._required, (slot) => slot._name === item.slotId && slot._required,
); );
} }
@ -1100,11 +1101,13 @@ export class ItemHelper
*/ */
public isAttachmentAttached(item: Item): boolean public isAttachmentAttached(item: Item): boolean
{ {
const equipmentSlots = Object.values(EquipmentSlots).map(value => value as string); const equipmentSlots = Object.values(EquipmentSlots).map((value) => value as string);
return !(["hideout", "main"].includes(item.slotId) return !(
|| equipmentSlots.includes(item.slotId) ["hideout", "main"].includes(item.slotId)
|| !Number.isNaN(Number(item.slotId))); || equipmentSlots.includes(item.slotId)
|| !Number.isNaN(Number(item.slotId))
);
} }
/** /**
@ -1125,7 +1128,7 @@ export class ItemHelper
public getEquipmentParent(itemId: string, itemsMap: Map<string, Item>): Item | null public getEquipmentParent(itemId: string, itemsMap: Map<string, Item>): Item | null
{ {
let currentItem = itemsMap.get(itemId); let currentItem = itemsMap.get(itemId);
const equipmentSlots = Object.values(EquipmentSlots).map(value => value as string); const equipmentSlots = Object.values(EquipmentSlots).map((value) => value as string);
while (currentItem && !equipmentSlots.includes(currentItem.slotId)) while (currentItem && !equipmentSlots.includes(currentItem.slotId))
{ {
@ -1146,7 +1149,7 @@ export class ItemHelper
*/ */
public getItemSize(items: Item[], rootItemId: string): ItemHelper.ItemSize public getItemSize(items: Item[], rootItemId: string): ItemHelper.ItemSize
{ {
const rootTemplate = this.getItem(items.filter(x => x._id === rootItemId)[0]._tpl)[1]; const rootTemplate = this.getItem(items.filter((x) => x._id === rootItemId)[0]._tpl)[1];
const width = rootTemplate._props.Width; const width = rootTemplate._props.Width;
const height = rootTemplate._props.Height; const height = rootTemplate._props.Height;
@ -1178,9 +1181,8 @@ export class ItemHelper
sizeUp = sizeUp < itemTemplate._props.ExtraSizeUp ? itemTemplate._props.ExtraSizeUp : sizeUp; sizeUp = sizeUp < itemTemplate._props.ExtraSizeUp ? itemTemplate._props.ExtraSizeUp : sizeUp;
sizeDown = sizeDown < itemTemplate._props.ExtraSizeDown ? itemTemplate._props.ExtraSizeDown : sizeDown; sizeDown = sizeDown < itemTemplate._props.ExtraSizeDown ? itemTemplate._props.ExtraSizeDown : sizeDown;
sizeLeft = sizeLeft < itemTemplate._props.ExtraSizeLeft ? itemTemplate._props.ExtraSizeLeft : sizeLeft; sizeLeft = sizeLeft < itemTemplate._props.ExtraSizeLeft ? itemTemplate._props.ExtraSizeLeft : sizeLeft;
sizeRight = sizeRight < itemTemplate._props.ExtraSizeRight sizeRight
? itemTemplate._props.ExtraSizeRight = sizeRight < itemTemplate._props.ExtraSizeRight ? itemTemplate._props.ExtraSizeRight : sizeRight;
: sizeRight;
} }
} }
@ -1221,7 +1223,7 @@ export class ItemHelper
const cartridgeMaxStackSize = cartridgeDetails[1]._props.StackMaxSize; const cartridgeMaxStackSize = cartridgeDetails[1]._props.StackMaxSize;
// Exit if ammo already exists in box // Exit if ammo already exists in box
if (ammoBox.find(item => item._tpl === cartridgeTpl)) if (ammoBox.find((item) => item._tpl === cartridgeTpl))
{ {
return; return;
} }
@ -1289,7 +1291,7 @@ export class ItemHelper
public itemIsInsideContainer(item: Item, desiredContainerSlotId: string, items: Item[]): boolean public itemIsInsideContainer(item: Item, desiredContainerSlotId: string, items: Item[]): boolean
{ {
// Get items parent // Get items parent
const parent = items.find(x => x._id === item.parentId); const parent = items.find((x) => x._id === item.parentId);
if (!parent) if (!parent)
{ {
// No parent, end of line, not inside container // No parent, end of line, not inside container
@ -1394,9 +1396,8 @@ export class ItemHelper
while (currentStoredCartridgeCount < desiredStackCount) while (currentStoredCartridgeCount < desiredStackCount)
{ {
// Get stack size of cartridges // Get stack size of cartridges
let cartridgeCountToAdd = desiredStackCount <= cartridgeMaxStackSize let cartridgeCountToAdd
? desiredStackCount = desiredStackCount <= cartridgeMaxStackSize ? desiredStackCount : cartridgeMaxStackSize;
: cartridgeMaxStackSize;
// Ensure we don't go over the max stackcount size // Ensure we don't go over the max stackcount size
const remainingSpace = desiredStackCount - currentStoredCartridgeCount; const remainingSpace = desiredStackCount - currentStoredCartridgeCount;
@ -1437,9 +1438,9 @@ export class ItemHelper
const ammoTpls = magTemplate._props.Cartridges[0]._props.filters[0].Filter; const ammoTpls = magTemplate._props.Cartridges[0]._props.filters[0].Filter;
const calibers = [ const calibers = [
...new Set( ...new Set(
ammoTpls.filter((x: string) => this.getItem(x)[0]).map((x: string) => ammoTpls
this.getItem(x)[1]._props.Caliber, .filter((x: string) => this.getItem(x)[0])
), .map((x: string) => this.getItem(x)[1]._props.Caliber),
), ),
]; ];
return this.randomUtil.drawRandomFromList(calibers)[0]; return this.randomUtil.drawRandomFromList(calibers)[0];
@ -1555,9 +1556,9 @@ export class ItemHelper
public getItemTplsOfBaseType(desiredBaseType: string): string[] public getItemTplsOfBaseType(desiredBaseType: string): string[]
{ {
return Object.values(this.databaseServer.getTables().templates.items).filter(x => return Object.values(this.databaseServer.getTables().templates.items)
x._parent === desiredBaseType, .filter((x) => x._parent === desiredBaseType)
).map(x => x._id); .map((x) => x._id);
} }
/** /**
@ -1771,7 +1772,7 @@ export class ItemHelper
for (const item of items) for (const item of items)
{ {
// Check if the item's parent exists. // Check if the item's parent exists.
const parentExists = items.some(parentItem => parentItem._id === item.parentId); const parentExists = items.some((parentItem) => parentItem._id === item.parentId);
// If the parent does not exist and the item is not already a 'hideout' item, adopt the orphaned item by // If the parent does not exist and the item is not already a 'hideout' item, adopt the orphaned item by
// setting the parent ID to the PMCs inventory equipment ID, the slot ID to 'hideout', and remove the location. // setting the parent ID to the PMCs inventory equipment ID, the slot ID to 'hideout', and remove the location.

View File

@ -86,9 +86,10 @@ export class NotificationSendHelper
protected getDialog(sessionId: string, messageType: MessageType, senderDetails: IUserDialogInfo): Dialogue protected getDialog(sessionId: string, messageType: MessageType, senderDetails: IUserDialogInfo): Dialogue
{ {
// Use trader id if sender is trader, otherwise use nickname // Use trader id if sender is trader, otherwise use nickname
const key = senderDetails.Info.MemberCategory === MemberCategory.TRADER const key
? senderDetails._id = senderDetails.Info.MemberCategory === MemberCategory.TRADER
: senderDetails.Info.Nickname; ? senderDetails._id
: senderDetails.Info.Nickname;
const dialogueData = this.saveServer.getProfile(sessionId).dialogues; const dialogueData = this.saveServer.getProfile(sessionId).dialogues;
const isNewDialogue = !(key in dialogueData); const isNewDialogue = !(key in dialogueData);
let dialogue: Dialogue = dialogueData[key]; let dialogue: Dialogue = dialogueData[key];

View File

@ -21,8 +21,8 @@ export class PaymentHelper
*/ */
public isMoneyTpl(tpl: string): boolean public isMoneyTpl(tpl: string): boolean
{ {
return [Money.DOLLARS, Money.EUROS, Money.ROUBLES, ...this.inventoryConfig.customMoneyTpls].some(element => return [Money.DOLLARS, Money.EUROS, Money.ROUBLES, ...this.inventoryConfig.customMoneyTpls].some(
element === tpl, (element) => element === tpl,
); );
} }

View File

@ -44,11 +44,9 @@ export class PresetHelper
{ {
if (!this.defaultWeaponPresets) if (!this.defaultWeaponPresets)
{ {
this.defaultWeaponPresets = Object.values( this.defaultWeaponPresets = Object.values(this.databaseServer.getTables().globals.ItemPresets)
this.databaseServer.getTables().globals.ItemPresets,
)
.filter( .filter(
preset => (preset) =>
preset._encyclopedia !== undefined preset._encyclopedia !== undefined
&& this.itemHelper.isOfBaseclass(preset._encyclopedia, BaseClasses.WEAPON), && this.itemHelper.isOfBaseclass(preset._encyclopedia, BaseClasses.WEAPON),
) )
@ -71,9 +69,12 @@ export class PresetHelper
if (!this.defaultEquipmentPresets) if (!this.defaultEquipmentPresets)
{ {
this.defaultEquipmentPresets = Object.values(this.databaseServer.getTables().globals.ItemPresets) this.defaultEquipmentPresets = Object.values(this.databaseServer.getTables().globals.ItemPresets)
.filter(preset => preset._encyclopedia !== undefined .filter(
&& this.itemHelper.armorItemCanHoldMods(preset._encyclopedia), (preset) =>
).reduce((acc, cur) => preset._encyclopedia !== undefined
&& this.itemHelper.armorItemCanHoldMods(preset._encyclopedia),
)
.reduce((acc, cur) =>
{ {
acc[cur._id] = cur; acc[cur._id] = cur;
return acc; return acc;
@ -186,7 +187,7 @@ export class PresetHelper
const defaultPreset = this.getDefaultPreset(tpl); const defaultPreset = this.getDefaultPreset(tpl);
// Bundle up tpls we want price for // Bundle up tpls we want price for
const tpls = defaultPreset ? defaultPreset._items.map(item => item._tpl) : [tpl]; const tpls = defaultPreset ? defaultPreset._items.map((item) => item._tpl) : [tpl];
// Get price of tpls // Get price of tpls
return this.itemHelper.getItemAndChildrenPrice(tpls); return this.itemHelper.getItemAndChildrenPrice(tpls);

View File

@ -52,7 +52,7 @@ export class ProfileHelper
for (const questId in questConditionId) for (const questId in questConditionId)
{ {
const conditionId = questConditionId[questId]; const conditionId = questConditionId[questId];
const profileQuest = pmcData.Quests.find(x => x.qid === questId); const profileQuest = pmcData.Quests.find((x) => x.qid === questId);
// Find index of condition in array // Find index of condition in array
const index = profileQuest.completedConditions.indexOf(conditionId); const index = profileQuest.completedConditions.indexOf(conditionId);
@ -339,7 +339,7 @@ export class ProfileHelper
public removeSecureContainer(profile: IPmcData): IPmcData public removeSecureContainer(profile: IPmcData): IPmcData
{ {
const items = profile.Inventory.items; const items = profile.Inventory.items;
const secureContainer = items.find(x => x.slotId === "SecuredContainer"); const secureContainer = items.find((x) => x.slotId === "SecuredContainer");
if (secureContainer) if (secureContainer)
{ {
// Find and remove container + children // Find and remove container + children
@ -349,7 +349,7 @@ export class ProfileHelper
); );
// Remove child items + secure container // Remove child items + secure container
profile.Inventory.items = items.filter(x => !childItemsInSecureContainer.includes(x._id)); profile.Inventory.items = items.filter((x) => !childItemsInSecureContainer.includes(x._id));
} }
return profile; return profile;
@ -393,7 +393,7 @@ export class ProfileHelper
return false; return false;
} }
return !!profile.aki.receivedGifts.find(x => x.giftId === giftId); return !!profile.aki.receivedGifts.find((x) => x.giftId === giftId);
} }
/** /**
@ -403,7 +403,7 @@ export class ProfileHelper
*/ */
public incrementStatCounter(counters: CounterKeyValue[], keyToIncrement: string): void public incrementStatCounter(counters: CounterKeyValue[], keyToIncrement: string): void
{ {
const stat = counters.find(x => x.Key.includes(keyToIncrement)); const stat = counters.find((x) => x.Key.includes(keyToIncrement));
if (stat) if (stat)
{ {
stat.Value++; stat.Value++;
@ -424,7 +424,7 @@ export class ProfileHelper
return false; return false;
} }
const profileSkill = profileSkills.find(x => x.Id === skillType); const profileSkill = profileSkills.find((x) => x.Id === skillType);
if (!profileSkill) if (!profileSkill)
{ {
this.logger.warning(`Unable to check for elite skill ${skillType}, not found in profile`); this.logger.warning(`Unable to check for elite skill ${skillType}, not found in profile`);
@ -466,7 +466,7 @@ export class ProfileHelper
return; return;
} }
const profileSkill = profileSkills.find(profileSkill => profileSkill.Id === skill); const profileSkill = profileSkills.find((profileSkill) => profileSkill.Id === skill);
if (!profileSkill) if (!profileSkill)
{ {
this.logger.error(this.localisationService.getText("quest-no_skill_found", skill)); this.logger.error(this.localisationService.getText("quest-no_skill_found", skill));
@ -499,7 +499,7 @@ export class ProfileHelper
*/ */
public getSkillFromProfile(pmcData: IPmcData, skill: SkillTypes): Common public getSkillFromProfile(pmcData: IPmcData, skill: SkillTypes): Common
{ {
const skillToReturn = pmcData.Skills.Common.find(commonSkill => commonSkill.Id === skill); const skillToReturn = pmcData.Skills.Common.find((commonSkill) => commonSkill.Id === skill);
if (!skillToReturn) if (!skillToReturn)
{ {
this.logger.warning(`Profile ${pmcData.sessionId} does not have a skill named: ${skill}`); this.logger.warning(`Profile ${pmcData.sessionId} does not have a skill named: ${skill}`);
@ -527,7 +527,7 @@ export class ProfileHelper
public addStashRowsBonusToProfile(sessionId: string, rowsToAdd: number): void public addStashRowsBonusToProfile(sessionId: string, rowsToAdd: number): void
{ {
const profile = this.getPmcProfile(sessionId); const profile = this.getPmcProfile(sessionId);
const existingBonus = profile.Bonuses.find(bonus => bonus.type === BonusType.STASH_ROWS); const existingBonus = profile.Bonuses.find((bonus) => bonus.type === BonusType.STASH_ROWS);
if (!existingBonus) if (!existingBonus)
{ {
profile.Bonuses.push({ profile.Bonuses.push({

View File

@ -11,7 +11,6 @@ import { IPmcData } from "@spt-aki/models/eft/common/IPmcData";
import { Common, IQuestStatus } from "@spt-aki/models/eft/common/tables/IBotBase"; import { Common, IQuestStatus } from "@spt-aki/models/eft/common/tables/IBotBase";
import { Item } from "@spt-aki/models/eft/common/tables/IItem"; import { Item } from "@spt-aki/models/eft/common/tables/IItem";
import { IQuest, IQuestCondition, IQuestReward } from "@spt-aki/models/eft/common/tables/IQuest"; import { IQuest, IQuestCondition, IQuestReward } from "@spt-aki/models/eft/common/tables/IQuest";
import { IRepeatableQuest } from "@spt-aki/models/eft/common/tables/IRepeatableQuests";
import { IItemEventRouterResponse } from "@spt-aki/models/eft/itemEvent/IItemEventRouterResponse"; import { IItemEventRouterResponse } from "@spt-aki/models/eft/itemEvent/IItemEventRouterResponse";
import { IAcceptQuestRequestData } from "@spt-aki/models/eft/quests/IAcceptQuestRequestData"; import { IAcceptQuestRequestData } from "@spt-aki/models/eft/quests/IAcceptQuestRequestData";
import { IFailQuestRequestData } from "@spt-aki/models/eft/quests/IFailQuestRequestData"; import { IFailQuestRequestData } from "@spt-aki/models/eft/quests/IFailQuestRequestData";
@ -69,7 +68,7 @@ export class QuestHelper
*/ */
public getQuestStatus(pmcData: IPmcData, questId: string): QuestStatus public getQuestStatus(pmcData: IPmcData, questId: string): QuestStatus
{ {
const quest = pmcData.Quests?.find(q => q.qid === questId); const quest = pmcData.Quests?.find((q) => q.qid === questId);
return quest ? quest.status : QuestStatus.Locked; return quest ? quest.status : QuestStatus.Locked;
} }
@ -150,7 +149,7 @@ export class QuestHelper
} }
// This calculates how much progress we have in the skill's starting level // This calculates how much progress we have in the skill's starting level
let startingLevelProgress = profileSkill.Progress % 100 * ((currentLevel + 1) / 10); let startingLevelProgress = (profileSkill.Progress % 100) * ((currentLevel + 1) / 10);
// The code below assumes a 1/10th progress skill amount // The code below assumes a 1/10th progress skill amount
let remainingProgress = progressAmount / 10; let remainingProgress = progressAmount / 10;
@ -164,7 +163,7 @@ export class QuestHelper
const currentLevelRemainingProgress = (currentLevel + 1) * 10 - startingLevelProgress; const currentLevelRemainingProgress = (currentLevel + 1) * 10 - startingLevelProgress;
this.logger.debug(`currentLevelRemainingProgress: ${currentLevelRemainingProgress}`); this.logger.debug(`currentLevelRemainingProgress: ${currentLevelRemainingProgress}`);
const progressToAdd = Math.min(remainingProgress, currentLevelRemainingProgress); const progressToAdd = Math.min(remainingProgress, currentLevelRemainingProgress);
const adjustedProgressToAdd = 10 / (currentLevel + 1) * progressToAdd; const adjustedProgressToAdd = (10 / (currentLevel + 1)) * progressToAdd;
this.logger.debug(`Progress To Add: ${progressToAdd} Adjusted for level: ${adjustedProgressToAdd}`); this.logger.debug(`Progress To Add: ${progressToAdd} Adjusted for level: ${adjustedProgressToAdd}`);
// Add the progress amount adjusted by level // Add the progress amount adjusted by level
@ -286,10 +285,13 @@ export class QuestHelper
// Is root item, fix stacks // Is root item, fix stacks
if (rewardItem._id === questReward.target) if (rewardItem._id === questReward.target)
{ // Is base reward item {
// Is base reward item
if ( if (
rewardItem.parentId !== undefined && rewardItem.parentId === "hideout" // Has parentId of hideout rewardItem.parentId !== undefined
&& rewardItem.upd !== undefined && rewardItem.upd.StackObjectsCount !== undefined // Has upd with stackobject count && rewardItem.parentId === "hideout" // Has parentId of hideout
&& rewardItem.upd !== undefined
&& rewardItem.upd.StackObjectsCount !== undefined // Has upd with stackobject count
&& rewardItem.upd.StackObjectsCount > 1 // More than 1 item in stack && rewardItem.upd.StackObjectsCount > 1 // More than 1 item in stack
) )
{ {
@ -353,7 +355,7 @@ export class QuestHelper
questReward.items = presetAndMods; questReward.items = presetAndMods;
// Find root item and set its stack count // Find root item and set its stack count
const rootItem = questReward.items.find(item => item._id === newRootId); const rootItem = questReward.items.find((item) => item._id === newRootId);
// Remap target id to the new presets root id // Remap target id to the new presets root id
questReward.target = rootItem._id; questReward.target = rootItem._id;
@ -385,7 +387,7 @@ export class QuestHelper
{ {
// Iterate over all rewards with the desired status, flatten out items that have a type of Item // Iterate over all rewards with the desired status, flatten out items that have a type of Item
const questRewards = quest.rewards[QuestStatus[status]].flatMap((reward: IQuestReward) => const questRewards = quest.rewards[QuestStatus[status]].flatMap((reward: IQuestReward) =>
(reward.type === "Item" ? this.processReward(reward) : []), reward.type === "Item" ? this.processReward(reward) : [],
); );
return questRewards; return questRewards;
@ -404,7 +406,7 @@ export class QuestHelper
): IQuestStatus ): IQuestStatus
{ {
const currentTimestamp = this.timeUtil.getTimestamp(); const currentTimestamp = this.timeUtil.getTimestamp();
const existingQuest = pmcData.Quests.find(q => q.qid === acceptedQuest.qid); const existingQuest = pmcData.Quests.find((q) => q.qid === acceptedQuest.qid);
if (existingQuest) if (existingQuest)
{ {
// Quest exists, update its status // Quest exists, update its status
@ -436,7 +438,7 @@ export class QuestHelper
this.logger.error(`Quest: ${acceptedQuest.qid} of type: ${acceptedQuest.type} not found`); this.logger.error(`Quest: ${acceptedQuest.qid} of type: ${acceptedQuest.type} not found`);
} }
const waitTime = questDbData?.conditions.AvailableForStart.find(x => x.availableAfter > 0); const waitTime = questDbData?.conditions.AvailableForStart.find((x) => x.availableAfter > 0);
if (waitTime && acceptedQuest.type !== "repeatable") if (waitTime && acceptedQuest.type !== "repeatable")
{ {
// Quest should be put into 'pending' state // Quest should be put into 'pending' state
@ -463,7 +465,7 @@ export class QuestHelper
{ {
// Get quest acceptance data from profile // Get quest acceptance data from profile
const profile: IPmcData = this.profileHelper.getPmcProfile(sessionID); const profile: IPmcData = this.profileHelper.getPmcProfile(sessionID);
const startedQuestInProfile = profile.Quests.find(profileQuest => profileQuest.qid === startedQuestId); const startedQuestInProfile = profile.Quests.find((profileQuest) => profileQuest.qid === startedQuestId);
// Get quests that // Get quests that
const eligibleQuests = this.getQuestsFromDb().filter((quest) => const eligibleQuests = this.getQuestsFromDb().filter((quest) =>
@ -472,9 +474,11 @@ export class QuestHelper
// e.g. Quest A passed in, quest B is looped over and has requirement of A to be started, include it // e.g. Quest A passed in, quest B is looped over and has requirement of A to be started, include it
const acceptedQuestCondition = quest.conditions.AvailableForStart.find((x) => const acceptedQuestCondition = quest.conditions.AvailableForStart.find((x) =>
{ {
return x.conditionType === "Quest" return (
&& x.target?.includes(startedQuestId) x.conditionType === "Quest"
&& x.status?.includes(QuestStatus.Started); && x.target?.includes(startedQuestId)
&& x.status?.includes(QuestStatus.Started)
);
}); });
// Not found, skip quest // Not found, skip quest
@ -512,8 +516,10 @@ export class QuestHelper
} }
// Include if quest found in profile and is started or ready to hand in // Include if quest found in profile and is started or ready to hand in
return startedQuestInProfile return (
&& [QuestStatus.Started, QuestStatus.AvailableForFinish].includes(startedQuestInProfile.status); startedQuestInProfile
&& [QuestStatus.Started, QuestStatus.AvailableForFinish].includes(startedQuestInProfile.status)
);
}); });
return this.getQuestsWithOnlyLevelRequirementStartCondition(eligibleQuests); return this.getQuestsWithOnlyLevelRequirementStartCondition(eligibleQuests);
@ -551,15 +557,15 @@ export class QuestHelper
public failedUnlocked(failedQuestId: string, sessionId: string): IQuest[] public failedUnlocked(failedQuestId: string, sessionId: string): IQuest[]
{ {
const profile = this.profileHelper.getPmcProfile(sessionId); const profile = this.profileHelper.getPmcProfile(sessionId);
const profileQuest = profile.Quests.find(x => x.qid === failedQuestId); const profileQuest = profile.Quests.find((x) => x.qid === failedQuestId);
const quests = this.getQuestsFromDb().filter((q) => const quests = this.getQuestsFromDb().filter((q) =>
{ {
const acceptedQuestCondition = q.conditions.AvailableForStart.find((c) => const acceptedQuestCondition = q.conditions.AvailableForStart.find((c) =>
{ {
return c.conditionType === "Quest" return (
&& c.target.includes(failedQuestId) c.conditionType === "Quest" && c.target.includes(failedQuestId) && c.status[0] === QuestStatus.Fail
&& c.status[0] === QuestStatus.Fail; );
}); });
if (!acceptedQuestCondition) if (!acceptedQuestCondition)
@ -595,7 +601,7 @@ export class QuestHelper
if (this.paymentHelper.isMoneyTpl(reward.items[0]._tpl)) if (this.paymentHelper.isMoneyTpl(reward.items[0]._tpl))
{ {
reward.items[0].upd.StackObjectsCount += Math.round( reward.items[0].upd.StackObjectsCount += Math.round(
reward.items[0].upd.StackObjectsCount * multiplier / 100, (reward.items[0].upd.StackObjectsCount * multiplier) / 100,
); );
} }
} }
@ -621,7 +627,7 @@ export class QuestHelper
output: IItemEventRouterResponse, output: IItemEventRouterResponse,
): void ): void
{ {
const inventoryItemIndex = pmcData.Inventory.items.findIndex(item => item._id === itemId); const inventoryItemIndex = pmcData.Inventory.items.findIndex((item) => item._id === itemId);
if (inventoryItemIndex < 0) if (inventoryItemIndex < 0)
{ {
this.logger.error(this.localisationService.getText("quest-item_not_found_in_inventory", itemId)); this.logger.error(this.localisationService.getText("quest-item_not_found_in_inventory", itemId));
@ -692,8 +698,8 @@ export class QuestHelper
public getQuestWithOnlyLevelRequirementStartCondition(quest: IQuest): IQuest public getQuestWithOnlyLevelRequirementStartCondition(quest: IQuest): IQuest
{ {
const updatedQuest = this.cloner.clone(quest); const updatedQuest = this.cloner.clone(quest);
updatedQuest.conditions.AvailableForStart = updatedQuest.conditions.AvailableForStart.filter(q => updatedQuest.conditions.AvailableForStart = updatedQuest.conditions.AvailableForStart.filter(
q.conditionType === "Level", (q) => q.conditionType === "Level",
); );
return updatedQuest; return updatedQuest;
@ -734,9 +740,9 @@ export class QuestHelper
const quest = this.getQuestFromDb(failRequest.qid, pmcData); const quest = this.getQuestFromDb(failRequest.qid, pmcData);
// Merge all daily/weekly/scav daily quests into one array and look for the matching quest by id // Merge all daily/weekly/scav daily quests into one array and look for the matching quest by id
const matchingRepeatableQuest = pmcData.RepeatableQuests.flatMap(repeatableType => const matchingRepeatableQuest = pmcData.RepeatableQuests.flatMap(
repeatableType.activeQuests, (repeatableType) => repeatableType.activeQuests,
).find(activeQuest => activeQuest._id === failRequest.qid); ).find((activeQuest) => activeQuest._id === failRequest.qid);
// Quest found and no repeatable found // Quest found and no repeatable found
if (quest && !matchingRepeatableQuest) if (quest && !matchingRepeatableQuest)
@ -783,7 +789,7 @@ export class QuestHelper
// Check daily/weekly objects // Check daily/weekly objects
for (const repeatableType of pmcData.RepeatableQuests) for (const repeatableType of pmcData.RepeatableQuests)
{ {
quest = <IQuest><unknown>repeatableType.activeQuests.find(x => x._id === questId); quest = <IQuest>(<unknown>repeatableType.activeQuests.find((x) => x._id === questId));
if (quest) if (quest)
{ {
break; break;
@ -805,7 +811,9 @@ export class QuestHelper
// blank or is a guid, use description instead // blank or is a guid, use description instead
const startedMessageText = this.getQuestLocaleIdFromDb(startedMessageTextId); const startedMessageText = this.getQuestLocaleIdFromDb(startedMessageTextId);
if ( if (
!startedMessageText || startedMessageText.trim() === "" || startedMessageText.toLowerCase() === "test" !startedMessageText
|| startedMessageText.trim() === ""
|| startedMessageText.toLowerCase() === "test"
|| startedMessageText.length === 24 || startedMessageText.length === 24
) )
{ {
@ -835,7 +843,7 @@ export class QuestHelper
public updateQuestState(pmcData: IPmcData, newQuestState: QuestStatus, questId: string): void public updateQuestState(pmcData: IPmcData, newQuestState: QuestStatus, questId: string): void
{ {
// Find quest in profile, update status to desired status // Find quest in profile, update status to desired status
const questToUpdate = pmcData.Quests.find(quest => quest.qid === questId); const questToUpdate = pmcData.Quests.find((quest) => quest.qid === questId);
if (questToUpdate) if (questToUpdate)
{ {
questToUpdate.status = newQuestState; questToUpdate.status = newQuestState;
@ -851,7 +859,7 @@ export class QuestHelper
*/ */
public resetQuestState(pmcData: IPmcData, newQuestState: QuestStatus, questId: string): void public resetQuestState(pmcData: IPmcData, newQuestState: QuestStatus, questId: string): void
{ {
const questToUpdate = pmcData.Quests.find(quest => quest.qid === questId); const questToUpdate = pmcData.Quests.find((quest) => quest.qid === questId);
if (questToUpdate) if (questToUpdate)
{ {
const currentTimestamp = this.timeUtil.getTimestamp(); const currentTimestamp = this.timeUtil.getTimestamp();
@ -994,10 +1002,11 @@ export class QuestHelper
{ {
// Get hideout crafts and find those that match by areatype/required level/end product tpl - hope for just one match // Get hideout crafts and find those that match by areatype/required level/end product tpl - hope for just one match
const hideoutProductions = this.databaseServer.getTables().hideout.production; const hideoutProductions = this.databaseServer.getTables().hideout.production;
const matchingProductions = hideoutProductions.filter(x => const matchingProductions = hideoutProductions.filter(
x.areaType === Number.parseInt(craftUnlockReward.traderId) (x) =>
&& x.requirements.some(x => x.requiredLevel === craftUnlockReward.loyaltyLevel) x.areaType === Number.parseInt(craftUnlockReward.traderId)
&& x.endProduct === craftUnlockReward.items[0]._tpl, && x.requirements.some((x) => x.requiredLevel === craftUnlockReward.loyaltyLevel)
&& x.endProduct === craftUnlockReward.items[0]._tpl,
); );
// More/less than 1 match, above filtering wasn't strict enough // More/less than 1 match, above filtering wasn't strict enough
@ -1027,7 +1036,7 @@ export class QuestHelper
protected getQuestMoneyRewardBonus(pmcData: IPmcData): number protected getQuestMoneyRewardBonus(pmcData: IPmcData): number
{ {
// Check player has intel center // Check player has intel center
const moneyRewardBonuses = pmcData.Bonuses.filter(x => x.type === "QuestMoneyReward"); const moneyRewardBonuses = pmcData.Bonuses.filter((x) => x.type === "QuestMoneyReward");
if (!moneyRewardBonuses) if (!moneyRewardBonuses)
{ {
return 0; return 0;
@ -1061,15 +1070,15 @@ export class QuestHelper
const result: Record<string, string> = {}; const result: Record<string, string> = {};
for (const questId of questIds) for (const questId of questIds)
{ {
const questInDb = allQuests.find(x => x._id === questId); const questInDb = allQuests.find((x) => x._id === questId);
if (!questInDb) if (!questInDb)
{ {
this.logger.debug(`Unable to find quest: ${questId} in db, cannot get 'FindItem' condition, skipping`); this.logger.debug(`Unable to find quest: ${questId} in db, cannot get 'FindItem' condition, skipping`);
continue; continue;
} }
const condition = questInDb.conditions.AvailableForFinish.find(c => const condition = questInDb.conditions.AvailableForFinish.find(
c.conditionType === "FindItem" && c?.target?.includes(itemTpl), (c) => c.conditionType === "FindItem" && c?.target?.includes(itemTpl),
); );
if (condition) if (condition)
{ {
@ -1095,7 +1104,7 @@ export class QuestHelper
{ {
// Quest from db matches quests in profile, skip // Quest from db matches quests in profile, skip
const questData = quests[questIdKey]; const questData = quests[questIdKey];
if (pmcProfile.Quests.find(x => x.qid === questData._id)) if (pmcProfile.Quests.find((x) => x.qid === questData._id))
{ {
continue; continue;
} }
@ -1115,10 +1124,10 @@ export class QuestHelper
availableAfter: 0, availableAfter: 0,
}; };
if (pmcProfile.Quests.some(x => x.qid === questIdKey)) if (pmcProfile.Quests.some((x) => x.qid === questIdKey))
{ {
// Update existing // Update existing
const existingQuest = pmcProfile.Quests.find(x => x.qid === questIdKey); const existingQuest = pmcProfile.Quests.find((x) => x.qid === questIdKey);
existingQuest.status = questRecordToAdd.status; existingQuest.status = questRecordToAdd.status;
existingQuest.statusTimers = questRecordToAdd.statusTimers; existingQuest.statusTimers = questRecordToAdd.statusTimers;
} }
@ -1132,7 +1141,7 @@ export class QuestHelper
public findAndRemoveQuestFromArrayIfExists(questId: string, quests: IQuestStatus[]): void public findAndRemoveQuestFromArrayIfExists(questId: string, quests: IQuestStatus[]): void
{ {
const pmcQuestToReplaceStatus = quests.find(quest => quest.qid === questId); const pmcQuestToReplaceStatus = quests.find((quest) => quest.qid === questId);
if (pmcQuestToReplaceStatus) if (pmcQuestToReplaceStatus)
{ {
quests.splice(quests.indexOf(pmcQuestToReplaceStatus), 1); quests.splice(quests.indexOf(pmcQuestToReplaceStatus), 1);
@ -1155,7 +1164,7 @@ export class QuestHelper
return false; return false;
} }
return quest.conditions.Fail.some(condition => condition.target?.includes(completedQuestId)); return quest.conditions.Fail.some((condition) => condition.target?.includes(completedQuestId));
}); });
} }
} }

View File

@ -156,7 +156,7 @@ export class RagfairHelper
for (let item of items) for (let item of items)
{ {
item = this.itemHelper.fixItemStackCount(item); item = this.itemHelper.fixItemStackCount(item);
const isChild = items.find(it => it._id === item.parentId); const isChild = items.find((it) => it._id === item.parentId);
if (!isChild) if (!isChild)
{ {

View File

@ -14,7 +14,6 @@ import { IItemEventRouterResponse } from "@spt-aki/models/eft/itemEvent/IItemEve
import { IAkiProfile, ISystemData } from "@spt-aki/models/eft/profile/IAkiProfile"; import { IAkiProfile, ISystemData } from "@spt-aki/models/eft/profile/IAkiProfile";
import { IRagfairOffer } from "@spt-aki/models/eft/ragfair/IRagfairOffer"; import { IRagfairOffer } from "@spt-aki/models/eft/ragfair/IRagfairOffer";
import { ISearchRequestData, OfferOwnerType } from "@spt-aki/models/eft/ragfair/ISearchRequestData"; import { ISearchRequestData, OfferOwnerType } from "@spt-aki/models/eft/ragfair/ISearchRequestData";
import { BaseClasses } from "@spt-aki/models/enums/BaseClasses";
import { ConfigTypes } from "@spt-aki/models/enums/ConfigTypes"; import { ConfigTypes } from "@spt-aki/models/enums/ConfigTypes";
import { MemberCategory } from "@spt-aki/models/enums/MemberCategory"; import { MemberCategory } from "@spt-aki/models/enums/MemberCategory";
import { MessageType } from "@spt-aki/models/enums/MessageType"; import { MessageType } from "@spt-aki/models/enums/MessageType";
@ -185,7 +184,7 @@ export class RagfairOfferHelper
const lockedOffers = this.getLoyaltyLockedOffers(possibleOffers, pmcData); const lockedOffers = this.getLoyaltyLockedOffers(possibleOffers, pmcData);
// Exclude locked offers + above loyalty locked offers if at least 1 was found // Exclude locked offers + above loyalty locked offers if at least 1 was found
const availableOffers = possibleOffers.filter(x => !(x.locked || lockedOffers.includes(x._id))); const availableOffers = possibleOffers.filter((x) => !(x.locked || lockedOffers.includes(x._id)));
if (availableOffers.length > 0) if (availableOffers.length > 0)
{ {
possibleOffers = availableOffers; possibleOffers = availableOffers;
@ -220,8 +219,8 @@ export class RagfairOfferHelper
*/ */
public traderOfferItemQuestLocked(offer: IRagfairOffer, traderAssorts: Record<string, ITraderAssort>): boolean public traderOfferItemQuestLocked(offer: IRagfairOffer, traderAssorts: Record<string, ITraderAssort>): boolean
{ {
return offer.items?.some(i => return offer.items?.some((i) =>
traderAssorts[offer.user.id].barter_scheme[i._id]?.some(bs1 => bs1?.some(bs2 => bs2.sptQuestLocked)), traderAssorts[offer.user.id].barter_scheme[i._id]?.some((bs1) => bs1?.some((bs2) => bs2.sptQuestLocked)),
); );
} }
@ -248,15 +247,15 @@ export class RagfairOfferHelper
protected traderBuyRestrictionReached(offer: IRagfairOffer): boolean protected traderBuyRestrictionReached(offer: IRagfairOffer): boolean
{ {
const traderAssorts = this.traderHelper.getTraderAssortsByTraderId(offer.user.id).items; const traderAssorts = this.traderHelper.getTraderAssortsByTraderId(offer.user.id).items;
const assortData = traderAssorts.find(x => x._id === offer.items[0]._id); const assortData = traderAssorts.find((x) => x._id === offer.items[0]._id);
// No trader assort data // No trader assort data
if (!assortData) if (!assortData)
{ {
this.logger.warning( this.logger.warning(
`Unable to find trader: ${offer.user.nickname} assort for item: ${ `Unable to find trader: ${offer.user.nickname} assort for item: ${this.itemHelper.getItemName(
this.itemHelper.getItemName(offer.items[0]._tpl) offer.items[0]._tpl,
} ${offer.items[0]._tpl}, cannot check if buy restriction reached`, )} ${offer.items[0]._tpl}, cannot check if buy restriction reached`,
); );
return false; return false;
} }
@ -333,7 +332,7 @@ export class RagfairOfferHelper
this.increaseProfileRagfairRating( this.increaseProfileRagfairRating(
this.saveServer.getProfile(sessionID), this.saveServer.getProfile(sessionID),
offer.summaryCost / totalItemsCount * boughtAmount, (offer.summaryCost / totalItemsCount) * boughtAmount,
); );
this.completeOffer(sessionID, offer, boughtAmount); this.completeOffer(sessionID, offer, boughtAmount);
@ -361,7 +360,7 @@ export class RagfairOfferHelper
return; return;
} }
profile.characters.pmc.RagfairInfo.rating profile.characters.pmc.RagfairInfo.rating
+= ragfairConfig.ratingIncreaseCount / ragfairConfig.ratingSumForIncrease * amountToIncrementBy; += (ragfairConfig.ratingIncreaseCount / ragfairConfig.ratingSumForIncrease) * amountToIncrementBy;
} }
/** /**
@ -389,7 +388,7 @@ export class RagfairOfferHelper
protected deleteOfferById(sessionID: string, offerId: string): void protected deleteOfferById(sessionID: string, offerId: string): void
{ {
const profileRagfairInfo = this.saveServer.getProfile(sessionID).characters.pmc.RagfairInfo; const profileRagfairInfo = this.saveServer.getProfile(sessionID).characters.pmc.RagfairInfo;
const index = profileRagfairInfo.offers.findIndex(o => o._id === offerId); const index = profileRagfairInfo.offers.findIndex((o) => o._id === offerId);
profileRagfairInfo.offers.splice(index, 1); profileRagfairInfo.offers.splice(index, 1);
// Also delete from ragfair // Also delete from ragfair
@ -416,7 +415,7 @@ export class RagfairOfferHelper
else else
{ {
offer.items[0].upd.StackObjectsCount -= boughtAmount; offer.items[0].upd.StackObjectsCount -= boughtAmount;
const rootItems = offer.items.filter(i => i.parentId === "hideout"); const rootItems = offer.items.filter((i) => i.parentId === "hideout");
rootItems.splice(0, 1); rootItems.splice(0, 1);
let removeCount = boughtAmount; let removeCount = boughtAmount;
@ -446,9 +445,9 @@ export class RagfairOfferHelper
for (const id of idsToRemove) for (const id of idsToRemove)
{ {
const newIds = offer.items.filter(i => const newIds = offer.items
!idsToRemove.includes(i._id) && idsToRemove.includes(i.parentId), .filter((i) => !idsToRemove.includes(i._id) && idsToRemove.includes(i.parentId))
).map(i => i._id); .map((i) => i._id);
if (newIds.length > 0) if (newIds.length > 0)
{ {
foundNewItems = true; foundNewItems = true;
@ -459,7 +458,7 @@ export class RagfairOfferHelper
if (idsToRemove.length > 0) if (idsToRemove.length > 0)
{ {
offer.items = offer.items.filter(i => !idsToRemove.includes(i._id)); offer.items = offer.items.filter((i) => !idsToRemove.includes(i._id));
} }
} }
@ -684,7 +683,7 @@ export class RagfairOfferHelper
if (this.itemHelper.armorItemCanHoldMods(offerRootItem._tpl)) if (this.itemHelper.armorItemCanHoldMods(offerRootItem._tpl))
{ {
const offerRootTemplate = this.itemHelper.getItem(offerRootItem._tpl)[1]; const offerRootTemplate = this.itemHelper.getItem(offerRootItem._tpl)[1];
const requiredPlateCount = offerRootTemplate._props.Slots?.filter(item => item._required)?.length; const requiredPlateCount = offerRootTemplate._props.Slots?.filter((item) => item._required)?.length;
return offer.items.length > requiredPlateCount; return offer.items.length > requiredPlateCount;
} }
@ -724,7 +723,7 @@ export class RagfairOfferHelper
// Performing a required search and offer doesn't have requirement for item // Performing a required search and offer doesn't have requirement for item
if ( if (
searchRequest.neededSearchId searchRequest.neededSearchId
&& !offer.requirements.some(requirement => requirement._tpl === searchRequest.neededSearchId) && !offer.requirements.some((requirement) => requirement._tpl === searchRequest.neededSearchId)
) )
{ {
return false; return false;
@ -782,7 +781,7 @@ export class RagfairOfferHelper
public isDisplayableOfferThatNeedsItem(searchRequest: ISearchRequestData, offer: IRagfairOffer): boolean public isDisplayableOfferThatNeedsItem(searchRequest: ISearchRequestData, offer: IRagfairOffer): boolean
{ {
if (offer.requirements.some(requirement => requirement._tpl === searchRequest.neededSearchId)) if (offer.requirements.some((requirement) => requirement._tpl === searchRequest.neededSearchId))
{ {
return true; return true;
} }
@ -799,7 +798,11 @@ export class RagfairOfferHelper
{ {
// thanks typescript, undefined assertion is not returnable since it // thanks typescript, undefined assertion is not returnable since it
// tries to return a multitype object // tries to return a multitype object
return item.upd.MedKit || item.upd.Repairable || item.upd.Resource || item.upd.FoodDrink || item.upd.Key return item.upd.MedKit
|| item.upd.Repairable
|| item.upd.Resource
|| item.upd.FoodDrink
|| item.upd.Key
|| item.upd.RepairKit || item.upd.RepairKit
? true ? true
: false; : false;

View File

@ -43,7 +43,7 @@ export class RagfairSellHelper
const baseSellChancePercent = sellConfig.base * qualityMultiplier; const baseSellChancePercent = sellConfig.base * qualityMultiplier;
// Modfier gets applied twice to either penalize or incentivize over/under pricing (Probably a cleaner way to do this) // Modfier gets applied twice to either penalize or incentivize over/under pricing (Probably a cleaner way to do this)
const sellModifier = averageOfferPriceRub / playerListedPriceRub * sellConfig.sellMultiplier; const sellModifier = (averageOfferPriceRub / playerListedPriceRub) * sellConfig.sellMultiplier;
let sellChance = Math.round(baseSellChancePercent * sellModifier * sellModifier ** 3 + 10); // Power of 3 let sellChance = Math.round(baseSellChancePercent * sellModifier * sellModifier ** 3 + 10); // Power of 3
// Adjust sell chance if below config value // Adjust sell chance if below config value
@ -72,10 +72,11 @@ export class RagfairSellHelper
const startTime = this.timeUtil.getTimestamp(); const startTime = this.timeUtil.getTimestamp();
// Get a time in future to stop simulating sell chances at // Get a time in future to stop simulating sell chances at
const endTime = startTime const endTime
+ this.timeUtil.getHoursAsSeconds( = startTime
this.databaseServer.getTables().globals.config.RagFair.offerDurationTimeInHour, + this.timeUtil.getHoursAsSeconds(
); this.databaseServer.getTables().globals.config.RagFair.offerDurationTimeInHour,
);
let sellTime = startTime; let sellTime = startTime;
let remainingCount = itemSellCount; let remainingCount = itemSellCount;

View File

@ -106,7 +106,8 @@ export class RagfairServerHelper
// Don't include damaged ammo packs // Don't include damaged ammo packs
if ( if (
this.ragfairConfig.dynamic.blacklist.damagedAmmoPacks && itemDetails[1]._parent === BaseClasses.AMMO_BOX this.ragfairConfig.dynamic.blacklist.damagedAmmoPacks
&& itemDetails[1]._parent === BaseClasses.AMMO_BOX
&& itemDetails[1]._name.includes("_damaged") && itemDetails[1]._name.includes("_damaged")
) )
{ {
@ -217,7 +218,7 @@ export class RagfairServerHelper
this.randomUtil.getInt(config.stackablePercent.min, config.stackablePercent.max), this.randomUtil.getInt(config.stackablePercent.min, config.stackablePercent.max),
); );
return Math.round(maxStackCount / 100 * stackPercent); return Math.round((maxStackCount / 100) * stackPercent);
} }
/** /**

View File

@ -31,8 +31,8 @@ export class RepeatableQuestHelper
repeatableConfig: IRepeatableQuestConfig, repeatableConfig: IRepeatableQuestConfig,
): IEliminationConfig ): IEliminationConfig
{ {
return repeatableConfig.questConfig.Elimination.find(x => return repeatableConfig.questConfig.Elimination.find(
pmcLevel >= x.levelRange.min && pmcLevel <= x.levelRange.max, (x) => pmcLevel >= x.levelRange.min && pmcLevel <= x.levelRange.max,
); );
} }

View File

@ -23,7 +23,7 @@ export class SecureContainerHelper
*/ */
public getSecureContainerItems(items: Item[]): string[] public getSecureContainerItems(items: Item[]): string[]
{ {
const secureContainer = items.find(x => x.slotId === "SecuredContainer"); const secureContainer = items.find((x) => x.slotId === "SecuredContainer");
// No container found, drop out // No container found, drop out
if (!secureContainer) if (!secureContainer)
@ -34,6 +34,6 @@ export class SecureContainerHelper
const itemsInSecureContainer = this.itemHelper.findAndReturnChildrenByItems(items, secureContainer._id); const itemsInSecureContainer = this.itemHelper.findAndReturnChildrenByItems(items, secureContainer._id);
// Return all items returned and exclude the secure container item itself // Return all items returned and exclude the secure container item itself
return itemsInSecureContainer.filter(x => x !== secureContainer._id); return itemsInSecureContainer.filter((x) => x !== secureContainer._id);
} }
} }

View File

@ -43,8 +43,8 @@ export class TradeHelper
@inject("InventoryHelper") protected inventoryHelper: InventoryHelper, @inject("InventoryHelper") protected inventoryHelper: InventoryHelper,
@inject("RagfairServer") protected ragfairServer: RagfairServer, @inject("RagfairServer") protected ragfairServer: RagfairServer,
@inject("TraderAssortHelper") protected traderAssortHelper: TraderAssortHelper, @inject("TraderAssortHelper") protected traderAssortHelper: TraderAssortHelper,
@inject("TraderPurchasePersisterService") protected traderPurchasePersisterService: @inject("TraderPurchasePersisterService")
TraderPurchasePersisterService, protected traderPurchasePersisterService: TraderPurchasePersisterService,
@inject("ConfigServer") protected configServer: ConfigServer, @inject("ConfigServer") protected configServer: ConfigServer,
@inject("RecursiveCloner") protected cloner: ICloner, @inject("RecursiveCloner") protected cloner: ICloner,
) )
@ -79,7 +79,7 @@ export class TradeHelper
const allOffers = this.ragfairServer.getOffers(); const allOffers = this.ragfairServer.getOffers();
// We store ragfair offerid in buyRequestData.item_id // We store ragfair offerid in buyRequestData.item_id
const offerWithItem = allOffers.find(x => x._id === buyRequestData.item_id); const offerWithItem = allOffers.find((x) => x._id === buyRequestData.item_id);
const itemPurchased = offerWithItem.items[0]; const itemPurchased = offerWithItem.items[0];
// Ensure purchase does not exceed trader item limit // Ensure purchase does not exceed trader item limit
@ -105,7 +105,7 @@ export class TradeHelper
// Get raw offer from ragfair, clone to prevent altering offer itself // Get raw offer from ragfair, clone to prevent altering offer itself
const allOffers = this.ragfairServer.getOffers(); const allOffers = this.ragfairServer.getOffers();
const offerWithItemCloned = this.cloner.clone(allOffers.find(x => x._id === buyRequestData.item_id)); const offerWithItemCloned = this.cloner.clone(allOffers.find((x) => x._id === buyRequestData.item_id));
offerItems = offerWithItemCloned.items; offerItems = offerWithItemCloned.items;
} }
else if (buyRequestData.tid === Traders.FENCE) else if (buyRequestData.tid === Traders.FENCE)
@ -114,7 +114,7 @@ export class TradeHelper
{ {
// Update assort/flea item values // Update assort/flea item values
const traderAssorts = this.traderHelper.getTraderAssortsByTraderId(buyRequestData.tid).items; const traderAssorts = this.traderHelper.getTraderAssortsByTraderId(buyRequestData.tid).items;
const itemPurchased = traderAssorts.find(assort => assort._id === buyRequestData.item_id); const itemPurchased = traderAssorts.find((assort) => assort._id === buyRequestData.item_id);
// Decrement trader item count // Decrement trader item count
itemPurchased.upd.StackObjectsCount -= buyCount; itemPurchased.upd.StackObjectsCount -= buyCount;
@ -123,7 +123,7 @@ export class TradeHelper
}; };
const fenceItems = this.fenceService.getRawFenceAssorts().items; const fenceItems = this.fenceService.getRawFenceAssorts().items;
const rootItemIndex = fenceItems.findIndex(item => item._id === buyRequestData.item_id); const rootItemIndex = fenceItems.findIndex((item) => item._id === buyRequestData.item_id);
if (rootItemIndex === -1) if (rootItemIndex === -1)
{ {
this.logger.debug(`Tried to buy item ${buyRequestData.item_id} from fence that no longer exists`); this.logger.debug(`Tried to buy item ${buyRequestData.item_id} from fence that no longer exists`);
@ -142,7 +142,7 @@ export class TradeHelper
{ {
// Update assort/flea item values // Update assort/flea item values
const traderAssorts = this.traderHelper.getTraderAssortsByTraderId(buyRequestData.tid).items; const traderAssorts = this.traderHelper.getTraderAssortsByTraderId(buyRequestData.tid).items;
const itemPurchased = traderAssorts.find(x => x._id === buyRequestData.item_id); const itemPurchased = traderAssorts.find((x) => x._id === buyRequestData.item_id);
// Ensure purchase does not exceed trader item limit // Ensure purchase does not exceed trader item limit
const assortHasBuyRestrictions = this.itemHelper.hasBuyRestrictions(itemPurchased); const assortHasBuyRestrictions = this.itemHelper.hasBuyRestrictions(itemPurchased);
@ -260,7 +260,7 @@ export class TradeHelper
const itemIdToFind = itemToBeRemoved.id.replace(/\s+/g, ""); // Strip out whitespace const itemIdToFind = itemToBeRemoved.id.replace(/\s+/g, ""); // Strip out whitespace
// Find item in player inventory, or show error to player if not found // Find item in player inventory, or show error to player if not found
const matchingItemInInventory = profileWithItemsToSell.Inventory.items.find(x => x._id === itemIdToFind); const matchingItemInInventory = profileWithItemsToSell.Inventory.items.find((x) => x._id === itemIdToFind);
if (!matchingItemInInventory) if (!matchingItemInInventory)
{ {
const errorMessage = `Unable to sell item ${itemToBeRemoved.id}, cannot be found in player inventory`; const errorMessage = `Unable to sell item ${itemToBeRemoved.id}, cannot be found in player inventory`;

View File

@ -40,8 +40,8 @@ export class TraderAssortHelper
@inject("RagfairOfferGenerator") protected ragfairOfferGenerator: RagfairOfferGenerator, @inject("RagfairOfferGenerator") protected ragfairOfferGenerator: RagfairOfferGenerator,
@inject("TraderAssortService") protected traderAssortService: TraderAssortService, @inject("TraderAssortService") protected traderAssortService: TraderAssortService,
@inject("LocalisationService") protected localisationService: LocalisationService, @inject("LocalisationService") protected localisationService: LocalisationService,
@inject("TraderPurchasePersisterService") protected traderPurchasePersisterService: @inject("TraderPurchasePersisterService")
TraderPurchasePersisterService, protected traderPurchasePersisterService: TraderPurchasePersisterService,
@inject("TraderHelper") protected traderHelper: TraderHelper, @inject("TraderHelper") protected traderHelper: TraderHelper,
@inject("FenceService") protected fenceService: FenceService, @inject("FenceService") protected fenceService: FenceService,
@inject("ConfigServer") protected configServer: ConfigServer, @inject("ConfigServer") protected configServer: ConfigServer,
@ -95,7 +95,7 @@ export class TraderAssortHelper
for (const assortId in assortPurchasesfromTrader) for (const assortId in assortPurchasesfromTrader)
{ {
// Find assort we want to update current buy count of // Find assort we want to update current buy count of
const assortToAdjust = traderClone.assort.items.find(x => x._id === assortId); const assortToAdjust = traderClone.assort.items.find((x) => x._id === assortId);
if (!assortToAdjust) if (!assortToAdjust)
{ {
this.logger.debug( this.logger.debug(
@ -147,7 +147,7 @@ export class TraderAssortHelper
protected resetBuyRestrictionCurrentValue(assortItems: Item[]): void protected resetBuyRestrictionCurrentValue(assortItems: Item[]): void
{ {
// iterate over root items // iterate over root items
for (const assort of assortItems.filter(item => item.slotId === "hideout")) for (const assort of assortItems.filter((item) => item.slotId === "hideout"))
{ {
// no value to adjust // no value to adjust
if (!assort.upd.BuyRestrictionCurrent) if (!assort.upd.BuyRestrictionCurrent)

View File

@ -108,7 +108,7 @@ export class TraderHelper
} }
// Find specific assort in traders data // Find specific assort in traders data
const purchasedAssort = traderAssorts.items.find(item => item._id === assortId); const purchasedAssort = traderAssorts.items.find((item) => item._id === assortId);
if (!purchasedAssort) if (!purchasedAssort)
{ {
this.logger.debug(`No assort ${assortId} on trader: ${traderId} found`); this.logger.debug(`No assort ${assortId} on trader: ${traderId} found`);
@ -239,7 +239,8 @@ export class TraderHelper
if ( if (
loyalty.minLevel <= pmcData.Info.Level loyalty.minLevel <= pmcData.Info.Level
&& loyalty.minSalesSum <= pmcData.TradersInfo[traderID].salesSum && loyalty.minSalesSum <= pmcData.TradersInfo[traderID].salesSum
&& loyalty.minStanding <= pmcData.TradersInfo[traderID].standing && targetLevel < 4 && loyalty.minStanding <= pmcData.TradersInfo[traderID].standing
&& targetLevel < 4
) )
{ {
// level reached // level reached
@ -270,7 +271,7 @@ export class TraderHelper
*/ */
public getTraderUpdateSeconds(traderId: string): number public getTraderUpdateSeconds(traderId: string): number
{ {
const traderDetails = this.traderConfig.updateTime.find(x => x.traderId === traderId); const traderDetails = this.traderConfig.updateTime.find((x) => x.traderId === traderId);
if (!traderDetails || traderDetails.seconds.min === undefined || traderDetails.seconds.max === undefined) if (!traderDetails || traderDetails.seconds.min === undefined || traderDetails.seconds.max === undefined)
{ {
this.logger.warning( this.logger.warning(
@ -280,7 +281,8 @@ export class TraderHelper
}), }),
); );
this.traderConfig.updateTime.push( // create temporary entry to prevent logger spam this.traderConfig.updateTime.push(
// create temporary entry to prevent logger spam
{ {
traderId: traderId, traderId: traderId,
seconds: { min: this.traderConfig.updateTimeDefault, max: this.traderConfig.updateTimeDefault }, seconds: { min: this.traderConfig.updateTimeDefault, max: this.traderConfig.updateTimeDefault },
@ -397,15 +399,16 @@ export class TraderHelper
} }
// Get all item assorts that have parentid of hideout (base item and not a mod of other item) // Get all item assorts that have parentid of hideout (base item and not a mod of other item)
for (const item of traderAssorts.items.filter(x => x.parentId === "hideout")) for (const item of traderAssorts.items.filter((x) => x.parentId === "hideout"))
{ {
// Get barter scheme (contains cost of item) // Get barter scheme (contains cost of item)
const barterScheme = traderAssorts.barter_scheme[item._id][0][0]; const barterScheme = traderAssorts.barter_scheme[item._id][0][0];
// Convert into roubles // Convert into roubles
const roubleAmount = barterScheme._tpl === Money.ROUBLES const roubleAmount
? barterScheme.count = barterScheme._tpl === Money.ROUBLES
: this.handbookHelper.inRUB(barterScheme.count, barterScheme._tpl); ? barterScheme.count
: this.handbookHelper.inRUB(barterScheme.count, barterScheme._tpl);
// Existing price smaller in dict than current iteration, overwrite // Existing price smaller in dict than current iteration, overwrite
if (this.highestTraderPriceItems[item._tpl] ?? 0 < roubleAmount) if (this.highestTraderPriceItems[item._tpl] ?? 0 < roubleAmount)
@ -478,7 +481,7 @@ export class TraderHelper
*/ */
public getTraderById(traderId: string): Traders public getTraderById(traderId: string): Traders
{ {
const keys = Object.keys(Traders).filter(x => Traders[x] === traderId); const keys = Object.keys(Traders).filter((x) => Traders[x] === traderId);
if (keys.length === 0) if (keys.length === 0)
{ {
@ -523,7 +526,7 @@ export class TraderHelper
*/ */
public traderEnumHasKey(key: string): boolean public traderEnumHasKey(key: string): boolean
{ {
return Object.keys(Traders).some(x => x === key); return Object.keys(Traders).some((x) => x === key);
} }
/** /**
@ -533,6 +536,6 @@ export class TraderHelper
*/ */
public traderEnumHasValue(traderId: string): boolean public traderEnumHasValue(traderId: string): boolean
{ {
return Object.values(Traders).some(x => x === traderId); return Object.values(Traders).some((x) => x === traderId);
} }
} }

View File

@ -5,6 +5,6 @@ export class UtilityHelper
{ {
public arrayIntersect<T>(a: T[], b: T[]): T[] public arrayIntersect<T>(a: T[], b: T[]): T[]
{ {
return a.filter(x => b.includes(x)); return a.filter((x) => b.includes(x));
} }
} }

View File

@ -58,7 +58,9 @@ export class BundleLoader
public addBundles(modpath: string): void public addBundles(modpath: string): void
{ {
const bundleManifestArr = this.jsonUtil.deserialize<BundleManifest>(this.vfs.readFile(`${modpath}bundles.json`)).manifest; const bundleManifestArr = this.jsonUtil.deserialize<BundleManifest>(
this.vfs.readFile(`${modpath}bundles.json`),
).manifest;
for (const bundleManifest of bundleManifestArr) for (const bundleManifest of bundleManifestArr)
{ {

View File

@ -69,11 +69,13 @@ export class ModTypeCheck
*/ */
public isPostV3Compatible(mod: any): boolean public isPostV3Compatible(mod: any): boolean
{ {
return this.isPreAkiLoad(mod) return (
|| this.isPostAkiLoad(mod) this.isPreAkiLoad(mod)
|| this.isPostDBAkiLoad(mod) || this.isPostAkiLoad(mod)
|| this.isPreAkiLoadAsync(mod) || this.isPostDBAkiLoad(mod)
|| this.isPostAkiLoadAsync(mod) || this.isPreAkiLoadAsync(mod)
|| this.isPostDBAkiLoadAsync(mod); || this.isPostAkiLoadAsync(mod)
|| this.isPostDBAkiLoadAsync(mod)
);
} }
} }

View File

@ -93,10 +93,10 @@ export class PreAkiModLoader implements IModLoader
for (const modName in modsGroupedByName) for (const modName in modsGroupedByName)
{ {
const modDatas = modsGroupedByName[modName]; const modDatas = modsGroupedByName[modName];
const modVersions = modDatas.map(x => x.version); const modVersions = modDatas.map((x) => x.version);
const highestVersion = maxSatisfying(modVersions, "*"); const highestVersion = maxSatisfying(modVersions, "*");
const chosenVersion = modDatas.find(x => x.name === modName && x.version === highestVersion); const chosenVersion = modDatas.find((x) => x.name === modName && x.version === highestVersion);
if (!chosenVersion) if (!chosenVersion)
{ {
continue; continue;
@ -174,7 +174,8 @@ export class PreAkiModLoader implements IModLoader
// if the mod has library dependencies check if these dependencies are bundled in the server, if not install them // if the mod has library dependencies check if these dependencies are bundled in the server, if not install them
if ( if (
modToValidate.dependencies && Object.keys(modToValidate.dependencies).length > 0 modToValidate.dependencies
&& Object.keys(modToValidate.dependencies).length > 0
&& !this.vfs.exists(`${this.basepath}${modFolderName}/node_modules`) && !this.vfs.exists(`${this.basepath}${modFolderName}/node_modules`)
) )
{ {
@ -267,7 +268,7 @@ export class PreAkiModLoader implements IModLoader
for (const mod of modPackageData.values()) for (const mod of modPackageData.values())
{ {
const name = `${mod.author}-${mod.name}`; const name = `${mod.author}-${mod.name}`;
grouppedMods.set(name, [...grouppedMods.get(name) ?? [], mod]); grouppedMods.set(name, [...(grouppedMods.get(name) ?? []), mod]);
// if there's more than one entry for a given mod it means there's at least 2 mods with the same author and name trying to load. // if there's more than one entry for a given mod it means there's at least 2 mods with the same author and name trying to load.
if (grouppedMods.get(name).length > 1 && !this.skippedMods.has(name)) if (grouppedMods.get(name).length > 1 && !this.skippedMods.has(name))
@ -641,7 +642,7 @@ export class PreAkiModLoader implements IModLoader
const modIsCalledSrc = modName.toLowerCase() === "src"; const modIsCalledSrc = modName.toLowerCase() === "src";
const modIsCalledDb = modName.toLowerCase() === "db"; const modIsCalledDb = modName.toLowerCase() === "db";
const hasBepinExFolderStructure = this.vfs.exists(`${modPath}/plugins`); const hasBepinExFolderStructure = this.vfs.exists(`${modPath}/plugins`);
const containsDll = this.vfs.getFiles(`${modPath}`).find(x => x.includes(".dll")); const containsDll = this.vfs.getFiles(`${modPath}`).find((x) => x.includes(".dll"));
if (modIsCalledSrc || modIsCalledDb || modIsCalledUser) if (modIsCalledSrc || modIsCalledDb || modIsCalledUser)
{ {
@ -691,7 +692,8 @@ export class PreAkiModLoader implements IModLoader
if ("main" in config) if ("main" in config)
{ {
if (config.main.split(".").pop() !== "js") if (config.main.split(".").pop() !== "js")
{ // expects js file as entry {
// expects js file as entry
this.logger.error(this.localisationService.getText("modloader-main_property_not_js", modName)); this.logger.error(this.localisationService.getText("modloader-main_property_not_js", modName));
issue = true; issue = true;
} }

View File

@ -1,3 +1,2 @@
export interface IEmptyRequestData export interface IEmptyRequestData
{ {}
}

View File

@ -386,9 +386,9 @@ export interface Productive
ProductionTime?: number ProductionTime?: number
GivenItemsInStart?: string[] GivenItemsInStart?: string[]
Interrupted?: boolean Interrupted?: boolean
Code?: string; Code?: string
Decoded?: boolean; Decoded?: boolean
AvailableForFinish?: boolean; AvailableForFinish?: boolean
/** Used in hideout production.json */ /** Used in hideout production.json */
needFuelForAllProductionTime?: boolean needFuelForAllProductionTime?: boolean
/** Used when sending data to client */ /** Used when sending data to client */
@ -459,8 +459,7 @@ export interface Notes
} }
export interface CarExtractCounts export interface CarExtractCounts
{ {}
}
export enum SurvivorClass export enum SurvivorClass
{ {

View File

@ -1,14 +1,11 @@
export interface IAcceptFriendRequestData extends IBaseFriendRequest export interface IAcceptFriendRequestData extends IBaseFriendRequest
{ {}
}
export interface ICancelFriendRequestData extends IBaseFriendRequest export interface ICancelFriendRequestData extends IBaseFriendRequest
{ {}
}
export interface IDeclineFriendRequestData extends IBaseFriendRequest export interface IDeclineFriendRequestData extends IBaseFriendRequest
{ {}
}
export interface IBaseFriendRequest export interface IBaseFriendRequest
{ {

View File

@ -1,5 +1,5 @@
export interface IAddUserGroupMailRequest export interface IAddUserGroupMailRequest
{ {
dialogId: string; dialogId: string
uid: string; uid: string
} }

View File

@ -1,5 +1,5 @@
export interface IChangeGroupMailOwnerRequest export interface IChangeGroupMailOwnerRequest
{ {
dialogId: string; dialogId: string
uid: string; uid: string
} }

View File

@ -1,5 +1,5 @@
export interface ICreateGroupMailRequest export interface ICreateGroupMailRequest
{ {
Name: string; Name: string
Users: string[]; Users: string[]
} }

View File

@ -1,5 +1,5 @@
export interface IRemoveUserGroupMailRequest export interface IRemoveUserGroupMailRequest
{ {
dialogId: string; dialogId: string
uid: string; uid: string
} }

View File

@ -17,8 +17,7 @@ export interface IQteData
area: HideoutAreas area: HideoutAreas
areaLevel: number areaLevel: number
quickTimeEvents: IQuickTimeEvent[] quickTimeEvents: IQuickTimeEvent[]
requirements: requirements: (
(
| IAreaRequirement | IAreaRequirement
| IItemRequirement | IItemRequirement
| ITraderUnlockRequirement | ITraderUnlockRequirement

View File

@ -1,8 +1,7 @@
import { IBaseInteractionRequestData } from "@spt-aki/models/eft/common/request/IBaseInteractionRequestData"; import { IBaseInteractionRequestData } from "@spt-aki/models/eft/common/request/IBaseInteractionRequestData";
export interface IInventoryBaseActionRequestData extends IBaseInteractionRequestData export interface IInventoryBaseActionRequestData extends IBaseInteractionRequestData
{ {}
}
export interface To export interface To
{ {

View File

@ -2,6 +2,4 @@ import { IWsNotificationEvent } from "@spt-aki/models/eft/ws/IWsNotificationEven
import { IGroupCharacter } from "../match/IGroupCharacter"; import { IGroupCharacter } from "../match/IGroupCharacter";
export interface IWsGroupMatchInviteAccept extends IWsNotificationEvent, IGroupCharacter export interface IWsGroupMatchInviteAccept extends IWsNotificationEvent, IGroupCharacter
{ {}
}

View File

@ -1,6 +1,4 @@
import { IWsNotificationEvent } from "@spt-aki/models/eft/ws/IWsNotificationEvent"; import { IWsNotificationEvent } from "@spt-aki/models/eft/ws/IWsNotificationEvent";
export interface IWsPing extends IWsNotificationEvent export interface IWsPing extends IWsNotificationEvent
{ {}
}

View File

@ -1,5 +1,5 @@
export enum MemberCategory // player type export enum MemberCategory
{ { // player type
DEFAULT = 0, DEFAULT = 0,
DEVELOPER = 1, DEVELOPER = 1,
UNIQUE_ID = 2, UNIQUE_ID = 2,

View File

@ -10,5 +10,5 @@ export enum QuestRewardType
TRADER_STANDING_RESET = "TraderStandingReset", TRADER_STANDING_RESET = "TraderStandingReset",
TRADER_STANDING_RESTORE = "TraderStandingRestore", TRADER_STANDING_RESTORE = "TraderStandingRestore",
STASH_ROWS = "StashRows", STASH_ROWS = "StashRows",
ACHIEVEMENT = "Achievement" ACHIEVEMENT = "Achievement",
} }

View File

@ -12,7 +12,7 @@ export type HandleFn = (_: string, req: IncomingMessage, resp: ServerResponse) =
*/ */
export const Listen = (basePath: string) => export const Listen = (basePath: string) =>
{ {
return <T extends { new(...args: any[]): any }>(Base: T): T => return <T extends { new (...args: any[]): any }>(Base: T): T =>
{ {
// Used for the base class to be able to use DI // Used for the base class to be able to use DI
injectable()(Base); injectable()(Base);
@ -61,8 +61,10 @@ export const Listen = (basePath: string) =>
{ {
const routesHandles = this.handlers[req.method]; const routesHandles = this.handlers[req.method];
return Object.keys(this.handlers).some(meth => meth === req.method) return (
&& Object.keys(routesHandles).some(route => new RegExp(route).test(req.url)); Object.keys(this.handlers).some((meth) => meth === req.method)
&& Object.keys(routesHandles).some((route) => new RegExp(route).test(req.url))
);
}; };
// The actual handle method dispatches the request to the registered handlers // The actual handle method dispatches the request to the registered handlers
@ -81,7 +83,7 @@ export const Listen = (basePath: string) =>
routes.sort((routeA, routeB) => routeB.length - routeA.length); routes.sort((routeA, routeB) => routeB.length - routeA.length);
// Filter to select valid routes but only use the first element since it's the most precise // Filter to select valid routes but only use the first element since it's the most precise
const validRoutes = routes.filter(handlerKey => new RegExp(handlerKey).test(route)); const validRoutes = routes.filter((handlerKey) => new RegExp(handlerKey).test(route));
if (validRoutes.length > 0) if (validRoutes.length > 0)
{ {
routesHandles[validRoutes[0]](sessionID, req, resp); routesHandles[validRoutes[0]](sessionID, req, resp);

View File

@ -5,7 +5,11 @@ export class ExhaustableArray<T> implements IExhaustableArray<T>
{ {
private pool: T[]; private pool: T[];
constructor(private itemPool: T[], private randomUtil: RandomUtil, private cloner: ICloner) constructor(
private itemPool: T[],
private randomUtil: RandomUtil,
private cloner: ICloner,
)
{ {
this.pool = this.cloner.clone(itemPool); this.pool = this.cloner.clone(itemPool);
} }

View File

@ -47,7 +47,7 @@ export class HttpRouter
} }
// TODO: Temporary hack to change ItemEventRouter response sessionID binding to what client expects // TODO: Temporary hack to change ItemEventRouter response sessionID binding to what client expects
if (wrapper.output?.includes("\"profileChanges\":{")) if (wrapper.output?.includes('"profileChanges":{'))
{ {
wrapper.output = wrapper.output.replace(sessionID, sessionID); wrapper.output = wrapper.output.replace(sessionID, sessionID);
} }

View File

@ -34,7 +34,7 @@ export class ItemEventRouter
{ {
const pmcData = this.profileHelper.getPmcProfile(sessionID); const pmcData = this.profileHelper.getPmcProfile(sessionID);
const eventRouter = this.itemEventRouters.find(r => r.canHandle(body.Action)); const eventRouter = this.itemEventRouters.find((r) => r.canHandle(body.Action));
if (eventRouter) if (eventRouter)
{ {
this.logger.debug(`event: ${body.Action}`); this.logger.debug(`event: ${body.Action}`);

View File

@ -13,7 +13,8 @@ export class HealthSaveLoadRouter extends SaveLoadRouter
public override handleLoad(profile: IAkiProfile): IAkiProfile public override handleLoad(profile: IAkiProfile): IAkiProfile
{ {
if (!profile.vitality) if (!profile.vitality)
{ // Occurs on newly created profiles {
// Occurs on newly created profiles
profile.vitality = { health: null, effects: null }; profile.vitality = { health: null, effects: null };
} }
profile.vitality.health = { profile.vitality.health = {

View File

@ -26,9 +26,10 @@ export class NotifySerializer extends Serializer
* Take our array of JSON message objects and cast them to JSON strings, so that they can then * Take our array of JSON message objects and cast them to JSON strings, so that they can then
* be sent to client as NEWLINE separated strings... yup. * be sent to client as NEWLINE separated strings... yup.
*/ */
this.notifierController.notifyAsync(tmpSessionID).then((messages: any) => this.notifierController
messages.map((message: any) => this.jsonUtil.serialize(message)).join("\n"), .notifyAsync(tmpSessionID)
).then(text => this.httpServerHelper.sendTextJson(resp, text)); .then((messages: any) => messages.map((message: any) => this.jsonUtil.serialize(message)).join("\n"))
.then((text) => this.httpServerHelper.sendTextJson(resp, text));
} }
public override canHandle(route: string): boolean public override canHandle(route: string): boolean

View File

@ -160,7 +160,12 @@ export class MatchStaticRouter extends StaticRouter
), ),
new RouteAction( new RouteAction(
"/client/match/group/invite/decline", "/client/match/group/invite/decline",
async (url: string, info: any, sessionID: string, output: string): Promise<IGetBodyResponseData<any>> => async (
url: string,
info: any,
sessionID: string,
output: string,
): Promise<IGetBodyResponseData<any>> =>
{ {
return this.matchCallbacks.declineGroupInvite(url, info, sessionID); return this.matchCallbacks.declineGroupInvite(url, info, sessionID);
}, },

View File

@ -60,14 +60,24 @@ export class ProfileStaticRouter extends StaticRouter
), ),
new RouteAction( new RouteAction(
"/client/game/profile/nickname/change", "/client/game/profile/nickname/change",
async (url: string, info: any, sessionID: string, output: string): Promise<IGetBodyResponseData<any>> => async (
url: string,
info: any,
sessionID: string,
output: string,
): Promise<IGetBodyResponseData<any>> =>
{ {
return this.profileCallbacks.changeNickname(url, info, sessionID); return this.profileCallbacks.changeNickname(url, info, sessionID);
}, },
), ),
new RouteAction( new RouteAction(
"/client/game/profile/nickname/validate", "/client/game/profile/nickname/validate",
async (url: string, info: any, sessionID: string, output: string): Promise<IGetBodyResponseData<any>> => async (
url: string,
info: any,
sessionID: string,
output: string,
): Promise<IGetBodyResponseData<any>> =>
{ {
return this.profileCallbacks.validateNickname(url, info, sessionID); return this.profileCallbacks.validateNickname(url, info, sessionID);
}, },

View File

@ -1,6 +1,5 @@
import { inject, injectable } from "tsyringe"; import { inject, injectable } from "tsyringe";
import { ConfigTypes } from "@spt-aki/models/enums/ConfigTypes"; import { ConfigTypes } from "@spt-aki/models/enums/ConfigTypes";
import { ICoreConfig } from "@spt-aki/models/spt/config/ICoreConfig";
import { ILogger } from "@spt-aki/models/spt/utils/ILogger"; import { ILogger } from "@spt-aki/models/spt/utils/ILogger";
import { JsonUtil } from "@spt-aki/utils/JsonUtil"; import { JsonUtil } from "@spt-aki/utils/JsonUtil";
import { VFS } from "@spt-aki/utils/VFS"; import { VFS } from "@spt-aki/utils/VFS";

View File

@ -95,10 +95,12 @@ export class HttpServer
} }
else else
{ {
this.logger.info(this.localisationService.getText("client_request_ip", { this.logger.info(
ip: clientIp, this.localisationService.getText("client_request_ip", {
url: req.url.replaceAll("/", "\\"), // Localisation service escapes `/` into hex code `&#x2f;` ip: clientIp,
})); url: req.url.replaceAll("/", "\\"), // Localisation service escapes `/` into hex code `&#x2f;`
}),
);
} }
} }
} }
@ -125,9 +127,11 @@ export class HttpServer
return undefined; return undefined;
} }
return remoteAddress.startsWith("127.0.0") return (
|| remoteAddress.startsWith("192.168.") remoteAddress.startsWith("127.0.0")
|| remoteAddress.startsWith("localhost"); || remoteAddress.startsWith("192.168.")
|| remoteAddress.startsWith("localhost")
);
} }
protected getCookies(req: IncomingMessage): Record<string, string> protected getCookies(req: IncomingMessage): Record<string, string>

View File

@ -79,7 +79,7 @@ export class RagfairServer
*/ */
public getUpdateableTraders(): string[] public getUpdateableTraders(): string[]
{ {
return Object.keys(this.ragfairConfig.traders).filter(x => this.ragfairConfig.traders[x]); return Object.keys(this.ragfairConfig.traders).filter((x) => this.ragfairConfig.traders[x]);
} }
public getAllActiveCategories( public getAllActiveCategories(
@ -98,7 +98,7 @@ export class RagfairServer
public hideOffer(offerId: string): void public hideOffer(offerId: string): void
{ {
const offers = this.ragfairOfferService.getOffers(); const offers = this.ragfairOfferService.getOffers();
const offer = offers.find(x => x._id === offerId); const offer = offers.find((x) => x._id === offerId);
if (!offer) if (!offer)
{ {

View File

@ -21,8 +21,7 @@ export class AkiHttpListener implements IHttpListener
@inject("HttpResponseUtil") protected httpResponse: HttpResponseUtil, @inject("HttpResponseUtil") protected httpResponse: HttpResponseUtil,
@inject("LocalisationService") protected localisationService: LocalisationService, @inject("LocalisationService") protected localisationService: LocalisationService,
) )
{ {}
}
public canHandle(_: string, req: IncomingMessage): boolean public canHandle(_: string, req: IncomingMessage): boolean
{ {
@ -153,7 +152,7 @@ export class AkiHttpListener implements IHttpListener
{ {
this.logger.error(this.localisationService.getText("unhandled_response", req.url)); this.logger.error(this.localisationService.getText("unhandled_response", req.url));
this.logger.info(info); this.logger.info(info);
output = <string><unknown> this.httpResponse.getBody(null, 404, `UNHANDLED RESPONSE: ${req.url}`); output = <string>(<unknown> this.httpResponse.getBody(null, 404, `UNHANDLED RESPONSE: ${req.url}`));
} }
return output; return output;
} }
@ -180,18 +179,28 @@ export class AkiHttpListener implements IHttpListener
class RequestData class RequestData
{ {
constructor(public url: string, public headers: IncomingHttpHeaders, public data?: any) constructor(
public url: string,
public headers: IncomingHttpHeaders,
public data?: any,
)
{} {}
} }
class Request class Request
{ {
constructor(public type: string, public req: RequestData) constructor(
public type: string,
public req: RequestData,
)
{} {}
} }
class Response class Response
{ {
constructor(public type: string, public response: any) constructor(
public type: string,
public response: any,
)
{} {}
} }

View File

@ -99,7 +99,10 @@ export class BotEquipmentFilterService
* @param equipmentChanges Changes to apply * @param equipmentChanges Changes to apply
* @param baseValues data to update * @param baseValues data to update
*/ */
protected adjustChances(equipmentChanges: Record<string, number>, baseValues: EquipmentChances | ModsChances): void protected adjustChances(
equipmentChanges: Record<string, number>,
baseValues: EquipmentChances | ModsChances,
): void
{ {
if (!equipmentChanges) if (!equipmentChanges)
{ {
@ -173,15 +176,16 @@ export class BotEquipmentFilterService
// No equipment blacklist found, skip // No equipment blacklist found, skip
if ( if (
!blacklistDetailsForBot || Object.keys(blacklistDetailsForBot).length === 0 !blacklistDetailsForBot
|| Object.keys(blacklistDetailsForBot).length === 0
|| !blacklistDetailsForBot.blacklist || !blacklistDetailsForBot.blacklist
) )
{ {
return null; return null;
} }
return blacklistDetailsForBot.blacklist.find(x => return blacklistDetailsForBot.blacklist.find(
playerLevel >= x.levelRange.min && playerLevel <= x.levelRange.max, (x) => playerLevel >= x.levelRange.min && playerLevel <= x.levelRange.max,
); );
} }
@ -201,8 +205,8 @@ export class BotEquipmentFilterService
return null; return null;
} }
return botEquipmentConfig.whitelist.find(x => return botEquipmentConfig.whitelist.find(
playerLevel >= x.levelRange.min && playerLevel <= x.levelRange.max, (x) => playerLevel >= x.levelRange.min && playerLevel <= x.levelRange.max,
); );
} }
@ -218,15 +222,16 @@ export class BotEquipmentFilterService
// No config found, skip // No config found, skip
if ( if (
!botEquipmentConfig || Object.keys(botEquipmentConfig).length === 0 !botEquipmentConfig
|| Object.keys(botEquipmentConfig).length === 0
|| !botEquipmentConfig.weightingAdjustmentsByBotLevel || !botEquipmentConfig.weightingAdjustmentsByBotLevel
) )
{ {
return null; return null;
} }
return botEquipmentConfig.weightingAdjustmentsByBotLevel.find(x => return botEquipmentConfig.weightingAdjustmentsByBotLevel.find(
botLevel >= x.levelRange.min && botLevel <= x.levelRange.max, (x) => botLevel >= x.levelRange.min && botLevel <= x.levelRange.max,
); );
} }
@ -236,21 +241,25 @@ export class BotEquipmentFilterService
* @param playerlevel Level of bot * @param playerlevel Level of bot
* @returns Weighting adjustments for bot items * @returns Weighting adjustments for bot items
*/ */
protected getBotWeightingAdjustmentsByPlayerLevel(botRole: string, playerlevel: number): WeightingAdjustmentDetails protected getBotWeightingAdjustmentsByPlayerLevel(
botRole: string,
playerlevel: number,
): WeightingAdjustmentDetails
{ {
const botEquipmentConfig = this.botEquipmentConfig[botRole]; const botEquipmentConfig = this.botEquipmentConfig[botRole];
// No config found, skip // No config found, skip
if ( if (
!botEquipmentConfig || Object.keys(botEquipmentConfig).length === 0 !botEquipmentConfig
|| Object.keys(botEquipmentConfig).length === 0
|| !botEquipmentConfig.weightingAdjustmentsByPlayerLevel || !botEquipmentConfig.weightingAdjustmentsByPlayerLevel
) )
{ {
return null; return null;
} }
return botEquipmentConfig.weightingAdjustmentsByPlayerLevel.find(x => return botEquipmentConfig.weightingAdjustmentsByPlayerLevel.find(
playerlevel >= x.levelRange.min && playerlevel <= x.levelRange.max, (x) => playerlevel >= x.levelRange.min && playerlevel <= x.levelRange.max,
); );
} }

View File

@ -91,7 +91,7 @@ export class BotEquipmentModPoolService
} }
// only add item to pool if it doesnt already exist // only add item to pool if it doesnt already exist
if (!pool[item._id][slot._name].some(x => x === itemToAdd)) if (!pool[item._id][slot._name].some((x) => x === itemToAdd))
{ {
pool[item._id][slot._name].push(itemToAdd); pool[item._id][slot._name].push(itemToAdd);
@ -185,8 +185,8 @@ export class BotEquipmentModPoolService
*/ */
protected generateWeaponPool(): void protected generateWeaponPool(): void
{ {
const weapons = Object.values(this.databaseServer.getTables().templates.items).filter(x => const weapons = Object.values(this.databaseServer.getTables().templates.items).filter(
x._type === "Item" && this.itemHelper.isOfBaseclass(x._id, BaseClasses.WEAPON), (x) => x._type === "Item" && this.itemHelper.isOfBaseclass(x._id, BaseClasses.WEAPON),
); );
this.generatePool(weapons, "weapon"); this.generatePool(weapons, "weapon");
@ -199,14 +199,15 @@ export class BotEquipmentModPoolService
*/ */
protected generateGearPool(): void protected generateGearPool(): void
{ {
const gear = Object.values(this.databaseServer.getTables().templates.items).filter(x => const gear = Object.values(this.databaseServer.getTables().templates.items).filter(
x._type === "Item" (x) =>
&& this.itemHelper.isOfBaseclasses(x._id, [ x._type === "Item"
BaseClasses.ARMORED_EQUIPMENT, && this.itemHelper.isOfBaseclasses(x._id, [
BaseClasses.VEST, BaseClasses.ARMORED_EQUIPMENT,
BaseClasses.ARMOR, BaseClasses.VEST,
BaseClasses.HEADWEAR, BaseClasses.ARMOR,
]), BaseClasses.HEADWEAR,
]),
); );
this.generatePool(gear, "gear"); this.generatePool(gear, "gear");

View File

@ -79,7 +79,7 @@ export class BotGenerationCacheService
*/ */
public getUsedBot(profileId: string): IBotBase public getUsedBot(profileId: string): IBotBase
{ {
return this.activeBotsInRaid.find(x => x._id === profileId); return this.activeBotsInRaid.find((x) => x._id === profileId);
} }
/** /**

View File

@ -90,7 +90,7 @@ export class BotWeaponModLimitService
{ {
// If weapon already has a longer ranged scope on it, allow ncstar to be spawned // If weapon already has a longer ranged scope on it, allow ncstar to be spawned
if ( if (
weapon.some(x => weapon.some((x) =>
this.itemHelper.isOfBaseclasses(x._tpl, [ this.itemHelper.isOfBaseclasses(x._tpl, [
BaseClasses.ASSAULT_SCOPE, BaseClasses.ASSAULT_SCOPE,
BaseClasses.OPTIC_SCOPE, BaseClasses.OPTIC_SCOPE,
@ -121,7 +121,7 @@ export class BotWeaponModLimitService
// Mod is a mount that can hold only scopes and limit is reached (dont want to add empty mounts if limit is reached) // Mod is a mount that can hold only scopes and limit is reached (dont want to add empty mounts if limit is reached)
if ( if (
this.itemHelper.isOfBaseclass(modTemplate._id, BaseClasses.MOUNT) this.itemHelper.isOfBaseclass(modTemplate._id, BaseClasses.MOUNT)
&& modTemplate._props.Slots.some(x => x._name === "mod_scope") && modTemplate._props.Slots.some((x) => x._name === "mod_scope")
&& modTemplate._props.Slots.length === 1 && modTemplate._props.Slots.length === 1
&& modLimits.scope.count >= modLimits.scopeMax && modLimits.scope.count >= modLimits.scopeMax
) )
@ -144,7 +144,7 @@ export class BotWeaponModLimitService
// Mod is a mount that can hold only flashlights ad limit is reached (dont want to add empty mounts if limit is reached) // Mod is a mount that can hold only flashlights ad limit is reached (dont want to add empty mounts if limit is reached)
if ( if (
this.itemHelper.isOfBaseclass(modTemplate._id, BaseClasses.MOUNT) this.itemHelper.isOfBaseclass(modTemplate._id, BaseClasses.MOUNT)
&& modTemplate._props.Slots.some(x => x._name === "mod_flashlight") && modTemplate._props.Slots.some((x) => x._name === "mod_flashlight")
&& modTemplate._props.Slots.length === 1 && modTemplate._props.Slots.length === 1
&& modLimits.scope.count >= modLimits.scopeMax && modLimits.scope.count >= modLimits.scopeMax
) )

View File

@ -73,7 +73,7 @@ export class CustomLocationWaveService
const location: ILocationBase = this.databaseServer.getTables().locations[mapKey].base; const location: ILocationBase = this.databaseServer.getTables().locations[mapKey].base;
for (const bossWave of bossWavesToApply[mapKey]) for (const bossWave of bossWavesToApply[mapKey])
{ {
if (location.BossLocationSpawn.find(x => x.sptId === bossWave.sptId)) if (location.BossLocationSpawn.find((x) => x.sptId === bossWave.sptId))
{ {
// Already exists, skip // Already exists, skip
continue; continue;
@ -90,7 +90,7 @@ export class CustomLocationWaveService
const location: ILocationBase = this.databaseServer.getTables().locations[mapKey].base; const location: ILocationBase = this.databaseServer.getTables().locations[mapKey].base;
for (const normalWave of normalWavesToApply[mapKey]) for (const normalWave of normalWavesToApply[mapKey])
{ {
if (location.waves.find(x => x.sptId === normalWave.sptId)) if (location.waves.find((x) => x.sptId === normalWave.sptId))
{ {
// Already exists, skip // Already exists, skip
continue; continue;

View File

@ -388,23 +388,28 @@ export class FenceService
* @param newFenceAssorts Assorts to fold into existing fence assorts * @param newFenceAssorts Assorts to fold into existing fence assorts
* @param existingFenceAssorts Current fence assorts new assorts will be added to * @param existingFenceAssorts Current fence assorts new assorts will be added to
*/ */
protected updateFenceAssorts(newFenceAssorts: ICreateFenceAssortsResult, existingFenceAssorts: ITraderAssort): void protected updateFenceAssorts(
newFenceAssorts: ICreateFenceAssortsResult,
existingFenceAssorts: ITraderAssort,
): void
{ {
for (const itemWithChildren of newFenceAssorts.sptItems) for (const itemWithChildren of newFenceAssorts.sptItems)
{ {
// Find the root item // Find the root item
const newRootItem = itemWithChildren.find(item => item.slotId === "hideout"); const newRootItem = itemWithChildren.find((item) => item.slotId === "hideout");
if (!newRootItem) if (!newRootItem)
{ {
const firstItem = itemWithChildren.find(x => x); const firstItem = itemWithChildren.find((x) => x);
this.logger.error(`Unable to process fence assort as root item is missing, ${firstItem?._tpl}, skipping`); this.logger.error(
`Unable to process fence assort as root item is missing, ${firstItem?._tpl}, skipping`,
);
continue; continue;
} }
// Find a matching root item with same tpl in existing assort // Find a matching root item with same tpl in existing assort
const existingRootItem = existingFenceAssorts.items.find(item => const existingRootItem = existingFenceAssorts.items.find(
item._tpl === newRootItem._tpl && item.slotId === "hideout", (item) => item._tpl === newRootItem._tpl && item.slotId === "hideout",
); );
// Check if same type of item exists + its on list of item types to always stack // Check if same type of item exists + its on list of item types to always stack
@ -453,8 +458,8 @@ export class FenceService
*/ */
protected incrementPartialRefreshTime(): void protected incrementPartialRefreshTime(): void
{ {
this.nextPartialRefreshTimestamp = this.timeUtil.getTimestamp() this.nextPartialRefreshTimestamp
+ this.traderConfig.fence.partialRefreshTimeSeconds; = this.timeUtil.getTimestamp() + this.traderConfig.fence.partialRefreshTimeSeconds;
} }
/** /**
@ -468,8 +473,8 @@ export class FenceService
generationValues: IGenerationAssortValues, generationValues: IGenerationAssortValues,
): IGenerationAssortValues ): IGenerationAssortValues
{ {
const allRootItems = assortItems.filter(item => item.slotId === "hideout"); const allRootItems = assortItems.filter((item) => item.slotId === "hideout");
const rootPresetItems = allRootItems.filter(item => item?.upd?.sptPresetId); const rootPresetItems = allRootItems.filter((item) => item?.upd?.sptPresetId);
// Get count of weapons // Get count of weapons
const currentWeaponPresetCount = rootPresetItems.reduce((count, item) => const currentWeaponPresetCount = rootPresetItems.reduce((count, item) =>
@ -507,7 +512,7 @@ export class FenceService
{ {
if (assort?.items?.length > 0) if (assort?.items?.length > 0)
{ {
const rootItems = assort.items.filter(item => item.slotId === "hideout"); const rootItems = assort.items.filter((item) => item.slotId === "hideout");
for (let index = 0; index < itemCountToReplace; index++) for (let index = 0; index < itemCountToReplace; index++)
{ {
this.removeRandomItemFromAssorts(assort, rootItems); this.removeRandomItemFromAssorts(assort, rootItems);
@ -735,8 +740,8 @@ export class FenceService
): void ): void
{ {
const priceLimits = this.traderConfig.fence.itemCategoryRoublePriceLimit; const priceLimits = this.traderConfig.fence.itemCategoryRoublePriceLimit;
const assortRootItems = baseFenceAssortClone.items.filter(item => const assortRootItems = baseFenceAssortClone.items.filter(
item.parentId === "hideout" && !item.upd?.sptPresetId, (item) => item.parentId === "hideout" && !item.upd?.sptPresetId,
); );
for (let i = 0; i < assortCount; i++) for (let i = 0; i < assortCount; i++)
@ -806,7 +811,8 @@ export class FenceService
const existingItemThatMatches = this.getMatchingItem(rootItemBeingAdded, itemDbDetails, assorts.sptItems); const existingItemThatMatches = this.getMatchingItem(rootItemBeingAdded, itemDbDetails, assorts.sptItems);
const shouldBeStacked = this.itemShouldBeForceStacked(existingItemThatMatches, itemDbDetails); const shouldBeStacked = this.itemShouldBeForceStacked(existingItemThatMatches, itemDbDetails);
if (shouldBeStacked && existingItemThatMatches) if (shouldBeStacked && existingItemThatMatches)
{ // Decrement loop counter so another items gets added {
// Decrement loop counter so another items gets added
i--; i--;
existingItemThatMatches.upd.StackObjectsCount++; existingItemThatMatches.upd.StackObjectsCount++;
@ -843,12 +849,18 @@ export class FenceService
* @param itemsWithChildren Items to search through * @param itemsWithChildren Items to search through
* @returns Matching assort item * @returns Matching assort item
*/ */
protected getMatchingItem(rootItemBeingAdded: Item, itemDbDetails: ITemplateItem, itemsWithChildren: Item[][]): Item protected getMatchingItem(
rootItemBeingAdded: Item,
itemDbDetails: ITemplateItem,
itemsWithChildren: Item[][],
): Item
{ {
// Get matching root items // Get matching root items
const matchingItems = itemsWithChildren.filter(itemWithChildren => const matchingItems = itemsWithChildren
itemWithChildren.find(item => item._tpl === rootItemBeingAdded._tpl && item.parentId === "hideout"), .filter((itemWithChildren) =>
).flatMap(x => x); itemWithChildren.find((item) => item._tpl === rootItemBeingAdded._tpl && item.parentId === "hideout"),
)
.flatMap((x) => x);
if (matchingItems.length === 0) if (matchingItems.length === 0)
{ {
// Nothing matches by tpl and is root item, exit early // Nothing matches by tpl and is root item, exit early
@ -998,8 +1010,8 @@ export class FenceService
let weaponPresetsAddedCount = 0; let weaponPresetsAddedCount = 0;
if (desiredWeaponPresetsCount > 0) if (desiredWeaponPresetsCount > 0)
{ {
const weaponPresetRootItems = baseFenceAssort.items.filter(item => const weaponPresetRootItems = baseFenceAssort.items.filter(
item.upd?.sptPresetId && this.itemHelper.isOfBaseclass(item._tpl, BaseClasses.WEAPON), (item) => item.upd?.sptPresetId && this.itemHelper.isOfBaseclass(item._tpl, BaseClasses.WEAPON),
); );
while (weaponPresetsAddedCount < desiredWeaponPresetsCount) while (weaponPresetsAddedCount < desiredWeaponPresetsCount)
{ {
@ -1021,8 +1033,9 @@ export class FenceService
// Check chosen item is below price cap // Check chosen item is below price cap
const priceLimitRouble = this.traderConfig.fence.itemCategoryRoublePriceLimit[rootItemDb._parent]; const priceLimitRouble = this.traderConfig.fence.itemCategoryRoublePriceLimit[rootItemDb._parent];
const itemPrice = this.handbookHelper.getTemplatePriceForItems(presetWithChildrenClone) const itemPrice
* this.itemHelper.getItemQualityModifierForOfferItems(presetWithChildrenClone); = this.handbookHelper.getTemplatePriceForItems(presetWithChildrenClone)
* this.itemHelper.getItemQualityModifierForOfferItems(presetWithChildrenClone);
if (priceLimitRouble) if (priceLimitRouble)
{ {
if (itemPrice > priceLimitRouble) if (itemPrice > priceLimitRouble)
@ -1043,10 +1056,14 @@ export class FenceService
// Set assort price // Set assort price
// Must be careful to use correct id as the item has had its IDs regenerated // Must be careful to use correct id as the item has had its IDs regenerated
assorts.barter_scheme[presetWithChildrenClone[0]._id] = [[{ assorts.barter_scheme[presetWithChildrenClone[0]._id] = [
_tpl: "5449016a4bdc2d6f028b456f", [
count: Math.round(itemPrice), {
}]]; _tpl: "5449016a4bdc2d6f028b456f",
count: Math.round(itemPrice),
},
],
];
assorts.loyal_level_items[presetWithChildrenClone[0]._id] = loyaltyLevel; assorts.loyal_level_items[presetWithChildrenClone[0]._id] = loyaltyLevel;
weaponPresetsAddedCount++; weaponPresetsAddedCount++;
@ -1059,8 +1076,8 @@ export class FenceService
return; return;
} }
const equipmentPresetRootItems = baseFenceAssort.items.filter(item => const equipmentPresetRootItems = baseFenceAssort.items.filter(
item.upd?.sptPresetId && this.itemHelper.armorItemCanHoldMods(item._tpl), (item) => item.upd?.sptPresetId && this.itemHelper.armorItemCanHoldMods(item._tpl),
); );
while (equipmentPresetsAddedCount < desiredEquipmentPresetsCount) while (equipmentPresetsAddedCount < desiredEquipmentPresetsCount)
{ {
@ -1081,8 +1098,9 @@ export class FenceService
// Check chosen item is below price cap // Check chosen item is below price cap
const priceLimitRouble = this.traderConfig.fence.itemCategoryRoublePriceLimit[rootItemDb._parent]; const priceLimitRouble = this.traderConfig.fence.itemCategoryRoublePriceLimit[rootItemDb._parent];
const itemPrice = this.handbookHelper.getTemplatePriceForItems(presetWithChildrenClone) const itemPrice
* this.itemHelper.getItemQualityModifierForOfferItems(presetWithChildrenClone); = this.handbookHelper.getTemplatePriceForItems(presetWithChildrenClone)
* this.itemHelper.getItemQualityModifierForOfferItems(presetWithChildrenClone);
if (priceLimitRouble) if (priceLimitRouble)
{ {
if (itemPrice > priceLimitRouble) if (itemPrice > priceLimitRouble)
@ -1103,10 +1121,14 @@ export class FenceService
// Set assort price // Set assort price
// Must be careful to use correct id as the item has had its IDs regenerated // Must be careful to use correct id as the item has had its IDs regenerated
assorts.barter_scheme[presetWithChildrenClone[0]._id] = [[{ assorts.barter_scheme[presetWithChildrenClone[0]._id] = [
_tpl: "5449016a4bdc2d6f028b456f", [
count: Math.round(itemPrice), {
}]]; _tpl: "5449016a4bdc2d6f028b456f",
count: Math.round(itemPrice),
},
],
];
assorts.loyal_level_items[presetWithChildrenClone[0]._id] = loyaltyLevel; assorts.loyal_level_items[presetWithChildrenClone[0]._id] = loyaltyLevel;
equipmentPresetsAddedCount++; equipmentPresetsAddedCount++;
@ -1128,7 +1150,7 @@ export class FenceService
} }
// Check for and adjust soft insert durability values // Check for and adjust soft insert durability values
const requiredSlots = itemDbDetails._props.Slots.filter(slot => slot._required); const requiredSlots = itemDbDetails._props.Slots.filter((slot) => slot._required);
const hasRequiredSlots = requiredSlots.length > 0; const hasRequiredSlots = requiredSlots.length > 0;
if (hasRequiredSlots) if (hasRequiredSlots)
{ {
@ -1147,8 +1169,8 @@ export class FenceService
} }
// Find items mod to apply dura changes to // Find items mod to apply dura changes to
const modItemToAdjust = armor.find(mod => const modItemToAdjust = armor.find(
mod.slotId.toLowerCase() === requiredSlot._name.toLowerCase(), (mod) => mod.slotId.toLowerCase() === requiredSlot._name.toLowerCase(),
); );
this.itemHelper.addUpdObjectToItem(modItemToAdjust); this.itemHelper.addUpdObjectToItem(modItemToAdjust);
@ -1170,14 +1192,15 @@ export class FenceService
&& modItemToAdjust.slotId === "mod_equipment_000" && modItemToAdjust.slotId === "mod_equipment_000"
&& modItemToAdjust.upd.Repairable.Durability < modItemDbDetails._props.MaxDurability && modItemToAdjust.upd.Repairable.Durability < modItemDbDetails._props.MaxDurability
) )
{ // Is damaged {
// Is damaged
modItemToAdjust.upd.FaceShield = { Hits: this.randomUtil.getInt(1, 3) }; modItemToAdjust.upd.FaceShield = { Hits: this.randomUtil.getInt(1, 3) };
} }
} }
} }
// Check for and adjust plate durability values // Check for and adjust plate durability values
const plateSlots = itemDbDetails._props.Slots.filter(slot => const plateSlots = itemDbDetails._props.Slots.filter((slot) =>
this.itemHelper.isRemovablePlateSlot(slot._name), this.itemHelper.isRemovablePlateSlot(slot._name),
); );
if (plateSlots.length > 0) if (plateSlots.length > 0)
@ -1203,7 +1226,7 @@ export class FenceService
); );
// Find items mod to apply dura changes to // Find items mod to apply dura changes to
const modItemToAdjust = armor.find(mod => mod.slotId.toLowerCase() === plateSlot._name.toLowerCase()); const modItemToAdjust = armor.find((mod) => mod.slotId.toLowerCase() === plateSlot._name.toLowerCase());
this.itemHelper.addUpdObjectToItem(modItemToAdjust); this.itemHelper.addUpdObjectToItem(modItemToAdjust);
if (!modItemToAdjust.upd.Repairable) if (!modItemToAdjust.upd.Repairable)
@ -1341,7 +1364,8 @@ export class FenceService
if ( if (
(itemDetails._parent === BaseClasses.ARMORED_EQUIPMENT (itemDetails._parent === BaseClasses.ARMORED_EQUIPMENT
|| itemDetails._parent === BaseClasses.FACECOVER || itemDetails._parent === BaseClasses.FACECOVER
|| itemDetails._parent === BaseClasses.ARMOR_PLATE) && itemDetails._props.MaxDurability > 0 || itemDetails._parent === BaseClasses.ARMOR_PLATE)
&& itemDetails._props.MaxDurability > 0
) )
{ {
const values = this.getRandomisedArmorDurabilityValues( const values = this.getRandomisedArmorDurabilityValues(
@ -1357,12 +1381,12 @@ export class FenceService
if (this.itemHelper.isOfBaseclass(itemDetails._id, BaseClasses.WEAPON)) if (this.itemHelper.isOfBaseclass(itemDetails._id, BaseClasses.WEAPON))
{ {
const weaponDurabilityLimits = this.traderConfig.fence.weaponDurabilityPercentMinMax; const weaponDurabilityLimits = this.traderConfig.fence.weaponDurabilityPercentMinMax;
const maxDuraMin = weaponDurabilityLimits.max.min / 100 * itemDetails._props.MaxDurability; const maxDuraMin = (weaponDurabilityLimits.max.min / 100) * itemDetails._props.MaxDurability;
const maxDuraMax = weaponDurabilityLimits.max.max / 100 * itemDetails._props.MaxDurability; const maxDuraMax = (weaponDurabilityLimits.max.max / 100) * itemDetails._props.MaxDurability;
const chosenMaxDurability = this.randomUtil.getInt(maxDuraMin, maxDuraMax); const chosenMaxDurability = this.randomUtil.getInt(maxDuraMin, maxDuraMax);
const currentDuraMin = weaponDurabilityLimits.current.min / 100 * itemDetails._props.MaxDurability; const currentDuraMin = (weaponDurabilityLimits.current.min / 100) * itemDetails._props.MaxDurability;
const currentDuraMax = weaponDurabilityLimits.current.max / 100 * itemDetails._props.MaxDurability; const currentDuraMax = (weaponDurabilityLimits.current.max / 100) * itemDetails._props.MaxDurability;
const currentDurability = Math.min( const currentDurability = Math.min(
this.randomUtil.getInt(currentDuraMin, currentDuraMax), this.randomUtil.getInt(currentDuraMin, currentDuraMax),
chosenMaxDurability, chosenMaxDurability,
@ -1414,12 +1438,12 @@ export class FenceService
equipmentDurabilityLimits: IItemDurabilityCurrentMax, equipmentDurabilityLimits: IItemDurabilityCurrentMax,
): Repairable ): Repairable
{ {
const maxDuraMin = equipmentDurabilityLimits.max.min / 100 * itemDetails._props.MaxDurability; const maxDuraMin = (equipmentDurabilityLimits.max.min / 100) * itemDetails._props.MaxDurability;
const maxDuraMax = equipmentDurabilityLimits.max.max / 100 * itemDetails._props.MaxDurability; const maxDuraMax = (equipmentDurabilityLimits.max.max / 100) * itemDetails._props.MaxDurability;
const chosenMaxDurability = this.randomUtil.getInt(maxDuraMin, maxDuraMax); const chosenMaxDurability = this.randomUtil.getInt(maxDuraMin, maxDuraMax);
const currentDuraMin = equipmentDurabilityLimits.current.min / 100 * itemDetails._props.MaxDurability; const currentDuraMin = (equipmentDurabilityLimits.current.min / 100) * itemDetails._props.MaxDurability;
const currentDuraMax = equipmentDurabilityLimits.current.max / 100 * itemDetails._props.MaxDurability; const currentDuraMax = (equipmentDurabilityLimits.current.max / 100) * itemDetails._props.MaxDurability;
const chosenCurrentDurability = Math.min( const chosenCurrentDurability = Math.min(
this.randomUtil.getInt(currentDuraMin, currentDuraMax), this.randomUtil.getInt(currentDuraMin, currentDuraMax),
chosenMaxDurability, chosenMaxDurability,
@ -1462,7 +1486,7 @@ export class FenceService
*/ */
protected getFenceRefreshTime(): number protected getFenceRefreshTime(): number
{ {
const fence = this.traderConfig.updateTime.find(x => x.traderId === Traders.FENCE).seconds; const fence = this.traderConfig.updateTime.find((x) => x.traderId === Traders.FENCE).seconds;
return this.randomUtil.getInt(fence.min, fence.max); return this.randomUtil.getInt(fence.min, fence.max);
} }
@ -1482,7 +1506,7 @@ export class FenceService
return fenceSettings.Levels["0"]; return fenceSettings.Levels["0"];
} }
const fenceLevels = Object.keys(fenceSettings.Levels).map(value => Number.parseInt(value)); const fenceLevels = Object.keys(fenceSettings.Levels).map((value) => Number.parseInt(value));
const minLevel = Math.min(...fenceLevels); const minLevel = Math.min(...fenceLevels);
const maxLevel = Math.max(...fenceLevels); const maxLevel = Math.max(...fenceLevels);
const pmcFenceLevel = Math.floor(pmcFenceInfo.standing); const pmcFenceLevel = Math.floor(pmcFenceInfo.standing);
@ -1508,11 +1532,11 @@ export class FenceService
public amendOrRemoveFenceOffer(assortId: string, buyCount: number): void public amendOrRemoveFenceOffer(assortId: string, buyCount: number): void
{ {
let isNormalAssort = true; let isNormalAssort = true;
let fenceAssortItem = this.fenceAssort.items.find(item => item._id === assortId); let fenceAssortItem = this.fenceAssort.items.find((item) => item._id === assortId);
if (!fenceAssortItem) if (!fenceAssortItem)
{ {
// Not in main assorts, check secondary section // Not in main assorts, check secondary section
fenceAssortItem = this.fenceDiscountAssort.items.find(item => item._id === assortId); fenceAssortItem = this.fenceDiscountAssort.items.find((item) => item._id === assortId);
if (!fenceAssortItem) if (!fenceAssortItem)
{ {
this.logger.error(`Offer with id: ${assortId} not found`); this.logger.error(`Offer with id: ${assortId} not found`);
@ -1540,12 +1564,12 @@ export class FenceService
const itemWithChildrenToRemove = this.itemHelper.findAndReturnChildrenAsItems(assorts, assortId); const itemWithChildrenToRemove = this.itemHelper.findAndReturnChildrenAsItems(assorts, assortId);
for (const itemToRemove of itemWithChildrenToRemove) for (const itemToRemove of itemWithChildrenToRemove)
{ {
let indexToRemove = assorts.findIndex(item => item._id === itemToRemove._id); let indexToRemove = assorts.findIndex((item) => item._id === itemToRemove._id);
// No offer found in main assort, check discount items // No offer found in main assort, check discount items
if (indexToRemove === -1) if (indexToRemove === -1)
{ {
indexToRemove = this.fenceDiscountAssort.items.findIndex(item => item._id === itemToRemove._id); indexToRemove = this.fenceDiscountAssort.items.findIndex((item) => item._id === itemToRemove._id);
this.fenceDiscountAssort.items.splice(indexToRemove, 1); this.fenceDiscountAssort.items.splice(indexToRemove, 1);
if (indexToRemove === -1) if (indexToRemove === -1)

View File

@ -120,7 +120,9 @@ export class InsuranceService
MessageType.NPC_TRADER, MessageType.NPC_TRADER,
this.randomUtil.getArrayValue(dialogueTemplates.insuranceStart), this.randomUtil.getArrayValue(dialogueTemplates.insuranceStart),
null, null,
this.timeUtil.getHoursAsSeconds(this.databaseServer.getTables().globals.config.Insurance.MaxStorageTimeInHour), this.timeUtil.getHoursAsSeconds(
this.databaseServer.getTables().globals.config.Insurance.MaxStorageTimeInHour,
),
systemData, systemData,
); );
@ -151,7 +153,7 @@ export class InsuranceService
for (const insuredItem of this.getInsurance(sessionId)[traderId]) for (const insuredItem of this.getInsurance(sessionId)[traderId])
{ {
// Find insured items parent // Find insured items parent
const insuredItemsParent = insuredItems.find(x => x._id === insuredItem.parentId); const insuredItemsParent = insuredItems.find((x) => x._id === insuredItem.parentId);
if (!insuredItemsParent) if (!insuredItemsParent)
{ {
// Remove location + set slotId of insured items parent // Remove location + set slotId of insured items parent
@ -179,9 +181,9 @@ export class InsuranceService
return this.timeUtil.getTimestamp() + this.insuranceConfig.returnTimeOverrideSeconds; return this.timeUtil.getTimestamp() + this.insuranceConfig.returnTimeOverrideSeconds;
} }
const insuranceReturnTimeBonus = pmcData.Bonuses.find(b => b.type === BonusType.INSURANCE_RETURN_TIME); const insuranceReturnTimeBonus = pmcData.Bonuses.find((b) => b.type === BonusType.INSURANCE_RETURN_TIME);
const insuranceReturnTimeBonusPercent = 1.0 const insuranceReturnTimeBonusPercent
- (insuranceReturnTimeBonus ? Math.abs(insuranceReturnTimeBonus.value) : 0) / 100; = 1.0 - (insuranceReturnTimeBonus ? Math.abs(insuranceReturnTimeBonus.value) : 0) / 100;
const traderMinReturnAsSeconds = trader.insurance.min_return_hour * TimeUtil.ONE_HOUR_AS_SECONDS; const traderMinReturnAsSeconds = trader.insurance.min_return_hour * TimeUtil.ONE_HOUR_AS_SECONDS;
const traderMaxReturnAsSeconds = trader.insurance.max_return_hour * TimeUtil.ONE_HOUR_AS_SECONDS; const traderMaxReturnAsSeconds = trader.insurance.max_return_hour * TimeUtil.ONE_HOUR_AS_SECONDS;
@ -256,7 +258,7 @@ export class InsuranceService
itemToReturnToPlayer: this.getInsuredItemDetails( itemToReturnToPlayer: this.getInsuredItemDetails(
pmcData, pmcData,
preRaidItem, preRaidItem,
offraidData.insurance?.find(insuranceItem => insuranceItem.id === insuredItem.itemId), offraidData.insurance?.find((insuranceItem) => insuranceItem.id === insuredItem.itemId),
), ),
traderId: insuredItem.tid, traderId: insuredItem.tid,
sessionID: sessionID, sessionID: sessionID,
@ -268,10 +270,13 @@ export class InsuranceService
if (this.itemHelper.itemHasSlots(preRaidItem._tpl)) if (this.itemHelper.itemHasSlots(preRaidItem._tpl))
{ {
// Get IDs of all soft insert child items on armor from pre raid gear data // Get IDs of all soft insert child items on armor from pre raid gear data
const softInsertChildIds = preRaidGear.filter(item => const softInsertChildIds = preRaidGear
item.parentId === preRaidItem._id .filter(
&& this.itemHelper.getSoftInsertSlotIds().includes(item.slotId.toLowerCase()), (item) =>
).map(x => x._id); item.parentId === preRaidItem._id
&& this.itemHelper.getSoftInsertSlotIds().includes(item.slotId.toLowerCase()),
)
.map((x) => x._id);
// Add all items found above to return data // Add all items found above to return data
for (const softInsertChildModId of softInsertChildIds) for (const softInsertChildModId of softInsertChildIds)
@ -280,9 +285,9 @@ export class InsuranceService
pmcData: pmcData, pmcData: pmcData,
itemToReturnToPlayer: this.getInsuredItemDetails( itemToReturnToPlayer: this.getInsuredItemDetails(
pmcData, pmcData,
preRaidGear.find(item => item._id === softInsertChildModId), preRaidGear.find((item) => item._id === softInsertChildModId),
offraidData.insurance?.find(insuranceItem => offraidData.insurance?.find(
insuranceItem.id === softInsertChildModId, (insuranceItem) => insuranceItem.id === softInsertChildModId,
), ),
), ),
traderId: insuredItem.tid, traderId: insuredItem.tid,

View File

@ -1,6 +1,5 @@
import { inject, injectable } from "tsyringe"; import { inject, injectable } from "tsyringe";
import { ITemplateItem } from "@spt-aki/models/eft/common/tables/ITemplateItem"; import { ITemplateItem } from "@spt-aki/models/eft/common/tables/ITemplateItem";
import { BaseClasses } from "@spt-aki/models/enums/BaseClasses";
import { ILogger } from "@spt-aki/models/spt/utils/ILogger"; import { ILogger } from "@spt-aki/models/spt/utils/ILogger";
import { DatabaseServer } from "@spt-aki/servers/DatabaseServer"; import { DatabaseServer } from "@spt-aki/servers/DatabaseServer";
import { LocalisationService } from "@spt-aki/services/LocalisationService"; import { LocalisationService } from "@spt-aki/services/LocalisationService";
@ -39,7 +38,7 @@ export class ItemBaseClassService
return; return;
} }
const filteredDbItems = Object.values(this.items).filter(x => x._type === "Item"); const filteredDbItems = Object.values(this.items).filter((x) => x._type === "Item");
for (const item of filteredDbItems) for (const item of filteredDbItems)
{ {
const itemIdToUpdate = item._id; const itemIdToUpdate = item._id;
@ -111,7 +110,7 @@ export class ItemBaseClassService
} }
} }
return this.itemBaseClassesCache[itemTpl].some(x => baseClasses.includes(x)); return this.itemBaseClassesCache[itemTpl].some((x) => baseClasses.includes(x));
} }
/** /**

View File

@ -1,7 +1,6 @@
import path from "node:path"; import path from "node:path";
import { I18n } from "i18n"; import { I18n } from "i18n";
import { inject, injectable } from "tsyringe"; import { inject, injectable } from "tsyringe";
import { ILocaleConfig } from "@spt-aki/models/spt/config/ILocaleConfig";
import { ILogger } from "@spt-aki/models/spt/utils/ILogger"; import { ILogger } from "@spt-aki/models/spt/utils/ILogger";
import { DatabaseServer } from "@spt-aki/servers/DatabaseServer"; import { DatabaseServer } from "@spt-aki/servers/DatabaseServer";
import { LocaleService } from "@spt-aki/services/LocaleService"; import { LocaleService } from "@spt-aki/services/LocaleService";
@ -66,7 +65,7 @@ export class LocalisationService
*/ */
public getRandomTextThatMatchesPartialKey(partialKey: string): string public getRandomTextThatMatchesPartialKey(partialKey: string): string
{ {
const filteredKeys = Object.keys(this.databaseServer.getTables().locales.server.en).filter(x => const filteredKeys = Object.keys(this.databaseServer.getTables().locales.server.en).filter((x) =>
x.startsWith(partialKey), x.startsWith(partialKey),
); );
const chosenKey = this.randomUtil.getArrayValue(filteredKeys); const chosenKey = this.randomUtil.getArrayValue(filteredKeys);

View File

@ -365,9 +365,8 @@ export class MailSendService
hasRewards: false, // The default dialog message has no rewards, can be added later via addRewardItemsToMessage() hasRewards: false, // The default dialog message has no rewards, can be added later via addRewardItemsToMessage()
rewardCollected: false, // The default dialog message has no rewards, can be added later via addRewardItemsToMessage() rewardCollected: false, // The default dialog message has no rewards, can be added later via addRewardItemsToMessage()
systemData: messageDetails.systemData ? messageDetails.systemData : undefined, // Used by ragfair / localised messages that need "location" or "time" systemData: messageDetails.systemData ? messageDetails.systemData : undefined, // Used by ragfair / localised messages that need "location" or "time"
profileChangeEvents: messageDetails.profileChangeEvents?.length === 0 profileChangeEvents:
? messageDetails.profileChangeEvents messageDetails.profileChangeEvents?.length === 0 ? messageDetails.profileChangeEvents : undefined, // no one knows, its never been used in any dumps
: undefined, // no one knows, its never been used in any dumps
}; };
// Clean up empty system data // Clean up empty system data
@ -412,7 +411,10 @@ export class MailSendService
* @param messageDetails * @param messageDetails
* @returns Sanitised items * @returns Sanitised items
*/ */
protected processItemsBeforeAddingToMail(dialogType: MessageType, messageDetails: ISendMessageDetails): MessageItems protected processItemsBeforeAddingToMail(
dialogType: MessageType,
messageDetails: ISendMessageDetails,
): MessageItems
{ {
const db = this.databaseServer.getTables().templates.items; const db = this.databaseServer.getTables().templates.items;
@ -512,7 +514,7 @@ export class MailSendService
} }
// Find first item with slotId that indicates its a 'base' item // Find first item with slotId that indicates its a 'base' item
let item = items.find(x => ["hideout", "main"].includes(x.slotId)); let item = items.find((x) => ["hideout", "main"].includes(x.slotId));
if (item) if (item)
{ {
return item; return item;
@ -520,7 +522,7 @@ export class MailSendService
// Not a singlular item + no items have a hideout/main slotid // Not a singlular item + no items have a hideout/main slotid
// Look for first item without parent id // Look for first item without parent id
item = items.find(x => !x.parentId); item = items.find((x) => !x.parentId);
if (item) if (item)
{ {
return item; return item;

View File

@ -58,7 +58,7 @@ export class PaymentService
for (const index in request.scheme_items) for (const index in request.scheme_items)
{ {
// Find the corresponding item in the player's inventory. // Find the corresponding item in the player's inventory.
const item = pmcData.Inventory.items.find(i => i._id === request.scheme_items[index].id); const item = pmcData.Inventory.items.find((i) => i._id === request.scheme_items[index].id);
if (item !== undefined) if (item !== undefined)
{ {
if (!this.paymentHelper.isMoneyTpl(item._tpl)) if (!this.paymentHelper.isMoneyTpl(item._tpl))
@ -396,7 +396,7 @@ export class PaymentService
*/ */
protected isInStash(itemId: string, inventoryItems: Item[], playerStashId: string): boolean protected isInStash(itemId: string, inventoryItems: Item[], playerStashId: string): boolean
{ {
const itemParent = inventoryItems.find(x => x._id === itemId); const itemParent = inventoryItems.find((x) => x._id === itemId);
if (itemParent) if (itemParent)
{ {

View File

@ -229,7 +229,7 @@ export class PmcChatResponseService
const keyBase = isVictim ? "pmcresponse-victim_" : "pmcresponse-killer_"; const keyBase = isVictim ? "pmcresponse-victim_" : "pmcresponse-killer_";
const keys = this.localisationService.getKeys(); const keys = this.localisationService.getKeys();
return keys.filter(x => x.startsWith(`${keyBase}${keyType}`)); return keys.filter((x) => x.startsWith(`${keyBase}${keyType}`));
} }
/** /**
@ -240,7 +240,7 @@ export class PmcChatResponseService
{ {
const keys = this.localisationService.getKeys(); const keys = this.localisationService.getKeys();
return keys.filter(x => x.startsWith("pmcresponse-suffix")); return keys.filter((x) => x.startsWith("pmcresponse-suffix"));
} }
/** /**

View File

@ -87,10 +87,10 @@ export class ProfileFixerService
this.reorderHideoutAreasWithResouceInputs(pmcProfile); this.reorderHideoutAreasWithResouceInputs(pmcProfile);
if ( if (
pmcProfile.Hideout.Areas.find(x => x.type === HideoutAreas.GENERATOR).slots.length pmcProfile.Hideout.Areas.find((x) => x.type === HideoutAreas.GENERATOR).slots.length
< 6 < 6
+ this.databaseServer.getTables().globals.config.SkillsSettings.HideoutManagement.EliteSlots + this.databaseServer.getTables().globals.config.SkillsSettings.HideoutManagement.EliteSlots.Generator
.Generator.Slots .Slots
) )
{ {
this.logger.debug("Updating generator area slots to a size of 6 + hideout management skill"); this.logger.debug("Updating generator area slots to a size of 6 + hideout management skill");
@ -104,7 +104,7 @@ export class ProfileFixerService
} }
if ( if (
pmcProfile.Hideout.Areas.find(x => x.type === HideoutAreas.WATER_COLLECTOR).slots.length pmcProfile.Hideout.Areas.find((x) => x.type === HideoutAreas.WATER_COLLECTOR).slots.length
< 1 < 1
+ this.databaseServer.getTables().globals.config.SkillsSettings.HideoutManagement.EliteSlots + this.databaseServer.getTables().globals.config.SkillsSettings.HideoutManagement.EliteSlots
.WaterCollector.Slots .WaterCollector.Slots
@ -121,7 +121,7 @@ export class ProfileFixerService
} }
if ( if (
pmcProfile.Hideout.Areas.find(x => x.type === HideoutAreas.AIR_FILTERING).slots.length pmcProfile.Hideout.Areas.find((x) => x.type === HideoutAreas.AIR_FILTERING).slots.length
< 3 < 3
+ this.databaseServer.getTables().globals.config.SkillsSettings.HideoutManagement.EliteSlots + this.databaseServer.getTables().globals.config.SkillsSettings.HideoutManagement.EliteSlots
.AirFilteringUnit.Slots .AirFilteringUnit.Slots
@ -139,7 +139,7 @@ export class ProfileFixerService
// BTC Farm doesnt have extra slots for hideout management, but we still check for modded stuff!! // BTC Farm doesnt have extra slots for hideout management, but we still check for modded stuff!!
if ( if (
pmcProfile.Hideout.Areas.find(x => x.type === HideoutAreas.BITCOIN_FARM).slots.length pmcProfile.Hideout.Areas.find((x) => x.type === HideoutAreas.BITCOIN_FARM).slots.length
< 50 < 50
+ this.databaseServer.getTables().globals.config.SkillsSettings.HideoutManagement.EliteSlots + this.databaseServer.getTables().globals.config.SkillsSettings.HideoutManagement.EliteSlots
.BitcoinFarm.Slots .BitcoinFarm.Slots
@ -172,7 +172,7 @@ export class ProfileFixerService
protected addMissingGunStandContainerImprovements(pmcProfile: IPmcData): void protected addMissingGunStandContainerImprovements(pmcProfile: IPmcData): void
{ {
const weaponStandArea = pmcProfile.Hideout.Areas.find(x => x.type === HideoutAreas.WEAPON_STAND); const weaponStandArea = pmcProfile.Hideout.Areas.find((x) => x.type === HideoutAreas.WEAPON_STAND);
if (!weaponStandArea || weaponStandArea.level === 0) if (!weaponStandArea || weaponStandArea.level === 0)
{ {
// No stand in profile or its level 0, skip // No stand in profile or its level 0, skip
@ -180,8 +180,8 @@ export class ProfileFixerService
} }
const db = this.databaseServer.getTables(); const db = this.databaseServer.getTables();
const hideoutStandAreaDb = db.hideout.areas.find(x => x.type === HideoutAreas.WEAPON_STAND); const hideoutStandAreaDb = db.hideout.areas.find((x) => x.type === HideoutAreas.WEAPON_STAND);
const hideoutStandSecondaryAreaDb = db.hideout.areas.find(x => x.parentArea === hideoutStandAreaDb._id); const hideoutStandSecondaryAreaDb = db.hideout.areas.find((x) => x.parentArea === hideoutStandAreaDb._id);
const stageCurrentAt = hideoutStandAreaDb.stages[weaponStandArea.level]; const stageCurrentAt = hideoutStandAreaDb.stages[weaponStandArea.level];
const hideoutStandStashId = pmcProfile.Inventory.hideoutAreaStashes[HideoutAreas.WEAPON_STAND]; const hideoutStandStashId = pmcProfile.Inventory.hideoutAreaStashes[HideoutAreas.WEAPON_STAND];
const hideoutSecondaryStashId = pmcProfile.Inventory.hideoutAreaStashes[HideoutAreas.WEAPON_STAND_SECONDARY]; const hideoutSecondaryStashId = pmcProfile.Inventory.hideoutAreaStashes[HideoutAreas.WEAPON_STAND_SECONDARY];
@ -195,7 +195,7 @@ export class ProfileFixerService
= hideoutStandSecondaryAreaDb._id; = hideoutStandSecondaryAreaDb._id;
// Add stash item to profile // Add stash item to profile
const gunStandStashItem = pmcProfile.Inventory.items.find(x => x._id === hideoutStandAreaDb._id); const gunStandStashItem = pmcProfile.Inventory.items.find((x) => x._id === hideoutStandAreaDb._id);
if (gunStandStashItem) if (gunStandStashItem)
{ {
gunStandStashItem._tpl = stageCurrentAt.container; gunStandStashItem._tpl = stageCurrentAt.container;
@ -212,8 +212,8 @@ export class ProfileFixerService
} }
// Add secondary stash item to profile // Add secondary stash item to profile
const gunStandStashSecondaryItem = pmcProfile.Inventory.items.find(x => const gunStandStashSecondaryItem = pmcProfile.Inventory.items.find(
x._id === hideoutStandSecondaryAreaDb._id, (x) => x._id === hideoutStandSecondaryAreaDb._id,
); );
if (gunStandStashItem) if (gunStandStashItem)
{ {
@ -236,12 +236,12 @@ export class ProfileFixerService
return; return;
} }
let stashItem = pmcProfile.Inventory.items?.find(x => x._id === hideoutStandAreaDb._id); let stashItem = pmcProfile.Inventory.items?.find((x) => x._id === hideoutStandAreaDb._id);
if (!stashItem) if (!stashItem)
{ {
// Stand inventory stash item doesnt exist, add it // Stand inventory stash item doesnt exist, add it
pmcProfile.Inventory.items.push({ _id: hideoutStandAreaDb._id, _tpl: stageCurrentAt.container }); pmcProfile.Inventory.items.push({ _id: hideoutStandAreaDb._id, _tpl: stageCurrentAt.container });
stashItem = pmcProfile.Inventory.items?.find(x => x._id === hideoutStandAreaDb._id); stashItem = pmcProfile.Inventory.items?.find((x) => x._id === hideoutStandAreaDb._id);
} }
// `hideoutAreaStashes` has value related stash inventory items tpl doesnt match what's expected // `hideoutAreaStashes` has value related stash inventory items tpl doesnt match what's expected
@ -254,12 +254,12 @@ export class ProfileFixerService
stashItem._tpl = stageCurrentAt.container; stashItem._tpl = stageCurrentAt.container;
} }
let stashSecondaryItem = pmcProfile.Inventory.items?.find(x => x._id === hideoutStandSecondaryAreaDb._id); let stashSecondaryItem = pmcProfile.Inventory.items?.find((x) => x._id === hideoutStandSecondaryAreaDb._id);
if (!stashSecondaryItem) if (!stashSecondaryItem)
{ {
// Stand inventory stash item doesnt exist, add it // Stand inventory stash item doesnt exist, add it
pmcProfile.Inventory.items.push({ _id: hideoutStandSecondaryAreaDb._id, _tpl: stageCurrentAt.container }); pmcProfile.Inventory.items.push({ _id: hideoutStandSecondaryAreaDb._id, _tpl: stageCurrentAt.container });
stashSecondaryItem = pmcProfile.Inventory.items?.find(x => x._id === hideoutStandSecondaryAreaDb._id); stashSecondaryItem = pmcProfile.Inventory.items?.find((x) => x._id === hideoutStandSecondaryAreaDb._id);
} }
// `hideoutAreaStashes` has value related stash inventory items tpl doesnt match what's expected // `hideoutAreaStashes` has value related stash inventory items tpl doesnt match what's expected
@ -275,7 +275,7 @@ export class ProfileFixerService
protected addMissingHallOfFameContainerImprovements(pmcProfile: IPmcData): void protected addMissingHallOfFameContainerImprovements(pmcProfile: IPmcData): void
{ {
const placeOfFameArea = pmcProfile.Hideout.Areas.find(x => x.type === HideoutAreas.PLACE_OF_FAME); const placeOfFameArea = pmcProfile.Hideout.Areas.find((x) => x.type === HideoutAreas.PLACE_OF_FAME);
if (!placeOfFameArea || placeOfFameArea.level === 0) if (!placeOfFameArea || placeOfFameArea.level === 0)
{ {
// No place of fame in profile or its level 0, skip // No place of fame in profile or its level 0, skip
@ -283,7 +283,7 @@ export class ProfileFixerService
} }
const db = this.databaseServer.getTables(); const db = this.databaseServer.getTables();
const placeOfFameAreaDb = db.hideout.areas.find(area => area.type === HideoutAreas.PLACE_OF_FAME); const placeOfFameAreaDb = db.hideout.areas.find((area) => area.type === HideoutAreas.PLACE_OF_FAME);
if (!placeOfFameAreaDb) if (!placeOfFameAreaDb)
{ {
return; return;
@ -298,7 +298,7 @@ export class ProfileFixerService
pmcProfile.Inventory.hideoutAreaStashes[HideoutAreas.PLACE_OF_FAME] = placeOfFameAreaDb._id; pmcProfile.Inventory.hideoutAreaStashes[HideoutAreas.PLACE_OF_FAME] = placeOfFameAreaDb._id;
// Add stash item to profile // Add stash item to profile
const placeOfFameStashItem = pmcProfile.Inventory.items.find(x => x._id === placeOfFameAreaDb._id); const placeOfFameStashItem = pmcProfile.Inventory.items.find((x) => x._id === placeOfFameAreaDb._id);
if (placeOfFameStashItem) if (placeOfFameStashItem)
{ {
placeOfFameStashItem._tpl = stageCurrentlyAt.container; placeOfFameStashItem._tpl = stageCurrentlyAt.container;
@ -317,12 +317,12 @@ export class ProfileFixerService
return; return;
} }
let stashItem = pmcProfile.Inventory.items?.find(x => x._id === placeOfFameAreaDb._id); let stashItem = pmcProfile.Inventory.items?.find((x) => x._id === placeOfFameAreaDb._id);
if (!stashItem) if (!stashItem)
{ {
// Stand inventory stash item doesnt exist, add it // Stand inventory stash item doesnt exist, add it
pmcProfile.Inventory.items.push({ _id: placeOfFameAreaDb._id, _tpl: stageCurrentlyAt.container }); pmcProfile.Inventory.items.push({ _id: placeOfFameAreaDb._id, _tpl: stageCurrentlyAt.container });
stashItem = pmcProfile.Inventory.items?.find(x => x._id === placeOfFameAreaDb._id); stashItem = pmcProfile.Inventory.items?.find((x) => x._id === placeOfFameAreaDb._id);
} }
// `hideoutAreaStashes` has value related stash inventory items tpl doesnt match what's expected // `hideoutAreaStashes` has value related stash inventory items tpl doesnt match what's expected
@ -339,10 +339,10 @@ export class ProfileFixerService
protected ensureGunStandLevelsMatch(pmcProfile: IPmcData): void protected ensureGunStandLevelsMatch(pmcProfile: IPmcData): void
{ {
// only proceed if stand is level 1 or above // only proceed if stand is level 1 or above
const gunStandParent = pmcProfile.Hideout.Areas.find(x => x.type === HideoutAreas.WEAPON_STAND); const gunStandParent = pmcProfile.Hideout.Areas.find((x) => x.type === HideoutAreas.WEAPON_STAND);
if (gunStandParent && gunStandParent.level > 0) if (gunStandParent && gunStandParent.level > 0)
{ {
const gunStandChild = pmcProfile.Hideout.Areas.find(x => x.type === HideoutAreas.WEAPON_STAND_SECONDARY); const gunStandChild = pmcProfile.Hideout.Areas.find((x) => x.type === HideoutAreas.WEAPON_STAND_SECONDARY);
if (gunStandChild && gunStandParent.level !== gunStandChild.level) if (gunStandChild && gunStandParent.level !== gunStandChild.level)
{ {
this.logger.success("Upgraded gun stand levels to match"); this.logger.success("Upgraded gun stand levels to match");
@ -362,7 +362,7 @@ export class ProfileFixerService
protected addMissingHideoutWallAreas(pmcProfile: IPmcData): void protected addMissingHideoutWallAreas(pmcProfile: IPmcData): void
{ {
if (!pmcProfile.Hideout.Areas.find(x => x.type === HideoutAreas.WEAPON_STAND)) if (!pmcProfile.Hideout.Areas.find((x) => x.type === HideoutAreas.WEAPON_STAND))
{ {
pmcProfile.Hideout.Areas.push({ pmcProfile.Hideout.Areas.push({
type: 24, type: 24,
@ -376,7 +376,7 @@ export class ProfileFixerService
}); });
} }
if (!pmcProfile.Hideout.Areas.find(x => x.type === HideoutAreas.WEAPON_STAND_SECONDARY)) if (!pmcProfile.Hideout.Areas.find((x) => x.type === HideoutAreas.WEAPON_STAND_SECONDARY))
{ {
pmcProfile.Hideout.Areas.push({ pmcProfile.Hideout.Areas.push({
type: 25, type: 25,
@ -473,14 +473,14 @@ export class ProfileFixerService
// Only check if profile has repeatable quests // Only check if profile has repeatable quests
if (pmcProfile.RepeatableQuests && activeRepeatableQuests.length > 0) if (pmcProfile.RepeatableQuests && activeRepeatableQuests.length > 0)
{ {
const existsInActiveRepeatableQuests = activeRepeatableQuests.some(quest => const existsInActiveRepeatableQuests = activeRepeatableQuests.some(
quest._id === taskConditionCounter.sourceId, (quest) => quest._id === taskConditionCounter.sourceId,
); );
const existsInQuests = pmcProfile.Quests.some(quest => const existsInQuests = pmcProfile.Quests.some(
quest.qid === taskConditionCounter.sourceId, (quest) => quest.qid === taskConditionCounter.sourceId,
); );
const isAchievementTracker = achievements.some(quest => const isAchievementTracker = achievements.some(
quest.id === taskConditionCounter.sourceId, (quest) => quest.id === taskConditionCounter.sourceId,
); );
// If task conditions id is neither in activeQuests, quests or achievements - it's stale and should be cleaned up // If task conditions id is neither in activeQuests, quests or achievements - it's stale and should be cleaned up
@ -565,12 +565,12 @@ export class ProfileFixerService
continue; continue;
} }
if (quest.status && Number.isNaN(Number.parseInt(<string><unknown>quest.status))) if (quest.status && Number.isNaN(Number.parseInt(<string>(<unknown>quest.status))))
{ {
fixes[quest.status] = (fixes[quest.status] ?? 0) + 1; fixes[quest.status] = (fixes[quest.status] ?? 0) + 1;
const newQuestStatus = QuestStatus[quest.status]; const newQuestStatus = QuestStatus[quest.status];
quest.status = <QuestStatus><unknown>newQuestStatus; quest.status = <QuestStatus>(<unknown>newQuestStatus);
} }
for (const statusTimer in quest.statusTimers) for (const statusTimer in quest.statusTimers)
@ -594,16 +594,18 @@ export class ProfileFixerService
if (Object.keys(fixes).length > 0) if (Object.keys(fixes).length > 0)
{ {
this.logger.debug( this.logger.debug(
`Updated quests values: ${Object.entries(fixes).map(([k, v]) => `(${k}: ${v} times)`).join(", ")}`, `Updated quests values: ${Object.entries(fixes)
.map(([k, v]) => `(${k}: ${v} times)`)
.join(", ")}`,
); );
} }
if (Object.keys(timerFixes).length > 0) if (Object.keys(timerFixes).length > 0)
{ {
this.logger.debug( this.logger.debug(
`Updated statusTimers values: ${ `Updated statusTimers values: ${Object.entries(timerFixes)
Object.entries(timerFixes).map(([k, v]) => `(${k}: ${v} times)`).join(", ") .map(([k, v]) => `(${k}: ${v} times)`)
}`, .join(", ")}`,
); );
} }
} }
@ -616,8 +618,12 @@ export class ProfileFixerService
for (const currentRepeatable of pmcProfile.RepeatableQuests) for (const currentRepeatable of pmcProfile.RepeatableQuests)
{ {
if ( if (
!(currentRepeatable.changeRequirement !(
&& currentRepeatable.activeQuests.every(x => typeof x.changeCost !== "undefined" && typeof x.changeStandingCost !== "undefined")) currentRepeatable.changeRequirement
&& currentRepeatable.activeQuests.every(
(x) => typeof x.changeCost !== "undefined" && typeof x.changeStandingCost !== "undefined",
)
)
) )
{ {
repeatablesCompatible = false; repeatablesCompatible = false;
@ -642,10 +648,10 @@ export class ProfileFixerService
*/ */
protected addMissingWallImprovements(pmcProfile: IPmcData): void protected addMissingWallImprovements(pmcProfile: IPmcData): void
{ {
const profileWallArea = pmcProfile.Hideout.Areas.find(x => x.type === HideoutAreas.EMERGENCY_WALL); const profileWallArea = pmcProfile.Hideout.Areas.find((x) => x.type === HideoutAreas.EMERGENCY_WALL);
const wallDb = this.databaseServer.getTables().hideout.areas.find(x => const wallDb = this.databaseServer
x.type === HideoutAreas.EMERGENCY_WALL, .getTables()
); .hideout.areas.find((x) => x.type === HideoutAreas.EMERGENCY_WALL);
if (profileWallArea.level > 0) if (profileWallArea.level > 0)
{ {
@ -693,13 +699,13 @@ export class ProfileFixerService
} }
// Only slots with location index // Only slots with location index
area.slots = area.slots.filter(x => "locationIndex" in x); area.slots = area.slots.filter((x) => "locationIndex" in x);
// Only slots that: // Only slots that:
// Have an item property and it has at least one item in it // Have an item property and it has at least one item in it
// Or // Or
// Have no item property // Have no item property
area.slots = area.slots.filter(x => ("item" in x && x.item?.length > 0) || !("item" in x)); area.slots = area.slots.filter((x) => ("item" in x && x.item?.length > 0) || !("item" in x));
} }
} }
@ -718,7 +724,7 @@ export class ProfileFixerService
for (const areaId of areasToCheck) for (const areaId of areasToCheck)
{ {
const area = pmcProfile.Hideout.Areas.find(area => area.type === areaId); const area = pmcProfile.Hideout.Areas.find((area) => area.type === areaId);
if (!area) if (!area)
{ {
this.logger.debug(`unable to sort: ${area.type} (${areaId}) slots, no area found`); this.logger.debug(`unable to sort: ${area.type} (${areaId}) slots, no area found`);
@ -749,7 +755,7 @@ export class ProfileFixerService
pmcProfile: IPmcData, pmcProfile: IPmcData,
): void ): void
{ {
const area = pmcProfile.Hideout.Areas.find(x => x.type === areaType); const area = pmcProfile.Hideout.Areas.find((x) => x.type === areaType);
area.slots = this.addObjectsToArray(emptyItemCount, area.slots); area.slots = this.addObjectsToArray(emptyItemCount, area.slots);
} }
@ -757,7 +763,7 @@ export class ProfileFixerService
{ {
for (let i = 0; i < count; i++) for (let i = 0; i < count; i++)
{ {
if (!slots.find(x => x.locationIndex === i)) if (!slots.find((x) => x.locationIndex === i))
{ {
slots.push({ locationIndex: i }); slots.push({ locationIndex: i });
} }
@ -795,7 +801,7 @@ export class ProfileFixerService
} }
// Iterate over area levels, check for bonuses, add if needed // Iterate over area levels, check for bonuses, add if needed
const dbArea = dbHideoutAreas.find(x => x.type === areaType); const dbArea = dbHideoutAreas.find((x) => x.type === areaType);
if (!dbArea) if (!dbArea)
{ {
continue; continue;
@ -840,22 +846,22 @@ export class ProfileFixerService
// match by id first, used by "TextBonus" bonuses // match by id first, used by "TextBonus" bonuses
if (bonus.id) if (bonus.id)
{ {
return profileBonuses.find(x => x.id === bonus.id); return profileBonuses.find((x) => x.id === bonus.id);
} }
if (bonus.type === BonusType.STASH_SIZE) if (bonus.type === BonusType.STASH_SIZE)
{ {
return profileBonuses.find(x => x.type === bonus.type && x.templateId === bonus.templateId); return profileBonuses.find((x) => x.type === bonus.type && x.templateId === bonus.templateId);
} }
if (bonus.type === BonusType.ADDITIONAL_SLOTS) if (bonus.type === BonusType.ADDITIONAL_SLOTS)
{ {
return profileBonuses.find(x => return profileBonuses.find(
x.type === bonus.type && x.value === bonus.value && x.visible === bonus.visible, (x) => x.type === bonus.type && x.value === bonus.value && x.visible === bonus.visible,
); );
} }
return profileBonuses.find(x => x.type === bonus.type && x.value === bonus.value); return profileBonuses.find((x) => x.type === bonus.type && x.value === bonus.value);
} }
/** /**
@ -870,7 +876,7 @@ export class ProfileFixerService
// Get items placed in root of stash // Get items placed in root of stash
// TODO: extend to other areas / sub items // TODO: extend to other areas / sub items
const inventoryItemsToCheck = pmcProfile.Inventory.items.filter(item => const inventoryItemsToCheck = pmcProfile.Inventory.items.filter((item) =>
["hideout", "main"].includes(item.slotId), ["hideout", "main"].includes(item.slotId),
); );
if (inventoryItemsToCheck) if (inventoryItemsToCheck)
@ -952,7 +958,10 @@ export class ProfileFixerService
if (this.coreConfig.fixes.removeModItemsFromProfile) if (this.coreConfig.fixes.removeModItemsFromProfile)
{ {
dialog.messages.splice(dialog.messages.findIndex(x => x._id === message._id), 1); dialog.messages.splice(
dialog.messages.findIndex((x) => x._id === message._id),
1,
);
this.logger.warning( this.logger.warning(
`Item: ${item._tpl} has resulted in the deletion of message: ${message._id} from dialog ${dialogId}`, `Item: ${item._tpl} has resulted in the deletion of message: ${message._id} from dialog ${dialogId}`,
); );
@ -991,7 +1000,7 @@ export class ProfileFixerService
`Non-default quest: ${activeQuest._id} from trader: ${activeQuest.traderId} removed from RepeatableQuests list in profile`, `Non-default quest: ${activeQuest._id} from trader: ${activeQuest.traderId} removed from RepeatableQuests list in profile`,
); );
repeatable.activeQuests.splice( repeatable.activeQuests.splice(
repeatable.activeQuests.findIndex(x => x._id === activeQuest._id), repeatable.activeQuests.findIndex((x) => x._id === activeQuest._id),
1, 1,
); );
} }
@ -1016,7 +1025,7 @@ export class ProfileFixerService
`Non-default quest: ${activeQuest._id} from trader: ${activeQuest.traderId} removed from RepeatableQuests list in profile`, `Non-default quest: ${activeQuest._id} from trader: ${activeQuest.traderId} removed from RepeatableQuests list in profile`,
); );
repeatable.activeQuests.splice( repeatable.activeQuests.splice(
repeatable.activeQuests.findIndex(x => x._id === activeQuest._id), repeatable.activeQuests.findIndex((x) => x._id === activeQuest._id),
1, 1,
); );
} }
@ -1081,7 +1090,10 @@ export class ProfileFixerService
* @param itemsDb The items database to use for item lookup * @param itemsDb The items database to use for item lookup
* @returns True if the build should be removed from the build list, false otherwise * @returns True if the build should be removed from the build list, false otherwise
*/ */
protected shouldRemoveMagazineBuild(magazineBuild: IMagazineBuild, itemsDb: Record<string, ITemplateItem>): boolean protected shouldRemoveMagazineBuild(
magazineBuild: IMagazineBuild,
itemsDb: Record<string, ITemplateItem>,
): boolean
{ {
for (const item of magazineBuild.Items) for (const item of magazineBuild.Items)
{ {
@ -1141,7 +1153,7 @@ export class ProfileFixerService
if (itemAJson === itemBJson) if (itemAJson === itemBJson)
{ {
// Both items match, we can safely delete one // Both items match, we can safely delete one
const indexOfItemToRemove = pmcProfile.Inventory.items.findIndex(x => x._id === key); const indexOfItemToRemove = pmcProfile.Inventory.items.findIndex((x) => x._id === key);
pmcProfile.Inventory.items.splice(indexOfItemToRemove, 1); pmcProfile.Inventory.items.splice(indexOfItemToRemove, 1);
this.logger.warning(`Deleted duplicate item: ${key}`); this.logger.warning(`Deleted duplicate item: ${key}`);
} }
@ -1149,10 +1161,10 @@ export class ProfileFixerService
{ {
// Items are different, replace ID with unique value // Items are different, replace ID with unique value
// Only replace ID if items have no children, we dont want orphaned children // Only replace ID if items have no children, we dont want orphaned children
const itemsHaveChildren = pmcProfile.Inventory.items.some(x => x.parentId === key); const itemsHaveChildren = pmcProfile.Inventory.items.some((x) => x.parentId === key);
if (!itemsHaveChildren) if (!itemsHaveChildren)
{ {
const itemToAdjustId = pmcProfile.Inventory.items.find(x => x._id === key); const itemToAdjustId = pmcProfile.Inventory.items.find((x) => x._id === key);
itemToAdjustId._id = this.hashUtil.generate(); itemToAdjustId._id = this.hashUtil.generate();
this.logger.warning(`Replace duplicate item Id: ${key} with ${itemToAdjustId._id}`); this.logger.warning(`Replace duplicate item Id: ${key} with ${itemToAdjustId._id}`);
} }
@ -1160,7 +1172,7 @@ export class ProfileFixerService
} }
// Iterate over all inventory items // Iterate over all inventory items
for (const item of pmcProfile.Inventory.items.filter(x => x.slotId)) for (const item of pmcProfile.Inventory.items.filter((x) => x.slotId))
{ {
if (!item.upd) if (!item.upd)
{ {
@ -1193,35 +1205,38 @@ export class ProfileFixerService
if (!customizationDb[pmcProfile.Customization.Head]) if (!customizationDb[pmcProfile.Customization.Head])
{ {
const defaultHead = playerIsUsec const defaultHead = playerIsUsec
? customizationDbArray.find(x => x._name === "DefaultUsecHead") ? customizationDbArray.find((x) => x._name === "DefaultUsecHead")
: customizationDbArray.find(x => x._name === "DefaultBearHead"); : customizationDbArray.find((x) => x._name === "DefaultBearHead");
pmcProfile.Customization.Head = defaultHead._id; pmcProfile.Customization.Head = defaultHead._id;
} }
// check Body // check Body
if (!customizationDb[pmcProfile.Customization.Body]) if (!customizationDb[pmcProfile.Customization.Body])
{ {
const defaultBody = pmcProfile.Info.Side.toLowerCase() === "usec" const defaultBody
? customizationDbArray.find(x => x._name === "DefaultUsecBody") = pmcProfile.Info.Side.toLowerCase() === "usec"
: customizationDbArray.find(x => x._name === "DefaultBearBody"); ? customizationDbArray.find((x) => x._name === "DefaultUsecBody")
: customizationDbArray.find((x) => x._name === "DefaultBearBody");
pmcProfile.Customization.Body = defaultBody._id; pmcProfile.Customization.Body = defaultBody._id;
} }
// check Hands // check Hands
if (!customizationDb[pmcProfile.Customization.Hands]) if (!customizationDb[pmcProfile.Customization.Hands])
{ {
const defaultHands = pmcProfile.Info.Side.toLowerCase() === "usec" const defaultHands
? customizationDbArray.find(x => x._name === "DefaultUsecHands") = pmcProfile.Info.Side.toLowerCase() === "usec"
: customizationDbArray.find(x => x._name === "DefaultBearHands"); ? customizationDbArray.find((x) => x._name === "DefaultUsecHands")
: customizationDbArray.find((x) => x._name === "DefaultBearHands");
pmcProfile.Customization.Hands = defaultHands._id; pmcProfile.Customization.Hands = defaultHands._id;
} }
// check Hands // check Hands
if (!customizationDb[pmcProfile.Customization.Feet]) if (!customizationDb[pmcProfile.Customization.Feet])
{ {
const defaultFeet = pmcProfile.Info.Side.toLowerCase() === "usec" const defaultFeet
? customizationDbArray.find(x => x._name === "DefaultUsecFeet") = pmcProfile.Info.Side.toLowerCase() === "usec"
: customizationDbArray.find(x => x._name === "DefaultBearFeet"); ? customizationDbArray.find((x) => x._name === "DefaultUsecFeet")
: customizationDbArray.find((x) => x._name === "DefaultBearFeet");
pmcProfile.Customization.Feet = defaultFeet._id; pmcProfile.Customization.Feet = defaultFeet._id;
} }
} }
@ -1266,7 +1281,7 @@ export class ProfileFixerService
// Get all areas from templates/profiles.json // Get all areas from templates/profiles.json
for (const area of profileTemplate.character.Hideout.Areas) for (const area of profileTemplate.character.Hideout.Areas)
{ {
if (!pmcProfile.Hideout.Areas.find(x => x.type === area.type)) if (!pmcProfile.Hideout.Areas.find((x) => x.type === area.type))
{ {
pmcProfile.Hideout.Areas.push(area); pmcProfile.Hideout.Areas.push(area);
this.logger.debug(`Added missing hideout area ${area.type} to profile`); this.logger.debug(`Added missing hideout area ${area.type} to profile`);
@ -1300,10 +1315,10 @@ export class ProfileFixerService
// biome-ignore lint/suspicious/noGlobalIsNan: <value can be a valid string, Number.IsNaN() would ignore it> // biome-ignore lint/suspicious/noGlobalIsNan: <value can be a valid string, Number.IsNaN() would ignore it>
if (isNaN(fullProfile.characters.pmc.aid) || !fullProfile.info.aid) if (isNaN(fullProfile.characters.pmc.aid) || !fullProfile.info.aid)
{ {
fullProfile.characters.pmc.sessionId = <string><unknown>fullProfile.characters.pmc.aid; fullProfile.characters.pmc.sessionId = <string>(<unknown>fullProfile.characters.pmc.aid);
fullProfile.characters.pmc.aid = this.hashUtil.generateAccountId(); fullProfile.characters.pmc.aid = this.hashUtil.generateAccountId();
fullProfile.characters.scav.sessionId = <string><unknown>fullProfile.characters.pmc.sessionId; fullProfile.characters.scav.sessionId = <string>(<unknown>fullProfile.characters.pmc.sessionId);
fullProfile.characters.scav.aid = fullProfile.characters.pmc.aid; fullProfile.characters.scav.aid = fullProfile.characters.pmc.aid;
fullProfile.info.aid = fullProfile.characters.pmc.aid; fullProfile.info.aid = fullProfile.characters.pmc.aid;
@ -1329,7 +1344,7 @@ export class ProfileFixerService
// Clear stats object // Clear stats object
fullProfile.characters.pmc.Stats = { Eft: null }; fullProfile.characters.pmc.Stats = { Eft: null };
fullProfile.characters.pmc.Stats.Eft = <any><unknown>statsCopy; fullProfile.characters.pmc.Stats.Eft = <any>(<unknown>statsCopy);
} }
} }
@ -1355,8 +1370,8 @@ export class ProfileFixerService
for (const stageIndex in area.stages) for (const stageIndex in area.stages)
{ {
const stageInfo = area.stages[stageIndex]; const stageInfo = area.stages[stageIndex];
const matchingBonus = stageInfo.bonuses.find(x => const matchingBonus = stageInfo.bonuses.find(
x.templateId === bonus.templateId && x.type === bonus.type, (x) => x.templateId === bonus.templateId && x.type === bonus.type,
); );
if (matchingBonus) if (matchingBonus)
{ {
@ -1425,7 +1440,7 @@ export class ProfileFixerService
for (let i = profileQuests.length - 1; i >= 0; i--) for (let i = profileQuests.length - 1; i >= 0; i--)
{ {
if (!(quests[profileQuests[i].qid] || repeatableQuests.find(x => x._id === profileQuests[i].qid))) if (!(quests[profileQuests[i].qid] || repeatableQuests.find((x) => x._id === profileQuests[i].qid)))
{ {
profileQuests.splice(i, 1); profileQuests.splice(i, 1);
this.logger.success("Successfully removed orphaned quest that doesnt exist in our quest data"); this.logger.success("Successfully removed orphaned quest that doesnt exist in our quest data");

View File

@ -28,46 +28,48 @@ export class RagfairCategoriesService
): Record<string, number> ): Record<string, number>
{ {
// Get offers valid for search request, then reduce them down to just the counts // Get offers valid for search request, then reduce them down to just the counts
return offers.filter((offer) => return offers
{ .filter((offer) =>
const isTraderOffer = offer.user.memberType === MemberCategory.TRADER;
// Not level 15 and offer is from player, skip
if (!(fleaUnlocked || isTraderOffer))
{ {
return false; const isTraderOffer = offer.user.memberType === MemberCategory.TRADER;
}
// Remove items not for money when `removeBartering` is enabled // Not level 15 and offer is from player, skip
if ( if (!(fleaUnlocked || isTraderOffer))
searchRequestData.removeBartering {
&& (offer.requirements.length > 1 || !this.paymentHelper.isMoneyTpl(offer.requirements[0]._tpl)) return false;
) }
// Remove items not for money when `removeBartering` is enabled
if (
searchRequestData.removeBartering
&& (offer.requirements.length > 1 || !this.paymentHelper.isMoneyTpl(offer.requirements[0]._tpl))
)
{
return false;
}
// Remove when filter set to players only + offer is from trader
if (searchRequestData.offerOwnerType === OfferOwnerType.PLAYEROWNERTYPE && isTraderOffer)
{
return false;
}
// Remove when filter set to traders only + offer is not from trader
if (searchRequestData.offerOwnerType === OfferOwnerType.TRADEROWNERTYPE && !isTraderOffer)
{
return false;
}
// Passed checks, its a valid offer to process
return true;
})
.reduce((acc, offer) =>
{ {
return false; const itemTpl = offer.items[0]._tpl;
} // Increment the category or add if doesnt exist
acc[itemTpl] = (acc[itemTpl] || 0) + 1;
// Remove when filter set to players only + offer is from trader return acc;
if (searchRequestData.offerOwnerType === OfferOwnerType.PLAYEROWNERTYPE && isTraderOffer) }, {});
{
return false;
}
// Remove when filter set to traders only + offer is not from trader
if (searchRequestData.offerOwnerType === OfferOwnerType.TRADEROWNERTYPE && !isTraderOffer)
{
return false;
}
// Passed checks, its a valid offer to process
return true;
}).reduce((acc, offer) =>
{
const itemTpl = offer.items[0]._tpl;
// Increment the category or add if doesnt exist
acc[itemTpl] = (acc[itemTpl] || 0) + 1;
return acc;
}, {});
} }
} }

View File

@ -94,7 +94,7 @@ export class RagfairLinkedItemService
applyLinkedItems: (items: string[]) => void, applyLinkedItems: (items: string[]) => void,
): void ): void
{ {
const cylinderMod = cylinder._props.Slots.find(x => x._name === "mod_magazine"); const cylinderMod = cylinder._props.Slots.find((x) => x._name === "mod_magazine");
if (cylinderMod) if (cylinderMod)
{ {
// Get the first cylinder filter tpl // Get the first cylinder filter tpl

View File

@ -2,7 +2,6 @@ import { inject, injectable } from "tsyringe";
import { ProfileHelper } from "@spt-aki/helpers/ProfileHelper"; import { ProfileHelper } from "@spt-aki/helpers/ProfileHelper";
import { RagfairServerHelper } from "@spt-aki/helpers/RagfairServerHelper"; import { RagfairServerHelper } from "@spt-aki/helpers/RagfairServerHelper";
import { Item } from "@spt-aki/models/eft/common/tables/IItem"; import { Item } from "@spt-aki/models/eft/common/tables/IItem";
import { IItemEventRouterResponse } from "@spt-aki/models/eft/itemEvent/IItemEventRouterResponse";
import { IRagfairOffer } from "@spt-aki/models/eft/ragfair/IRagfairOffer"; import { IRagfairOffer } from "@spt-aki/models/eft/ragfair/IRagfairOffer";
import { ConfigTypes } from "@spt-aki/models/enums/ConfigTypes"; import { ConfigTypes } from "@spt-aki/models/enums/ConfigTypes";
import { IRagfairConfig } from "@spt-aki/models/spt/config/IRagfairConfig"; import { IRagfairConfig } from "@spt-aki/models/spt/config/IRagfairConfig";
@ -251,7 +250,7 @@ export class RagfairOfferService
const pmcId = String(playerOffer.user.id); const pmcId = String(playerOffer.user.id);
const profile = this.profileHelper.getProfileByPmcId(pmcId); const profile = this.profileHelper.getProfileByPmcId(pmcId);
const offerinProfileIndex = profile.RagfairInfo.offers.findIndex(o => o._id === playerOffer._id); const offerinProfileIndex = profile.RagfairInfo.offers.findIndex((o) => o._id === playerOffer._id);
if (offerinProfileIndex === -1) if (offerinProfileIndex === -1)
{ {
this.logger.warning( this.logger.warning(

View File

@ -73,11 +73,9 @@ export class RagfairPriceService implements OnLoad
*/ */
public generateStaticPrices(): void public generateStaticPrices(): void
{ {
for ( for (const item of Object.values(this.databaseServer.getTables().templates.items).filter(
const item of Object.values(this.databaseServer.getTables().templates.items).filter(x => (x) => x._type === "Item",
x._type === "Item", ))
)
)
{ {
this.prices.static[item._id] = Math.round(this.handbookHelper.getTemplatePrice(item._id)); this.prices.static[item._id] = Math.round(this.handbookHelper.getTemplatePrice(item._id));
} }
@ -195,7 +193,7 @@ export class RagfairPriceService implements OnLoad
*/ */
protected getPriceDifference(a: number, b: number): number protected getPriceDifference(a: number, b: number): number
{ {
return 100 * a / (a + b); return (100 * a) / (a + b);
} }
/** /**
@ -239,7 +237,10 @@ export class RagfairPriceService implements OnLoad
price += this.getDynamicItemPrice(item._tpl, desiredCurrency, item, offerItems, isPackOffer); price += this.getDynamicItemPrice(item._tpl, desiredCurrency, item, offerItems, isPackOffer);
// Check if the item is a weapon preset. // Check if the item is a weapon preset.
if (item?.upd?.sptPresetId && this.presetHelper.isPresetBaseClass(item.upd.sptPresetId, BaseClasses.WEAPON)) if (
item?.upd?.sptPresetId
&& this.presetHelper.isPresetBaseClass(item.upd.sptPresetId, BaseClasses.WEAPON)
)
{ {
// This is a weapon preset, which has it's own price calculation that takes into account the mods in the // This is a weapon preset, which has it's own price calculation that takes into account the mods in the
// preset. Since we've already calculated the price for the preset entire preset in // preset. Since we've already calculated the price for the preset entire preset in
@ -365,7 +366,7 @@ export class RagfairPriceService implements OnLoad
price: number, price: number,
): number ): number
{ {
const itemHandbookPrice = handbookPrices.find(handbookItem => handbookItem.Id === itemTpl); const itemHandbookPrice = handbookPrices.find((handbookItem) => handbookItem.Id === itemTpl);
if (!itemHandbookPrice) if (!itemHandbookPrice)
{ {
return price; return price;
@ -423,7 +424,8 @@ export class RagfairPriceService implements OnLoad
// Only adjust price if difference is > a percent AND item price passes threshold set in config // Only adjust price if difference is > a percent AND item price passes threshold set in config
if ( if (
priceDifferencePercent > this.ragfairConfig.dynamic.offerAdjustment.maxPriceDifferenceBelowHandbookPercent priceDifferencePercent
> this.ragfairConfig.dynamic.offerAdjustment.maxPriceDifferenceBelowHandbookPercent
&& itemPrice >= this.ragfairConfig.dynamic.offerAdjustment.priceThreshholdRub && itemPrice >= this.ragfairConfig.dynamic.offerAdjustment.priceThreshholdRub
) )
{ {
@ -467,8 +469,8 @@ export class RagfairPriceService implements OnLoad
} }
// Get mods on current gun not in default preset // Get mods on current gun not in default preset
const newOrReplacedModsInPresetVsDefault = weaponWithChildren.filter(x => const newOrReplacedModsInPresetVsDefault = weaponWithChildren.filter(
!presetResult.preset._items.some(y => y._tpl === x._tpl), (x) => !presetResult.preset._items.some((y) => y._tpl === x._tpl),
); );
// Add up extra mods price // Add up extra mods price
@ -483,8 +485,8 @@ export class RagfairPriceService implements OnLoad
if (newOrReplacedModsInPresetVsDefault.length >= 1) if (newOrReplacedModsInPresetVsDefault.length >= 1)
{ {
// Add up cost of mods replaced // Add up cost of mods replaced
const modsReplacedByNewMods = newOrReplacedModsInPresetVsDefault.filter(x => const modsReplacedByNewMods = newOrReplacedModsInPresetVsDefault.filter((x) =>
presetResult.preset._items.some(y => y.slotId === x.slotId), presetResult.preset._items.some((y) => y.slotId === x.slotId),
); );
// Add up replaced mods price // Add up replaced mods price
@ -536,17 +538,13 @@ export class RagfairPriceService implements OnLoad
if (nonDefaultPresets.length === 1) if (nonDefaultPresets.length === 1)
{ {
this.logger.debug( this.logger.debug(
`Item Id: ${weapon._tpl} has no default encyclopedia entry but only one preset (${ `Item Id: ${weapon._tpl} has no default encyclopedia entry but only one preset (${nonDefaultPresets[0]._name}), choosing preset (${nonDefaultPresets[0]._name})`,
nonDefaultPresets[0]._name
}), choosing preset (${nonDefaultPresets[0]._name})`,
); );
} }
else else
{ {
this.logger.debug( this.logger.debug(
`Item Id: ${weapon._tpl} has no default encyclopedia entry, choosing first preset (${ `Item Id: ${weapon._tpl} has no default encyclopedia entry, choosing first preset (${nonDefaultPresets[0]._name}) of ${nonDefaultPresets.length}`,
nonDefaultPresets[0]._name
}) of ${nonDefaultPresets.length}`,
); );
} }

View File

@ -70,8 +70,8 @@ export class RagfairTaxService
const requirementsPrice = requirementsValue * (sellInOnePiece ? 1 : offerItemCount); const requirementsPrice = requirementsValue * (sellInOnePiece ? 1 : offerItemCount);
const itemTaxMult = this.databaseServer.getTables().globals.config.RagFair.communityItemTax / 100.0; const itemTaxMult = this.databaseServer.getTables().globals.config.RagFair.communityItemTax / 100.0;
const requirementTaxMult = this.databaseServer.getTables().globals.config.RagFair.communityRequirementTax const requirementTaxMult
/ 100.0; = this.databaseServer.getTables().globals.config.RagFair.communityRequirementTax / 100.0;
let itemPriceMult = Math.log10(itemWorth / requirementsPrice); let itemPriceMult = Math.log10(itemWorth / requirementsPrice);
let requirementPriceMult = Math.log10(requirementsPrice / itemWorth); let requirementPriceMult = Math.log10(requirementsPrice / itemWorth);
@ -88,11 +88,11 @@ export class RagfairTaxService
itemPriceMult = 4 ** itemPriceMult; itemPriceMult = 4 ** itemPriceMult;
requirementPriceMult = 4 ** requirementPriceMult; requirementPriceMult = 4 ** requirementPriceMult;
const hideoutFleaTaxDiscountBonus = pmcData.Bonuses.find(b => b.type === BonusType.RAGFAIR_COMMISSION); const hideoutFleaTaxDiscountBonus = pmcData.Bonuses.find((b) => b.type === BonusType.RAGFAIR_COMMISSION);
const taxDiscountPercent = hideoutFleaTaxDiscountBonus ? Math.abs(hideoutFleaTaxDiscountBonus.value) : 0; const taxDiscountPercent = hideoutFleaTaxDiscountBonus ? Math.abs(hideoutFleaTaxDiscountBonus.value) : 0;
const tax = itemWorth * itemTaxMult * itemPriceMult const tax
+ requirementsPrice * requirementTaxMult * requirementPriceMult; = itemWorth * itemTaxMult * itemPriceMult + requirementsPrice * requirementTaxMult * requirementPriceMult;
const discountedTax = tax * (1.0 - taxDiscountPercent / 100.0); const discountedTax = tax * (1.0 - taxDiscountPercent / 100.0);
const itemComissionMult = itemTemplate._props.RagFairCommissionModifier const itemComissionMult = itemTemplate._props.RagFairCommissionModifier
? itemTemplate._props.RagFairCommissionModifier ? itemTemplate._props.RagFairCommissionModifier
@ -123,7 +123,8 @@ export class RagfairTaxService
// In client, all item slots are traversed and any items contained within have their values added // In client, all item slots are traversed and any items contained within have their values added
if (isRootItem) if (isRootItem)
{ // Since we get a flat list of all child items, we only want to recurse from parent item {
// Since we get a flat list of all child items, we only want to recurse from parent item
const itemChildren = this.itemHelper.findAndReturnChildrenAsItems(pmcData.Inventory.items, item._id); const itemChildren = this.itemHelper.findAndReturnChildrenAsItems(pmcData.Inventory.items, item._id);
if (itemChildren.length > 1) if (itemChildren.length > 1)
{ {
@ -152,38 +153,40 @@ export class RagfairTaxService
if ("Key" in item.upd && itemTemplate._props.MaximumNumberOfUsage > 0) if ("Key" in item.upd && itemTemplate._props.MaximumNumberOfUsage > 0)
{ {
worth = worth / itemTemplate._props.MaximumNumberOfUsage worth
* (itemTemplate._props.MaximumNumberOfUsage - item.upd.Key.NumberOfUsages); = (worth / itemTemplate._props.MaximumNumberOfUsage)
* (itemTemplate._props.MaximumNumberOfUsage - item.upd.Key.NumberOfUsages);
} }
if ("Resource" in item.upd && itemTemplate._props.MaxResource > 0) if ("Resource" in item.upd && itemTemplate._props.MaxResource > 0)
{ {
worth = worth * 0.1 + worth * 0.9 / itemTemplate._props.MaxResource * item.upd.Resource.Value; worth = worth * 0.1 + ((worth * 0.9) / itemTemplate._props.MaxResource) * item.upd.Resource.Value;
} }
if ("SideEffect" in item.upd && itemTemplate._props.MaxResource > 0) if ("SideEffect" in item.upd && itemTemplate._props.MaxResource > 0)
{ {
worth = worth * 0.1 + worth * 0.9 / itemTemplate._props.MaxResource * item.upd.SideEffect.Value; worth = worth * 0.1 + ((worth * 0.9) / itemTemplate._props.MaxResource) * item.upd.SideEffect.Value;
} }
if ("MedKit" in item.upd && itemTemplate._props.MaxHpResource > 0) if ("MedKit" in item.upd && itemTemplate._props.MaxHpResource > 0)
{ {
worth = worth / itemTemplate._props.MaxHpResource * item.upd.MedKit.HpResource; worth = (worth / itemTemplate._props.MaxHpResource) * item.upd.MedKit.HpResource;
} }
if ("FoodDrink" in item.upd && itemTemplate._props.MaxResource > 0) if ("FoodDrink" in item.upd && itemTemplate._props.MaxResource > 0)
{ {
worth = worth / itemTemplate._props.MaxResource * item.upd.FoodDrink.HpPercent; worth = (worth / itemTemplate._props.MaxResource) * item.upd.FoodDrink.HpPercent;
} }
if ("Repairable" in item.upd && <number > itemTemplate._props.armorClass > 0) if ("Repairable" in item.upd && <number > itemTemplate._props.armorClass > 0)
{ {
const num2 = 0.01 * 0.0 ** item.upd.Repairable.MaxDurability; const num2 = 0.01 * 0.0 ** item.upd.Repairable.MaxDurability;
worth = worth * (item.upd.Repairable.MaxDurability / itemTemplate._props.Durability - num2) worth
- Math.floor( = worth * (item.upd.Repairable.MaxDurability / itemTemplate._props.Durability - num2)
itemTemplate._props.RepairCost - Math.floor(
* (item.upd.Repairable.MaxDurability - item.upd.Repairable.Durability), itemTemplate._props.RepairCost
); * (item.upd.Repairable.MaxDurability - item.upd.Repairable.Durability),
);
} }
return worth * itemCount; return worth * itemCount;

View File

@ -80,7 +80,7 @@ export class RaidTimeAdjustmentService
{ {
// Remove waves that spawned before the player joined // Remove waves that spawned before the player joined
const originalWaveCount = mapBase.waves.length; const originalWaveCount = mapBase.waves.length;
mapBase.waves = mapBase.waves.filter(x => x.time_max > raidAdjustments.simulatedRaidStartSeconds); mapBase.waves = mapBase.waves.filter((x) => x.time_max > raidAdjustments.simulatedRaidStartSeconds);
// Adjust wave min/max times to match new simulated start // Adjust wave min/max times to match new simulated start
for (const wave of mapBase.waves) for (const wave of mapBase.waves)

View File

@ -60,7 +60,7 @@ export class RepairService
traderId: string, traderId: string,
): RepairDetails ): RepairDetails
{ {
const itemToRepair = pmcData.Inventory.items.find(x => x._id === repairItemDetails._id); const itemToRepair = pmcData.Inventory.items.find((x) => x._id === repairItemDetails._id);
if (itemToRepair === undefined) if (itemToRepair === undefined)
{ {
throw new Error(`Item ${repairItemDetails._id} not found in profile inventory, unable to repair`); throw new Error(`Item ${repairItemDetails._id} not found in profile inventory, unable to repair`);
@ -121,10 +121,12 @@ export class RepairService
): void ): void
{ {
const options: IProcessBuyTradeRequestData = { const options: IProcessBuyTradeRequestData = {
scheme_items: [{ scheme_items: [
id: "5449016a4bdc2d6f028b456f", // Rouble tpl {
count: Math.round(repairCost), id: "5449016a4bdc2d6f028b456f", // Rouble tpl
}], count: Math.round(repairCost),
},
],
tid: traderId, tid: traderId,
Action: "SptRepair", Action: "SptRepair",
type: "", type: "",
@ -184,8 +186,8 @@ export class RepairService
const isHeavyArmor = itemDetails[1]._props.ArmorType === "Heavy"; const isHeavyArmor = itemDetails[1]._props.ArmorType === "Heavy";
const vestSkillToLevel = isHeavyArmor ? SkillTypes.HEAVY_VESTS : SkillTypes.LIGHT_VESTS; const vestSkillToLevel = isHeavyArmor ? SkillTypes.HEAVY_VESTS : SkillTypes.LIGHT_VESTS;
const pointsToAddToVestSkill = repairDetails.repairPoints const pointsToAddToVestSkill
* this.repairConfig.armorKitSkillPointGainPerRepairPointMultiplier; = repairDetails.repairPoints * this.repairConfig.armorKitSkillPointGainPerRepairPointMultiplier;
this.logger.debug(`Added: ${pointsToAddToVestSkill} ${vestSkillToLevel} skill`); this.logger.debug(`Added: ${pointsToAddToVestSkill} ${vestSkillToLevel} skill`);
this.profileHelper.addSkillPointsToPlayer(pmcData, vestSkillToLevel, pointsToAddToVestSkill); this.profileHelper.addSkillPointsToPlayer(pmcData, vestSkillToLevel, pointsToAddToVestSkill);
@ -205,10 +207,12 @@ export class RepairService
if (repairDetails.repairedByKit) if (repairDetails.repairedByKit)
{ {
// Weapons/armor have different multipliers // Weapons/armor have different multipliers
const intRepairMultiplier const intRepairMultiplier = this.itemHelper.isOfBaseclass(
= this.itemHelper.isOfBaseclass(repairDetails.repairedItem._tpl, BaseClasses.WEAPON) repairDetails.repairedItem._tpl,
? this.repairConfig.repairKitIntellectGainMultiplier.weapon BaseClasses.WEAPON,
: this.repairConfig.repairKitIntellectGainMultiplier.armor; )
? this.repairConfig.repairKitIntellectGainMultiplier.weapon
: this.repairConfig.repairKitIntellectGainMultiplier.armor;
// Limit gain to a max value defined in config.maxIntellectGainPerRepair // Limit gain to a max value defined in config.maxIntellectGainPerRepair
return Math.min( return Math.min(
@ -302,7 +306,7 @@ export class RepairService
// Find and use repair kit defined in body // Find and use repair kit defined in body
for (const repairKit of repairKits) for (const repairKit of repairKits)
{ {
const repairKitInInventory = pmcData.Inventory.items.find(x => x._id === repairKit._id); const repairKitInInventory = pmcData.Inventory.items.find((x) => x._id === repairKit._id);
const repairKitDetails = itemsDb[repairKitInInventory._tpl]; const repairKitDetails = itemsDb[repairKitInInventory._tpl];
const repairKitReductionAmount = repairKit.count; const repairKitReductionAmount = repairKit.count;
@ -336,8 +340,8 @@ export class RepairService
const globalRepairSettings = globals.config.RepairSettings; const globalRepairSettings = globals.config.RepairSettings;
const intellectRepairPointsPerLevel = globals.config.SkillsSettings.Intellect.RepairPointsCostReduction; const intellectRepairPointsPerLevel = globals.config.SkillsSettings.Intellect.RepairPointsCostReduction;
const profileIntellectLevel = this.profileHelper.getSkillFromProfile(pmcData, SkillTypes.INTELLECT)?.Progress const profileIntellectLevel
?? 0; = this.profileHelper.getSkillFromProfile(pmcData, SkillTypes.INTELLECT)?.Progress ?? 0;
const intellectPointReduction = intellectRepairPointsPerLevel * Math.trunc(profileIntellectLevel / 100); const intellectPointReduction = intellectRepairPointsPerLevel * Math.trunc(profileIntellectLevel / 100);
if (isArmor) if (isArmor)
@ -370,11 +374,11 @@ export class RepairService
*/ */
protected getBonusMultiplierValue(skillBonus: BonusType, pmcData: IPmcData): number protected getBonusMultiplierValue(skillBonus: BonusType, pmcData: IPmcData): number
{ {
const bonusesMatched = pmcData?.Bonuses?.filter(b => b.type === skillBonus); const bonusesMatched = pmcData?.Bonuses?.filter((b) => b.type === skillBonus);
let value = 1; let value = 1;
if (bonusesMatched != null) if (bonusesMatched != null)
{ {
const sumedPercentage = bonusesMatched.map(b => b.value).reduce((v1, v2) => v1 + v2, 0); const sumedPercentage = bonusesMatched.map((b) => b.value).reduce((v1, v2) => v1 + v2, 0);
value = 1 + sumedPercentage / 100; value = 1 + sumedPercentage / 100;
} }

View File

@ -342,7 +342,7 @@ export class SeasonalEventService
} }
// Get non-christmas items // Get non-christmas items
const nonChristmasTpls = Object.keys(containerItems).filter(tpl => !christmasItems.includes(tpl)); const nonChristmasTpls = Object.keys(containerItems).filter((tpl) => !christmasItems.includes(tpl));
const intermediaryDict = {}; const intermediaryDict = {};
for (const tpl of nonChristmasTpls) for (const tpl of nonChristmasTpls)
@ -368,7 +368,7 @@ export class SeasonalEventService
switch (eventType.toLowerCase()) switch (eventType.toLowerCase())
{ {
case SeasonalEventType.HALLOWEEN.toLowerCase(): case SeasonalEventType.HALLOWEEN.toLowerCase():
globalConfig.EventType = globalConfig.EventType.filter(x => x !== "None"); globalConfig.EventType = globalConfig.EventType.filter((x) => x !== "None");
globalConfig.EventType.push("Halloween"); globalConfig.EventType.push("Halloween");
globalConfig.EventType.push("HalloweenIllumination"); globalConfig.EventType.push("HalloweenIllumination");
globalConfig.Health.ProfileHealthSettings.DefaultStimulatorBuff = "Buffs_Halloween"; globalConfig.Health.ProfileHealthSettings.DefaultStimulatorBuff = "Buffs_Halloween";
@ -380,7 +380,7 @@ export class SeasonalEventService
this.adjustTraderIcons(eventType); this.adjustTraderIcons(eventType);
break; break;
case SeasonalEventType.CHRISTMAS.toLowerCase(): case SeasonalEventType.CHRISTMAS.toLowerCase():
globalConfig.EventType = globalConfig.EventType.filter(x => x !== "None"); globalConfig.EventType = globalConfig.EventType.filter((x) => x !== "None");
globalConfig.EventType.push("Christmas"); globalConfig.EventType.push("Christmas");
this.addEventGearToBots(eventType); this.addEventGearToBots(eventType);
this.addGifterBotToMaps(); this.addGifterBotToMaps();
@ -435,7 +435,7 @@ export class SeasonalEventService
{ {
const mapBosses: BossLocationSpawn[] const mapBosses: BossLocationSpawn[]
= this.databaseServer.getTables().locations[mapKey].base.BossLocationSpawn; = this.databaseServer.getTables().locations[mapKey].base.BossLocationSpawn;
if (!mapBosses.find(x => x.BossName === boss.BossName)) if (!mapBosses.find((x) => x.BossName === boss.BossName))
{ {
this.databaseServer.getTables().locations[mapKey].base.BossLocationSpawn.push(...bossesToAdd); this.databaseServer.getTables().locations[mapKey].base.BossLocationSpawn.push(...bossesToAdd);
} }
@ -474,9 +474,11 @@ export class SeasonalEventService
break; break;
} }
this.databaseImporter.loadImages(`${this.databaseImporter.getSptDataPath()}images/`, ["traders"], [ this.databaseImporter.loadImages(
"/files/trader/avatar/", `${this.databaseImporter.getSptDataPath()}images/`,
]); ["traders"],
["/files/trader/avatar/"],
);
} }
/** /**
@ -487,8 +489,9 @@ export class SeasonalEventService
const gifterBot = this.databaseServer.getTables().bots.types.gifter; const gifterBot = this.databaseServer.getTables().bots.types.gifter;
for (const difficulty in gifterBot.difficulty) for (const difficulty in gifterBot.difficulty)
{ {
gifterBot.difficulty[difficulty].Patrol.ITEMS_TO_DROP = Object.keys(gifterBot.inventory.items.Backpack) gifterBot.difficulty[difficulty].Patrol.ITEMS_TO_DROP = Object.keys(
.join(", "); gifterBot.inventory.items.Backpack,
).join(", ");
} }
} }
@ -574,7 +577,7 @@ export class SeasonalEventService
{ {
const mapData: ILocation = maps[gifterMapSettings.map]; const mapData: ILocation = maps[gifterMapSettings.map];
// Dont add gifter to map twice // Dont add gifter to map twice
if (mapData.base.BossLocationSpawn.some(boss => boss.BossName === "gifter")) if (mapData.base.BossLocationSpawn.some((boss) => boss.BossName === "gifter"))
{ {
continue; continue;
} }

View File

@ -121,7 +121,7 @@ export class TraderPurchasePersisterService
for (const purchaseKey in profile.traderPurchases[traderId]) for (const purchaseKey in profile.traderPurchases[traderId])
{ {
const traderUpdateDetails = this.traderConfig.updateTime.find(x => x.traderId === traderId); const traderUpdateDetails = this.traderConfig.updateTime.find((x) => x.traderId === traderId);
if (!traderUpdateDetails) if (!traderUpdateDetails)
{ {
this.logger.error( this.logger.error(
@ -135,8 +135,9 @@ export class TraderPurchasePersisterService
} }
const purchaseDetails = profile.traderPurchases[traderId][purchaseKey]; const purchaseDetails = profile.traderPurchases[traderId][purchaseKey];
const resetTimeForItem = purchaseDetails.purchaseTimestamp const resetTimeForItem
+ this.randomUtil.getInt(traderUpdateDetails.seconds.min, traderUpdateDetails.seconds.max); = purchaseDetails.purchaseTimestamp
+ this.randomUtil.getInt(traderUpdateDetails.seconds.min, traderUpdateDetails.seconds.max);
if (resetTimeForItem < this.timeUtil.getTimestamp()) if (resetTimeForItem < this.timeUtil.getTimestamp())
{ {
// Item was purchased far enough in past a trader refresh would have occured, remove purchase record from profile // Item was purchased far enough in past a trader refresh would have occured, remove purchase record from profile

View File

@ -46,7 +46,7 @@ export class TraderServicesService
{ {
for (const questId of service.requirements.completedQuests) for (const questId of service.requirements.completedQuests)
{ {
const quest = pmcData.Quests.find(x => x.qid === questId); const quest = pmcData.Quests.find((x) => x.qid === questId);
if (!quest || quest.status !== QuestStatus.Success) if (!quest || quest.status !== QuestStatus.Success)
{ {
servicesToDelete.push(service.serviceType); servicesToDelete.push(service.serviceType);
@ -57,7 +57,7 @@ export class TraderServicesService
} }
// Clear any unavailable services from the list // Clear any unavailable services from the list
traderServices = traderServices.filter(x => !servicesToDelete.includes(x.serviceType)); traderServices = traderServices.filter((x) => !servicesToDelete.includes(x.serviceType));
return traderServices; return traderServices;
} }

View File

@ -2,7 +2,10 @@ import { DynamicRouter, RouteAction } from "@spt-aki/di/Router";
export class DynamicRouterMod extends DynamicRouter export class DynamicRouterMod extends DynamicRouter
{ {
public constructor(routes: RouteAction[], private topLevelRoute: string) public constructor(
routes: RouteAction[],
private topLevelRoute: string,
)
{ {
super(routes); super(routes);
} }

View File

@ -7,8 +7,7 @@ export class HttpListenerMod implements IHttpListener
private canHandleOverride: (sessionId: string, req: IncomingMessage) => boolean, private canHandleOverride: (sessionId: string, req: IncomingMessage) => boolean,
private handleOverride: (sessionId: string, req: IncomingMessage, resp: ServerResponse) => void, private handleOverride: (sessionId: string, req: IncomingMessage, resp: ServerResponse) => void,
) )
{ {}
}
public canHandle(sessionId: string, req: IncomingMessage): boolean public canHandle(sessionId: string, req: IncomingMessage): boolean
{ {

View File

@ -2,7 +2,10 @@ import { OnLoad } from "@spt-aki/di/OnLoad";
export class OnLoadMod implements OnLoad export class OnLoadMod implements OnLoad
{ {
public constructor(private onLoadOverride: () => void, private getRouteOverride: () => string) public constructor(
private onLoadOverride: () => void,
private getRouteOverride: () => string,
)
{ {
// super(); // super();
} }

View File

@ -6,8 +6,7 @@ export class OnUpdateMod implements OnUpdate
private onUpdateOverride: (timeSinceLastRun: number) => boolean, private onUpdateOverride: (timeSinceLastRun: number) => boolean,
private getRouteOverride: () => string, private getRouteOverride: () => string,
) )
{ {}
}
public async onUpdate(timeSinceLastRun: number): Promise<boolean> public async onUpdate(timeSinceLastRun: number): Promise<boolean>
{ {

View File

@ -7,7 +7,11 @@ export class OnUpdateModService
constructor(protected container: DependencyContainer) constructor(protected container: DependencyContainer)
{} {}
public registerOnUpdate(name: string, onUpdate: (timeSinceLastRun: number) => boolean, getRoute: () => string): void public registerOnUpdate(
name: string,
onUpdate: (timeSinceLastRun: number) => boolean,
getRoute: () => string,
): void
{ {
this.container.register(name, { useValue: new OnUpdateMod(onUpdate, getRoute) }); this.container.register(name, { useValue: new OnUpdateMod(onUpdate, getRoute) });
this.container.registerType("OnUpdate", name); this.container.registerType("OnUpdate", name);

View File

@ -2,7 +2,10 @@ import { RouteAction, StaticRouter } from "@spt-aki/di/Router";
export class StaticRouterMod extends StaticRouter export class StaticRouterMod extends StaticRouter
{ {
public constructor(routes: RouteAction[], private topLevelRoute: string) public constructor(
routes: RouteAction[],
private topLevelRoute: string,
)
{ {
super(routes); super(routes);
} }

View File

@ -42,7 +42,7 @@ export class CompareUtil
{ {
return false; return false;
} }
return arr1.every(vOf1 => arr2.find(vOf2 => this.recursiveCompare(vOf1, vOf2))); return arr1.every((vOf1) => arr2.find((vOf2) => this.recursiveCompare(vOf1, vOf2)));
} }
for (const propOf1 in v1) for (const propOf1 in v1)
{ {

View File

@ -107,9 +107,8 @@ export class DatabaseImporter implements OnLoad
(fileWithPath: string, data: string) => this.onReadValidate(fileWithPath, data), (fileWithPath: string, data: string) => this.onReadValidate(fileWithPath, data),
); );
const validation = this.valid === VaildationResult.FAILED || this.valid === VaildationResult.NOT_FOUND const validation
? "." = this.valid === VaildationResult.FAILED || this.valid === VaildationResult.NOT_FOUND ? "." : "";
: "";
this.logger.info(`${this.localisationService.getText("importing_database_finish")}${validation}`); this.logger.info(`${this.localisationService.getText("importing_database_finish")}${validation}`);
this.databaseServer.setTables(dataToImport); this.databaseServer.setTables(dataToImport);
} }

View File

@ -7,14 +7,14 @@ import { HttpServerHelper } from "@spt-aki/helpers/HttpServerHelper";
export class HttpFileUtil export class HttpFileUtil
{ {
constructor(@inject("HttpServerHelper") protected httpServerHelper: HttpServerHelper) constructor(@inject("HttpServerHelper") protected httpServerHelper: HttpServerHelper)
{ {}
}
public sendFile(resp: ServerResponse, filePath: string): void public sendFile(resp: ServerResponse, filePath: string): void
{ {
const pathSlic = filePath.split("/"); const pathSlic = filePath.split("/");
const type = this.httpServerHelper.getMimeText(pathSlic[pathSlic.length - 1].split(".").at(-1)) const type
|| this.httpServerHelper.getMimeText("txt"); = this.httpServerHelper.getMimeText(pathSlic[pathSlic.length - 1].split(".").at(-1))
|| this.httpServerHelper.getMimeText("txt");
const fileStream = fs.createReadStream(filePath); const fileStream = fs.createReadStream(filePath);
fileStream.on("open", () => fileStream.on("open", () =>

View File

@ -17,12 +17,12 @@ export class HttpResponseUtil
protected clearString(s: string): any protected clearString(s: string): any
{ {
return s.replace(/[\b]/g, "").replace(/[\f]/g, "").replace(/[\n]/g, "") return s
.replace(/[\b]/g, "")
.replace(/[\f]/g, "")
.replace(/[\n]/g, "")
.replace(/[\r]/g, "") .replace(/[\r]/g, "")
.replace( .replace(/[\t]/g, "");
/[\t]/g,
"",
);
} }
/** /**

View File

@ -8,7 +8,10 @@ import { VFS } from "@spt-aki/utils/VFS";
@injectable() @injectable()
export class ImporterUtil export class ImporterUtil
{ {
constructor(@inject("VFS") protected vfs: VFS, @inject("JsonUtil") protected jsonUtil: JsonUtil) constructor(
@inject("VFS") protected vfs: VFS,
@inject("JsonUtil") protected jsonUtil: JsonUtil,
)
{} {}
/** /**
@ -55,7 +58,7 @@ export class ImporterUtil
// set all loadRecursive to be executed asynchronously // set all loadRecursive to be executed asynchronously
const resEntries = Object.entries(result); const resEntries = Object.entries(result);
const resResolved = await Promise.all(resEntries.map(ent => ent[1])); const resResolved = await Promise.all(resEntries.map((ent) => ent[1]));
for (let resIdx = 0; resIdx < resResolved.length; resIdx++) for (let resIdx = 0; resIdx < resResolved.length; resIdx++)
{ {
resEntries[resIdx][1] = resResolved[resIdx]; resEntries[resIdx][1] = resResolved[resIdx];
@ -70,9 +73,13 @@ export class ImporterUtil
* @param filepath Path to folder with files * @param filepath Path to folder with files
* @returns * @returns
*/ */
public loadRecursive<T>(filepath: string, onReadCallback: (fileWithPath: string, data: string) => void = () => public loadRecursive<T>(
{}, onObjectDeserialized: (fileWithPath: string, object: any) => void = () => filepath: string,
{}): T onReadCallback: (fileWithPath: string, data: string) => void = () =>
{},
onObjectDeserialized: (fileWithPath: string, object: any) => void = () =>
{},
): T
{ {
const result = {} as T; const result = {} as T;
@ -123,14 +130,14 @@ export class ImporterUtil
const files = this.vfs.getFiles(filepath); const files = this.vfs.getFiles(filepath);
const directories = this.vfs.getDirs(filepath); const directories = this.vfs.getDirs(filepath);
directoriesToRead.enqueueAll(directories.map(d => `${filepath}${d}`)); directoriesToRead.enqueueAll(directories.map((d) => `${filepath}${d}`));
filesToProcess.enqueueAll(files.map(f => new VisitNode(filepath, f))); filesToProcess.enqueueAll(files.map((f) => new VisitNode(filepath, f)));
while (directoriesToRead.length !== 0) while (directoriesToRead.length !== 0)
{ {
const directory = directoriesToRead.dequeue(); const directory = directoriesToRead.dequeue();
filesToProcess.enqueueAll(this.vfs.getFiles(directory).map(f => new VisitNode(`${directory}/`, f))); filesToProcess.enqueueAll(this.vfs.getFiles(directory).map((f) => new VisitNode(`${directory}/`, f)));
directoriesToRead.enqueueAll(this.vfs.getDirs(directory).map(d => `${directory}/${d}`)); directoriesToRead.enqueueAll(this.vfs.getDirs(directory).map((d) => `${directory}/${d}`));
} }
while (filesToProcess.length !== 0) while (filesToProcess.length !== 0)
@ -140,21 +147,24 @@ export class ImporterUtil
{ {
const filePathAndName = `${fileNode.filePath}${fileNode.fileName}`; const filePathAndName = `${fileNode.filePath}${fileNode.fileName}`;
promises.push( promises.push(
this.vfs.readFileAsync(filePathAndName).then(async (fileData) => this.vfs
{ .readFileAsync(filePathAndName)
onReadCallback(filePathAndName, fileData); .then(async (fileData) =>
return this.jsonUtil.deserializeWithCacheCheckAsync<any>(fileData, filePathAndName); {
}).then(async (fileDeserialized) => onReadCallback(filePathAndName, fileData);
{ return this.jsonUtil.deserializeWithCacheCheckAsync<any>(fileData, filePathAndName);
onObjectDeserialized(filePathAndName, fileDeserialized); })
const strippedFilePath = this.vfs.stripExtension(filePathAndName).replace(filepath, ""); .then(async (fileDeserialized) =>
this.placeObject(fileDeserialized, strippedFilePath, result, strippablePath); {
}), onObjectDeserialized(filePathAndName, fileDeserialized);
const strippedFilePath = this.vfs.stripExtension(filePathAndName).replace(filepath, "");
this.placeObject(fileDeserialized, strippedFilePath, result, strippablePath);
}),
); );
} }
} }
await Promise.all(promises).catch(e => console.error(e)); await Promise.all(promises).catch((e) => console.error(e));
return result; return result;
} }
@ -186,6 +196,9 @@ export class ImporterUtil
class VisitNode class VisitNode
{ {
constructor(public filePath: string, public fileName: string) constructor(
public filePath: string,
public fileName: string,
)
{} {}
} }

View File

@ -178,7 +178,8 @@ export class JsonUtil
{ {
const { data, changed } = fixJson(jsonString); const { data, changed } = fixJson(jsonString);
if (changed) if (changed)
{ // data invalid, return it {
// data invalid, return it
this.logger.error(`${filePath} - Detected faulty json, please fix your json file using VSCodium`); this.logger.error(`${filePath} - Detected faulty json, please fix your json file using VSCodium`);
} }
else else

View File

@ -39,7 +39,7 @@ export class MathUtil
*/ */
public arrayProd(values: number[], factor: number): number[] public arrayProd(values: number[], factor: number): number[]
{ {
return values.map(x => x * factor); return values.map((x) => x * factor);
} }
/** /**
@ -49,7 +49,7 @@ export class MathUtil
*/ */
public arrayAdd(values: number[], summand: number): number[] public arrayAdd(values: number[], summand: number): number[]
{ {
return values.map(x => x + summand); return values.map((x) => x + summand);
} }
/** /**
@ -101,7 +101,7 @@ export class MathUtil
{ {
if (xp >= x[i] && xp <= x[i + 1]) if (xp >= x[i] && xp <= x[i + 1])
{ {
return y[i] + (xp - x[i]) * (y[i + 1] - y[i]) / (x[i + 1] - x[i]); return y[i] + ((xp - x[i]) * (y[i + 1] - y[i])) / (x[i + 1] - x[i]);
} }
} }
} }

View File

@ -25,7 +25,7 @@ export class ObjectId
let hexString = ""; let hexString = "";
for (let i = 0; i < byteArray.length; i++) for (let i = 0; i < byteArray.length; i++)
{ {
hexString += `0${(byteArray[i] & 0xFF).toString(16)}`.slice(-2); hexString += `0${(byteArray[i] & 0xff).toString(16)}`.slice(-2);
} }
return hexString; return hexString;
} }
@ -42,16 +42,16 @@ export class ObjectId
const objectIdBinary = Buffer.alloc(12); const objectIdBinary = Buffer.alloc(12);
objectIdBinary[3] = time & 0xff; objectIdBinary[3] = time & 0xff;
objectIdBinary[2] = time >> 8 & 0xff; objectIdBinary[2] = (time >> 8) & 0xff;
objectIdBinary[1] = time >> 16 & 0xff; objectIdBinary[1] = (time >> 16) & 0xff;
objectIdBinary[0] = time >> 24 & 0xff; objectIdBinary[0] = (time >> 24) & 0xff;
objectIdBinary[4] = this.randomBytes[0]; objectIdBinary[4] = this.randomBytes[0];
objectIdBinary[5] = this.randomBytes[1]; objectIdBinary[5] = this.randomBytes[1];
objectIdBinary[6] = this.randomBytes[2]; objectIdBinary[6] = this.randomBytes[2];
objectIdBinary[7] = this.randomBytes[3]; objectIdBinary[7] = this.randomBytes[3];
objectIdBinary[8] = this.randomBytes[4]; objectIdBinary[8] = this.randomBytes[4];
objectIdBinary[9] = counter >> 16 & 0xff; objectIdBinary[9] = (counter >> 16) & 0xff;
objectIdBinary[10] = counter >> 8 & 0xff; objectIdBinary[10] = (counter >> 8) & 0xff;
objectIdBinary[11] = counter & 0xff; objectIdBinary[11] = counter & 0xff;
return this.toHexString(objectIdBinary); return this.toHexString(objectIdBinary);

View File

@ -7,7 +7,10 @@ export class RagfairOfferHolder
protected offersByTemplate: Map<string, Map<string, IRagfairOffer>>; protected offersByTemplate: Map<string, Map<string, IRagfairOffer>>;
protected offersByTrader: Map<string, Map<string, IRagfairOffer>>; protected offersByTrader: Map<string, Map<string, IRagfairOffer>>;
constructor(protected maxOffersPerTemplate: number, protected ragfairServerHelper: RagfairServerHelper) constructor(
protected maxOffersPerTemplate: number,
protected ragfairServerHelper: RagfairServerHelper,
)
{ {
this.offersById = new Map(); this.offersById = new Map();
this.offersByTemplate = new Map(); this.offersByTemplate = new Map();
@ -122,7 +125,7 @@ export class RagfairOfferHolder
*/ */
public getStaleOffers(time: number): Array<IRagfairOffer> public getStaleOffers(time: number): Array<IRagfairOffer>
{ {
return this.getOffers().filter(o => this.isStale(o, time)); return this.getOffers().filter((o) => this.isStale(o, time));
} }
protected addOfferByTemplates(template: string, offer: IRagfairOffer): void protected addOfferByTemplates(template: string, offer: IRagfairOffer): void

View File

@ -20,7 +20,11 @@ import { MathUtil } from "@spt-aki/utils/MathUtil";
*/ */
export class ProbabilityObjectArray<K, V = undefined> extends Array<ProbabilityObject<K, V>> export class ProbabilityObjectArray<K, V = undefined> extends Array<ProbabilityObject<K, V>>
{ {
constructor(private mathUtil: MathUtil, private cloner: ICloner, ...items: ProbabilityObject<K, V>[]) constructor(
private mathUtil: MathUtil,
private cloner: ICloner,
...items: ProbabilityObject<K, V>[]
)
{ {
super(); super();
this.push(...items); this.push(...items);
@ -69,7 +73,7 @@ export class ProbabilityObjectArray<K, V = undefined> extends Array<ProbabilityO
*/ */
drop(key: K): ProbabilityObjectArray<K, V> drop(key: K): ProbabilityObjectArray<K, V>
{ {
return this.filter(r => r.key !== key); return this.filter((r) => r.key !== key);
} }
/** /**
@ -79,7 +83,7 @@ export class ProbabilityObjectArray<K, V = undefined> extends Array<ProbabilityO
*/ */
data(key: K): V data(key: K): V
{ {
return this.filter(r => r.key === key)[0]?.data; return this.filter((r) => r.key === key)[0]?.data;
} }
/** /**
@ -94,7 +98,7 @@ export class ProbabilityObjectArray<K, V = undefined> extends Array<ProbabilityO
*/ */
probability(key: K): number probability(key: K): number
{ {
return this.filter(r => r.key === key)[0].relativeProbability; return this.filter((r) => r.key === key)[0].relativeProbability;
} }
/** /**
@ -108,7 +112,7 @@ export class ProbabilityObjectArray<K, V = undefined> extends Array<ProbabilityO
*/ */
maxProbability(): number maxProbability(): number
{ {
return Math.max(...this.map(x => x.relativeProbability)); return Math.max(...this.map((x) => x.relativeProbability));
} }
/** /**
@ -122,7 +126,7 @@ export class ProbabilityObjectArray<K, V = undefined> extends Array<ProbabilityO
*/ */
minProbability(): number minProbability(): number
{ {
return Math.min(...this.map(x => x.relativeProbability)); return Math.min(...this.map((x) => x.relativeProbability));
} }
/** /**
@ -140,19 +144,22 @@ export class ProbabilityObjectArray<K, V = undefined> extends Array<ProbabilityO
return []; return [];
} }
const { probArray, keyArray } = this.reduce((acc, x) => const { probArray, keyArray } = this.reduce(
{ (acc, x) =>
acc.probArray.push(x.relativeProbability); {
acc.keyArray.push(x.key); acc.probArray.push(x.relativeProbability);
return acc; acc.keyArray.push(x.key);
}, { probArray: [], keyArray: [] }); return acc;
},
{ probArray: [], keyArray: [] },
);
let probCumsum = this.cumulativeProbability(probArray); let probCumsum = this.cumulativeProbability(probArray);
const drawnKeys = []; const drawnKeys = [];
for (let i = 0; i < count; i++) for (let i = 0; i < count; i++)
{ {
const rand = Math.random(); const rand = Math.random();
const randomIndex = probCumsum.findIndex(x => x > rand); const randomIndex = probCumsum.findIndex((x) => x > rand);
// We cannot put Math.random() directly in the findIndex because then it draws anew for each of its iteration // We cannot put Math.random() directly in the findIndex because then it draws anew for each of its iteration
if (replacement || locklist.includes(keyArray[randomIndex])) if (replacement || locklist.includes(keyArray[randomIndex]))
{ {
@ -204,9 +211,11 @@ export class ProbabilityObject<K, V = undefined>
@injectable() @injectable()
export class RandomUtil export class RandomUtil
{ {
constructor(@inject("RecursiveCloner") protected cloner: ICloner, @inject("WinstonLogger") protected logger: ILogger) constructor(
{ @inject("RecursiveCloner") protected cloner: ICloner,
} @inject("WinstonLogger") protected logger: ILogger,
)
{}
public getInt(min: number, max: number): number public getInt(min: number, max: number): number
{ {
@ -232,7 +241,7 @@ export class RandomUtil
public getPercentOfValue(percent: number, number: number, toFixed = 2): number public getPercentOfValue(percent: number, number: number, toFixed = 2): number
{ {
return Number.parseFloat((percent * number / 100).toFixed(toFixed)); return Number.parseFloat(((percent * number) / 100).toFixed(toFixed));
} }
/** /**
@ -446,8 +455,7 @@ export class RandomUtil
do do
{ {
num = boundedGaussian(biasedMin, biasedMax, n); num = boundedGaussian(biasedMin, biasedMax, n);
} } while (num < min || num > max);
while (num < min || num > max);
return num; return num;
} }

View File

@ -112,8 +112,8 @@ export class TimeUtil
public getTimestampOfNextHour(): number public getTimestampOfNextHour(): number
{ {
const now = new Date(); const now = new Date();
const millisecondsUntilNextHour = (60 - now.getMinutes()) * 60 * 1000 - now.getSeconds() * 1000 const millisecondsUntilNextHour
- now.getMilliseconds(); = (60 - now.getMinutes()) * 60 * 1000 - now.getSeconds() * 1000 - now.getMilliseconds();
return (now.getTime() + millisecondsUntilNextHour) / 1000; return (now.getTime() + millisecondsUntilNextHour) / 1000;
} }
} }

View File

@ -121,7 +121,7 @@ export class VFS
const files = this.getFiles(filepath); const files = this.getFiles(filepath);
const dirs = this.getDirs(filepath); const dirs = this.getDirs(filepath);
if (!await this.existsAsync(target)) if (!(await this.existsAsync(target)))
{ {
await this.createDirAsync(`${target}/`); await this.createDirAsync(`${target}/`);
} }
@ -194,7 +194,7 @@ export class VFS
{ {
const options = append ? { flag: "a" } : { flag: "w" }; const options = append ? { flag: "a" } : { flag: "w" };
if (!await this.exists(filepath)) if (!(await this.exists(filepath)))
{ {
await this.createDir(filepath); await this.createDir(filepath);
await this.writeFilePromisify(filepath, ""); await this.writeFilePromisify(filepath, "");
@ -332,7 +332,7 @@ export class VFS
public async minifyAllJsonInDirRecursive(filepath: string): Promise<void> public async minifyAllJsonInDirRecursive(filepath: string): Promise<void>
{ {
const files = this.getFiles(filepath).filter(item => this.getFileExtension(item) === "json"); const files = this.getFiles(filepath).filter((item) => this.getFileExtension(item) === "json");
for (const file of files) for (const file of files)
{ {
const filePathAndName = path.join(filepath, file); const filePathAndName = path.join(filepath, file);
@ -349,7 +349,7 @@ export class VFS
public async minifyAllJsonInDirRecursiveAsync(filepath: string): Promise<void> public async minifyAllJsonInDirRecursiveAsync(filepath: string): Promise<void>
{ {
const files = this.getFiles(filepath).filter(item => this.getFileExtension(item) === "json"); const files = this.getFiles(filepath).filter((item) => this.getFileExtension(item) === "json");
for (const file of files) for (const file of files)
{ {
const filePathAndName = path.join(filepath, file); const filePathAndName = path.join(filepath, file);

View File

@ -154,10 +154,11 @@ export class Watermark
const result = []; const result = [];
// Calculate size, add 10% for spacing to the right // Calculate size, add 10% for spacing to the right
const longestLength = this.text.reduce((a, b) => const longestLength
{ = this.text.reduce((a, b) =>
return a.length > b.length ? a : b; {
}).length * 1.1; return a.length > b.length ? a : b;
}).length * 1.1;
// Create line of - to add top/bottom of watermark // Create line of - to add top/bottom of watermark
let line = ""; let line = "";

View File

@ -29,7 +29,7 @@ export class RecursiveCloner implements ICloner
{ {
// biome-ignore lint/suspicious/noExplicitAny: used for clone // biome-ignore lint/suspicious/noExplicitAny: used for clone
const objArr = obj as Array<any>; const objArr = obj as Array<any>;
return objArr.map(v => this.clone(v)) as T; return objArr.map((v) => this.clone(v)) as T;
} }
const newObj = {}; const newObj = {};

View File

@ -1,5 +1,9 @@
export class LinkedListNode<T> export class LinkedListNode<T>
{ {
constructor(public value: T, public prev?: LinkedListNode<T>, public next?: LinkedListNode<T>) constructor(
public value: T,
public prev?: LinkedListNode<T>,
public next?: LinkedListNode<T>,
)
{} {}
} }

View File

@ -133,7 +133,11 @@ export abstract class AbstractWinstonLogger implements ILogger
await this.asyncQueue.waitFor(command); await this.asyncQueue.waitFor(command);
} }
public async log(data: string | Error | Record<string, unknown>, color: string, backgroundColor = ""): Promise<void> public async log(
data: string | Error | Record<string, unknown>,
color: string,
backgroundColor = "",
): Promise<void>
{ {
const textColor = `${color} ${backgroundColor}`.trimEnd(); const textColor = `${color} ${backgroundColor}`.trimEnd();
const tmpLogger = createLogger({ const tmpLogger = createLogger({