Formatting for controller classes.

This commit is contained in:
Refringe 2023-11-10 16:49:29 -05:00
parent 5fa8803f8c
commit 87bb07cfd9
No known key found for this signature in database
GPG Key ID: 64E03E5F892C6F9E
28 changed files with 1870 additions and 914 deletions

View File

@ -21,12 +21,63 @@
"source.fixAll.eslint", "source.fixAll.eslint",
"source.organizeImports.biome" "source.organizeImports.biome"
], ],
"cSpell.language": "en-GB",
"cSpell.words": [ "cSpell.words": [
"armor",
"asonline",
"behaviour",
"biomejs",
"botreload",
"currexp",
"currlvl",
"dbaeumer",
"deathmatch", "deathmatch",
"dprint",
"edgeofdarkness",
"fulfill",
"gethideout", "gethideout",
"gifter",
"hpresource",
"inraid",
"isvalid",
"leftbehind",
"leveled",
"loadout",
"maxlvl",
"medkit",
"MEDSTATION",
"nextlvl",
"offraid",
"peacefullzryachiyevent",
"preparetoescape",
"prevexp",
"profileid", "profileid",
"Protobuf",
"pscav",
"Ragfair",
"Regen",
"requestid", "requestid",
"scavcase" "sanitise",
"Sanitised",
"scav",
"scavcase",
"scavs",
"Spawnpoint",
"spawnpoints",
"sptbear",
"sptdeveloper",
"spteasystart",
"sptusec",
"sptzerotohero",
"stackcount",
"statustimer",
"Tarkov",
"toggleable",
"tooshort",
"unrestartable",
"usec",
"userbuilds",
"Wishlist"
] ]
} }
} }

View File

@ -43,7 +43,7 @@ export class BotController
@inject("ProfileHelper") protected profileHelper: ProfileHelper, @inject("ProfileHelper") protected profileHelper: ProfileHelper,
@inject("ConfigServer") protected configServer: ConfigServer, @inject("ConfigServer") protected configServer: ConfigServer,
@inject("ApplicationContext") protected applicationContext: ApplicationContext, @inject("ApplicationContext") protected applicationContext: ApplicationContext,
@inject("JsonUtil") protected jsonUtil: JsonUtil @inject("JsonUtil") protected jsonUtil: JsonUtil,
) )
{ {
this.botConfig = this.configServer.getConfig(ConfigTypes.BOT); this.botConfig = this.configServer.getConfig(ConfigTypes.BOT);
@ -51,29 +51,29 @@ export class BotController
} }
/** /**
* Return the number of bot loadout varieties to be generated * Return the number of bot load-out varieties to be generated
* @param type bot Type we want the loadout gen count for * @param type bot Type we want the load-out gen count for
* @returns number of bots to generate * @returns number of bots to generate
*/ */
public getBotPresetGenerationLimit(type: string): number public getBotPresetGenerationLimit(type: string): number
{ {
const value = this.botConfig.presetBatch[(type === "assaultGroup") const value = this.botConfig.presetBatch[
? "assault" (type === "assaultGroup") ?
: type]; "assault" :
type
];
if (!value) if (!value)
{ {
this.logger.warning(`No value found for bot type ${type}, defaulting to 30`); this.logger.warning(`No value found for bot type ${type}, defaulting to 30`);
return value; return value;
} }
return value; return value;
} }
/** /**
* Handle singleplayer/settings/bot/difficulty * Handle singleplayer/settings/bot/difficulty
* Get the core.json difficulty settings from database\bots * Get the core.json difficulty settings from database/bots
* @returns IBotCore * @returns IBotCore
*/ */
public getBotCoreDifficulty(): IBotCore public getBotCoreDifficulty(): IBotCore
@ -90,10 +90,14 @@ export class BotController
*/ */
public getBotDifficulty(type: string, difficulty: string): Difficulty public getBotDifficulty(type: string, difficulty: string): Difficulty
{ {
const raidConfig = this.applicationContext.getLatestValue(ContextVariableType.RAID_CONFIGURATION)?.getValue<IGetRaidConfigurationRequestData>(); const raidConfig = this.applicationContext.getLatestValue(ContextVariableType.RAID_CONFIGURATION)?.getValue<
IGetRaidConfigurationRequestData
>();
if (!raidConfig) if (!raidConfig)
{ {
this.logger.error(this.localisationService.getText("bot-missing_application_context", "RAID_CONFIGURATION")); this.logger.error(
this.localisationService.getText("bot-missing_application_context", "RAID_CONFIGURATION"),
);
} }
// Check value chosen in pre-raid difficulty dropdown // Check value chosen in pre-raid difficulty dropdown
@ -101,7 +105,9 @@ export class BotController
const botDifficultyDropDownValue = raidConfig.wavesSettings.botDifficulty.toLowerCase(); const botDifficultyDropDownValue = raidConfig.wavesSettings.botDifficulty.toLowerCase();
if (botDifficultyDropDownValue !== "asonline") if (botDifficultyDropDownValue !== "asonline")
{ {
difficulty = this.botDifficultyHelper.convertBotDifficultyDropdownToBotDifficulty(botDifficultyDropDownValue); difficulty = this.botDifficultyHelper.convertBotDifficultyDropdownToBotDifficulty(
botDifficultyDropDownValue,
);
} }
let difficultySettings: Difficulty; let difficultySettings: Difficulty;
@ -109,19 +115,31 @@ export class BotController
switch (lowercasedBotType) switch (lowercasedBotType)
{ {
case this.pmcConfig.bearType.toLowerCase(): case this.pmcConfig.bearType.toLowerCase():
difficultySettings = this.botDifficultyHelper.getPmcDifficultySettings("bear", difficulty, this.pmcConfig.usecType, this.pmcConfig.bearType); difficultySettings = this.botDifficultyHelper.getPmcDifficultySettings(
"bear",
difficulty,
this.pmcConfig.usecType,
this.pmcConfig.bearType,
);
break; break;
case this.pmcConfig.usecType.toLowerCase(): case this.pmcConfig.usecType.toLowerCase():
difficultySettings = this.botDifficultyHelper.getPmcDifficultySettings("usec", difficulty, this.pmcConfig.usecType, this.pmcConfig.bearType); difficultySettings = this.botDifficultyHelper.getPmcDifficultySettings(
"usec",
difficulty,
this.pmcConfig.usecType,
this.pmcConfig.bearType,
);
break; break;
default: default:
difficultySettings = this.botDifficultyHelper.getBotDifficultySettings(type, difficulty); difficultySettings = this.botDifficultyHelper.getBotDifficultySettings(type, difficulty);
// Don't add pmcs to event enemies (e.g. gifter/peacefullzryachiyevent) // Don't add PMCs to event enemies (e.g. gifter/peacefullzryachiyevent)
if (!this.botConfig.botsToNotAddPMCsAsEnemiesTo.includes(type.toLowerCase())) if (!this.botConfig.botsToNotAddPMCsAsEnemiesTo.includes(type.toLowerCase()))
{ {
this.botHelper.addBotToEnemyList(difficultySettings, [this.pmcConfig.bearType, this.pmcConfig.usecType], lowercasedBotType); this.botHelper.addBotToEnemyList(difficultySettings, [
this.pmcConfig.bearType,
this.pmcConfig.usecType,
], lowercasedBotType);
} }
break; break;
} }
@ -149,7 +167,7 @@ export class BotController
botRelativeLevelDeltaMax: this.pmcConfig.botRelativeLevelDeltaMax, botRelativeLevelDeltaMax: this.pmcConfig.botRelativeLevelDeltaMax,
botCountToGenerate: this.botConfig.presetBatch[condition.Role], botCountToGenerate: this.botConfig.presetBatch[condition.Role],
botDifficulty: condition.Difficulty, botDifficulty: condition.Difficulty,
isPlayerScav: false isPlayerScav: 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
@ -158,7 +176,9 @@ export class BotController
{ {
// Add eventRole data + reassign role property to be base type // Add eventRole data + reassign role property to be base type
botGenerationDetails.eventRole = condition.Role; botGenerationDetails.eventRole = condition.Role;
botGenerationDetails.role = this.seasonalEventService.getBaseRoleForEventBot(botGenerationDetails.eventRole); botGenerationDetails.role = this.seasonalEventService.getBaseRoleForEventBot(
botGenerationDetails.eventRole,
);
} }
// Custom map waves can have spt roles in them // Custom map waves can have spt roles in them
@ -171,10 +191,10 @@ export class BotController
// Loop over and make x bots for this condition // Loop over and make x bots for this condition
let cacheKey = ""; let cacheKey = "";
for (let i = 0; i < botGenerationDetails.botCountToGenerate; i ++) for (let i = 0; i < botGenerationDetails.botCountToGenerate; i++)
{ {
const details = this.jsonUtil.clone(botGenerationDetails); const details = this.jsonUtil.clone(botGenerationDetails);
const botRole = (isEventBot) ? details.eventRole : details.role; const botRole = isEventBot ? details.eventRole : details.role;
// Roll chance to be pmc if type is allowed to be one // Roll chance to be pmc if type is allowed to be one
const botConvertRateMinMax = this.pmcConfig.convertIntoPmcChance[botRole.toLowerCase()]; const botConvertRateMinMax = this.pmcConfig.convertIntoPmcChance[botRole.toLowerCase()];
@ -192,6 +212,7 @@ export class BotController
} }
cacheKey = `${botRole}${details.botDifficulty}`; cacheKey = `${botRole}${details.botDifficulty}`;
// Check for bot in cache, add if not // Check for bot in cache, add if not
if (!this.botGenerationCacheService.cacheHasBotOfRole(cacheKey)) if (!this.botGenerationCacheService.cacheHasBotOfRole(cacheKey))
{ {
@ -200,6 +221,7 @@ export class BotController
this.botGenerationCacheService.storeBots(cacheKey, botsToAddToCache); this.botGenerationCacheService.storeBots(cacheKey, botsToAddToCache);
} }
} }
// Get bot from cache, add to return array // Get bot from cache, add to return array
const botToReturn = this.botGenerationCacheService.getBot(cacheKey); const botToReturn = this.botGenerationCacheService.getBot(cacheKey);
@ -217,13 +239,13 @@ export class BotController
} }
/** /**
* Get the difficulty passed in, if its not "asoline", get selected difficulty from config * Get the difficulty passed in, if its not "asonline", get selected difficulty from config
* @param requestedDifficulty * @param requestedDifficulty
* @returns * @returns
*/ */
public getPMCDifficulty(requestedDifficulty: string): string public getPMCDifficulty(requestedDifficulty: string): string
{ {
// maybe retrun a random difficulty... // Maybe return a random difficulty...
if (this.pmcConfig.difficulty.toLowerCase() === "asonline") if (this.pmcConfig.difficulty.toLowerCase() === "asonline")
{ {
return requestedDifficulty; return requestedDifficulty;
@ -245,20 +267,27 @@ export class BotController
public getBotCap(): number public getBotCap(): number
{ {
const defaultMapCapId = "default"; const defaultMapCapId = "default";
const raidConfig = this.applicationContext.getLatestValue(ContextVariableType.RAID_CONFIGURATION).getValue<IGetRaidConfigurationRequestData>(); const raidConfig = this.applicationContext.getLatestValue(ContextVariableType.RAID_CONFIGURATION).getValue<
IGetRaidConfigurationRequestData
>();
if (!raidConfig) if (!raidConfig)
{ {
this.logger.warning(this.localisationService.getText("bot-missing_saved_match_info")); this.logger.warning(this.localisationService.getText("bot-missing_saved_match_info"));
} }
const mapName = (raidConfig) const mapName = raidConfig ?
? raidConfig.location raidConfig.location :
: defaultMapCapId; defaultMapCapId;
let botCap = this.botConfig.maxBotCap[mapName.toLowerCase()]; let botCap = this.botConfig.maxBotCap[mapName.toLowerCase()];
if (!botCap) if (!botCap)
{ {
this.logger.warning(this.localisationService.getText("bot-no_bot_cap_found_for_location", raidConfig.location.toLowerCase())); this.logger.warning(
this.localisationService.getText(
"bot-no_bot_cap_found_for_location",
raidConfig.location.toLowerCase(),
),
);
botCap = this.botConfig.maxBotCap[defaultMapCapId]; botCap = this.botConfig.maxBotCap[defaultMapCapId];
} }
@ -269,7 +298,7 @@ export class BotController
{ {
return { return {
pmc: this.pmcConfig.pmcType, pmc: this.pmcConfig.pmcType,
assault: this.botConfig.assaultBrainType assault: this.botConfig.assaultBrainType,
}; };
} }
} }

View File

@ -9,9 +9,9 @@ import { inject, injectable } from "tsyringe";
export class ClientLogController export class ClientLogController
{ {
constructor( constructor(
@inject("WinstonLogger") protected logger: ILogger @inject("WinstonLogger") protected logger: ILogger,
) )
{ } {}
/** /**
* Handle /singleplayer/log * Handle /singleplayer/log

View File

@ -17,7 +17,7 @@ export class CustomizationController
{ {
protected readonly clothingIds = { protected readonly clothingIds = {
lowerParentId: "5cd944d01388ce000a659df9", lowerParentId: "5cd944d01388ce000a659df9",
upperParentId: "5cd944ca1388ce03a44dc2a4" upperParentId: "5cd944ca1388ce03a44dc2a4",
}; };
constructor( constructor(
@ -26,7 +26,7 @@ export class CustomizationController
@inject("DatabaseServer") protected databaseServer: DatabaseServer, @inject("DatabaseServer") protected databaseServer: DatabaseServer,
@inject("SaveServer") protected saveServer: SaveServer, @inject("SaveServer") protected saveServer: SaveServer,
@inject("LocalisationService") protected localisationService: LocalisationService, @inject("LocalisationService") protected localisationService: LocalisationService,
@inject("ProfileHelper") protected profileHelper: ProfileHelper @inject("ProfileHelper") protected profileHelper: ProfileHelper,
) )
{} {}
@ -42,18 +42,22 @@ export class CustomizationController
const templates = this.databaseServer.getTables().templates.customization; const templates = this.databaseServer.getTables().templates.customization;
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 ragmans 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));
} }
/** /**
* Handle CustomizationWear event * Handle CustomizationWear event
* Equip one to many clothing items to player * Equip one to many clothing items to player
*/ */
public wearClothing(pmcData: IPmcData, wearClothingRequest: IWearClothingRequestData, sessionID: string): IItemEventRouterResponse public wearClothing(
pmcData: IPmcData,
wearClothingRequest: IWearClothingRequestData,
sessionID: string,
): IItemEventRouterResponse
{ {
for (const suitId of wearClothingRequest.suites) for (const suitId of wearClothingRequest.suites)
{ {
@ -85,7 +89,11 @@ export class CustomizationController
* @param sessionId Session id * @param sessionId Session id
* @returns IItemEventRouterResponse * @returns IItemEventRouterResponse
*/ */
public buyClothing(pmcData: IPmcData, buyClothingRequest: IBuyClothingRequestData, sessionId: string): IItemEventRouterResponse public buyClothing(
pmcData: IPmcData,
buyClothingRequest: IBuyClothingRequestData,
sessionId: string,
): IItemEventRouterResponse
{ {
const db = this.databaseServer.getTables(); const db = this.databaseServer.getTables();
const output = this.eventOutputHolder.getOutput(sessionId); const output = this.eventOutputHolder.getOutput(sessionId);
@ -93,7 +101,9 @@ export class CustomizationController
const traderOffer = this.getTraderClothingOffer(sessionId, buyClothingRequest.offer); const traderOffer = this.getTraderClothingOffer(sessionId, buyClothingRequest.offer);
if (!traderOffer) if (!traderOffer)
{ {
this.logger.error(this.localisationService.getText("customisation-unable_to_find_suit_by_id", buyClothingRequest.offer)); this.logger.error(
this.localisationService.getText("customisation-unable_to_find_suit_by_id", buyClothingRequest.offer),
);
return output; return output;
} }
@ -102,7 +112,12 @@ export class CustomizationController
if (this.outfitAlreadyPurchased(suitId, sessionId)) if (this.outfitAlreadyPurchased(suitId, sessionId))
{ {
const suitDetails = db.templates.customization[suitId]; const suitDetails = db.templates.customization[suitId];
this.logger.error(this.localisationService.getText("customisation-item_already_purchased", {itemId: suitDetails._id, itemName: suitDetails._name})); this.logger.error(
this.localisationService.getText("customisation-item_already_purchased", {
itemId: suitDetails._id,
itemName: suitDetails._name,
}),
);
return output; return output;
} }
@ -118,7 +133,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);
} }
/** /**
@ -139,7 +154,12 @@ export class CustomizationController
* @param clothingItems Clothing purchased * @param clothingItems Clothing purchased
* @param output Client response * @param output Client response
*/ */
protected payForClothingItems(sessionId: string, pmcData: IPmcData, clothingItems: ClothingItem[], output: IItemEventRouterResponse): void protected payForClothingItems(
sessionId: string,
pmcData: IPmcData,
clothingItems: ClothingItem[],
output: IItemEventRouterResponse,
): void
{ {
for (const sellItem of clothingItems) for (const sellItem of clothingItems)
{ {
@ -154,12 +174,22 @@ export class CustomizationController
* @param clothingItem Clothing item purchased * @param clothingItem Clothing item purchased
* @param output Client response * @param output Client response
*/ */
protected payForClothingItem(sessionId: string, pmcData: IPmcData, clothingItem: ClothingItem, output: IItemEventRouterResponse): void protected payForClothingItem(
sessionId: string,
pmcData: IPmcData,
clothingItem: ClothingItem,
output: IItemEventRouterResponse,
): 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.localisationService.getText("customisation-unable_to_find_clothing_item_in_inventory", clothingItem.id)); this.logger.error(
this.localisationService.getText(
"customisation-unable_to_find_clothing_item_in_inventory",
clothingItem.id,
),
);
return; return;
} }
@ -179,7 +209,7 @@ export class CustomizationController
parentId: relatedItem.parentId, parentId: relatedItem.parentId,
slotId: relatedItem.slotId, slotId: relatedItem.slotId,
location: relatedItem.location, location: relatedItem.location,
upd: { StackObjectsCount: relatedItem.upd.StackObjectsCount } upd: {StackObjectsCount: relatedItem.upd.StackObjectsCount},
}); });
} }
} }

View File

@ -37,7 +37,7 @@ export class DialogueController
@inject("MailSendService") protected mailSendService: MailSendService, @inject("MailSendService") protected mailSendService: MailSendService,
@inject("GiftService") protected giftService: GiftService, @inject("GiftService") protected giftService: GiftService,
@inject("HashUtil") protected hashUtil: HashUtil, @inject("HashUtil") protected hashUtil: HashUtil,
@inject("ConfigServer") protected configServer: ConfigServer @inject("ConfigServer") protected configServer: ConfigServer,
) )
{ {
this.coreConfig = this.configServer.getConfig(ConfigTypes.CORE); this.coreConfig = this.configServer.getConfig(ConfigTypes.CORE);
@ -57,14 +57,13 @@ export class DialogueController
* Handle client/friend/list * Handle client/friend/list
* @returns IGetFriendListDataResponse * @returns IGetFriendListDataResponse
*/ */
// eslint-disable-next-line @typescript-eslint/no-unused-vars
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 { return {
Friends: [this.getSptFriendData()], Friends: [this.getSptFriendData()],
Ignore: [], Ignore: [],
InIgnoreList: [] InIgnoreList: [],
}; };
} }
@ -104,7 +103,7 @@ export class DialogueController
new: dialogue.new, new: dialogue.new,
attachmentsNew: dialogue.attachmentsNew, attachmentsNew: dialogue.attachmentsNew,
pinned: dialogue.pinned, pinned: dialogue.pinned,
Users: this.getDialogueUsers(dialogue, dialogue.type, sessionID) Users: this.getDialogueUsers(dialogue, dialogue.type, sessionID),
}; };
return result; return result;
@ -121,7 +120,9 @@ export class DialogueController
const profile = this.saveServer.getProfile(sessionID); const profile = this.saveServer.getProfile(sessionID);
// 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 (messageType === MessageType.USER_MESSAGE && !dialog.Users?.find(x => x._id === profile.characters.pmc._id)) if (
messageType === MessageType.USER_MESSAGE && !dialog.Users?.find((x) => x._id === profile.characters.pmc._id)
)
{ {
if (!dialog.Users) if (!dialog.Users)
{ {
@ -134,8 +135,8 @@ export class DialogueController
Level: profile.characters.pmc.Info.Level, Level: profile.characters.pmc.Info.Level,
Nickname: profile.characters.pmc.Info.Nickname, Nickname: profile.characters.pmc.Info.Nickname,
Side: profile.characters.pmc.Info.Side, Side: profile.characters.pmc.Info.Side,
MemberCategory: profile.characters.pmc.Info.MemberCategory MemberCategory: profile.characters.pmc.Info.MemberCategory,
} },
}); });
} }
@ -144,14 +145,17 @@ export class DialogueController
/** /**
* Handle client/mail/dialog/view * Handle client/mail/dialog/view
* Handle player clicking 'messenger' and seeing all the messages they've recieved * Handle player clicking 'messenger' and seeing all the messages they've received
* Set the content of the dialogue on the details panel, showing all the messages * Set the content of the dialogue on the details panel, showing all the messages
* for the specified dialogue. * for the specified dialogue.
* @param request Get dialog request * @param request Get dialog request
* @param sessionId Session id * @param sessionId Session id
* @returns IGetMailDialogViewResponseData object * @returns IGetMailDialogViewResponseData object
*/ */
public generateDialogueView(request: IGetMailDialogViewRequestData, sessionId: string): IGetMailDialogViewResponseData public generateDialogueView(
request: IGetMailDialogViewRequestData,
sessionId: string,
): IGetMailDialogViewResponseData
{ {
const dialogueId = request.dialogId; const dialogueId = request.dialogId;
const fullProfile = this.saveServer.getProfile(sessionId); const fullProfile = this.saveServer.getProfile(sessionId);
@ -166,14 +170,14 @@ export class DialogueController
return { return {
messages: dialogue.messages, messages: dialogue.messages,
profiles: this.getProfilesForMail(fullProfile, dialogue.Users), profiles: this.getProfilesForMail(fullProfile, dialogue.Users),
hasMessagesWithRewards: this.messagesHaveUncollectedRewards(dialogue.messages) hasMessagesWithRewards: this.messagesHaveUncollectedRewards(dialogue.messages),
}; };
} }
/** /**
* Get dialog from player profile, create if doesn't exist * Get dialog from player profile, create if doesn't exist
* @param profile Player profile * @param profile Player profile
* @param request get dialog request (params used when dialog doesnt exist in profile) * @param request get dialog request (params used when dialog doesn't exist in profile)
* @returns Dialogue * @returns Dialogue
*/ */
protected getDialogByIdFromProfile(profile: IAkiProfile, request: IGetMailDialogViewRequestData): Dialogue protected getDialogByIdFromProfile(profile: IAkiProfile, request: IGetMailDialogViewRequestData): Dialogue
@ -186,7 +190,7 @@ export class DialogueController
pinned: false, pinned: false,
messages: [], messages: [],
new: 0, new: 0,
type: request.type type: request.type,
}; };
if (request.type === MessageType.USER_MESSAGE) if (request.type === MessageType.USER_MESSAGE)
@ -211,8 +215,8 @@ export class DialogueController
{ {
result.push(...dialogUsers); result.push(...dialogUsers);
// Player doesnt exist, add them in before returning // Player doesn't exist, add them in before returning
if (!result.find(x => x._id === fullProfile.info.id)) if (!result.find((x) => x._id === fullProfile.info.id))
{ {
const pmcProfile = fullProfile.characters.pmc; const pmcProfile = fullProfile.characters.pmc;
result.push({ result.push({
@ -221,8 +225,8 @@ export class DialogueController
Nickname: pmcProfile.Info.Nickname, Nickname: pmcProfile.Info.Nickname,
Side: pmcProfile.Info.Side, Side: pmcProfile.Info.Side,
Level: pmcProfile.Info.Level, Level: pmcProfile.Info.Level,
MemberCategory: pmcProfile.Info.MemberCategory MemberCategory: pmcProfile.Info.MemberCategory,
} },
}); });
} }
} }
@ -258,7 +262,7 @@ export class DialogueController
*/ */
protected messagesHaveUncollectedRewards(messages: Message[]): boolean protected messagesHaveUncollectedRewards(messages: Message[]): boolean
{ {
return messages.some(x => x.items?.data?.length > 0); return messages.some((x) => x.items?.data?.length > 0);
} }
/** /**
@ -274,7 +278,6 @@ export class DialogueController
if (!dialog) if (!dialog)
{ {
this.logger.error(`No dialog in profile: ${sessionId} found with id: ${dialogueId}`); this.logger.error(`No dialog in profile: ${sessionId} found with id: ${dialogueId}`);
return; return;
} }
@ -288,7 +291,6 @@ export class DialogueController
if (!dialog) if (!dialog)
{ {
this.logger.error(`No dialog in profile: ${sessionId} found with id: ${dialogueId}`); this.logger.error(`No dialog in profile: ${sessionId} found with id: ${dialogueId}`);
return; return;
} }
@ -307,7 +309,6 @@ export class DialogueController
if (!dialogs) if (!dialogs)
{ {
this.logger.error(`No dialog object in profile: ${sessionId}`); this.logger.error(`No dialog object in profile: ${sessionId}`);
return; return;
} }
@ -332,7 +333,6 @@ export class DialogueController
if (!dialog) if (!dialog)
{ {
this.logger.error(`No dialog in profile: ${sessionId} found with id: ${dialogueId}`); this.logger.error(`No dialog in profile: ${sessionId} found with id: ${dialogueId}`);
return; return;
} }
@ -345,12 +345,11 @@ export class DialogueController
return { return {
messages: messagesWithAttachments, messages: messagesWithAttachments,
profiles: [], profiles: [],
hasMessagesWithRewards: this.messagesHaveUncollectedRewards(messagesWithAttachments) hasMessagesWithRewards: this.messagesHaveUncollectedRewards(messagesWithAttachments),
}; };
} }
/** client/mail/msg/send */ /** client/mail/msg/send */
// eslint-disable-next-line @typescript-eslint/no-unused-vars
public sendMessage(sessionId: string, request: ISendMessageRequest): string public sendMessage(sessionId: string, request: ISendMessageRequest): string
{ {
this.mailSendService.sendPlayerMessageToNpc(sessionId, request.dialogId, request.text); this.mailSendService.sendPlayerMessageToNpc(sessionId, request.dialogId, request.text);
@ -372,48 +371,97 @@ export class DialogueController
protected handleChatWithSPTFriend(sessionId: string, request: ISendMessageRequest): void protected handleChatWithSPTFriend(sessionId: string, request: ISendMessageRequest): void
{ {
const sender = this.profileHelper.getPmcProfile(sessionId); const sender = this.profileHelper.getPmcProfile(sessionId);
const sptFriendUser = this.getSptFriendData(); const sptFriendUser = this.getSptFriendData();
const giftSent = this.giftService.sendGiftToPlayer(sessionId, request.text); const giftSent = this.giftService.sendGiftToPlayer(sessionId, request.text);
if (giftSent === GiftSentResult.SUCCESS) if (giftSent === GiftSentResult.SUCCESS)
{ {
this.mailSendService.sendUserMessageToPlayer(sessionId, sptFriendUser, this.randomUtil.getArrayValue(["Hey! you got the right code!", "A secret code, how exciting!", "You found a gift code!"])); this.mailSendService.sendUserMessageToPlayer(
sessionId,
sptFriendUser,
this.randomUtil.getArrayValue([
"Hey! you got the right code!",
"A secret code, how exciting!",
"You found a gift code!",
]),
);
return; return;
} }
if (giftSent === GiftSentResult.FAILED_GIFT_ALREADY_RECEIVED) if (giftSent === GiftSentResult.FAILED_GIFT_ALREADY_RECEIVED)
{ {
this.mailSendService.sendUserMessageToPlayer(sessionId, sptFriendUser, this.randomUtil.getArrayValue(["Looks like you already used that code", "You already have that!!"])); this.mailSendService.sendUserMessageToPlayer(
sessionId,
sptFriendUser,
this.randomUtil.getArrayValue(["Looks like you already used that code", "You already have that!!"]),
);
return; return;
} }
if (request.text.toLowerCase().includes("love you")) if (request.text.toLowerCase().includes("love you"))
{ {
this.mailSendService.sendUserMessageToPlayer(sessionId, sptFriendUser, this.randomUtil.getArrayValue(["That's quite forward but i love you too in a purely chatbot-human way", "I love you too buddy :3!", "uwu", `love you too ${sender?.Info?.Nickname}`])); this.mailSendService.sendUserMessageToPlayer(
sessionId,
sptFriendUser,
this.randomUtil.getArrayValue([
"That's quite forward but i love you too in a purely chatbot-human way",
"I love you too buddy :3!",
"uwu",
`love you too ${sender?.Info?.Nickname}`,
]),
);
} }
if (request.text.toLowerCase() === "spt") if (request.text.toLowerCase() === "spt")
{ {
this.mailSendService.sendUserMessageToPlayer(sessionId, sptFriendUser, this.randomUtil.getArrayValue(["Its me!!", "spt? i've heard of that project"])); this.mailSendService.sendUserMessageToPlayer(
sessionId,
sptFriendUser,
this.randomUtil.getArrayValue(["Its me!!", "spt? i've heard of that project"]),
);
} }
if (["hello", "hi", "sup", "yo", "hey"].includes(request.text.toLowerCase())) if (["hello", "hi", "sup", "yo", "hey"].includes(request.text.toLowerCase()))
{ {
this.mailSendService.sendUserMessageToPlayer(sessionId, sptFriendUser, this.randomUtil.getArrayValue(["Howdy", "Hi", "Greetings", "Hello", "bonjor", "Yo", "Sup", "Heyyyyy", "Hey there", `Hello ${sender?.Info?.Nickname}`])); this.mailSendService.sendUserMessageToPlayer(
sessionId,
sptFriendUser,
this.randomUtil.getArrayValue([
"Howdy",
"Hi",
"Greetings",
"Hello",
"Bonjour",
"Yo",
"Sup",
"Heyyyyy",
"Hey there",
`Hello ${sender?.Info?.Nickname}`,
]),
);
} }
if (request.text.toLowerCase() === "nikita") if (request.text.toLowerCase() === "nikita")
{ {
this.mailSendService.sendUserMessageToPlayer(sessionId, sptFriendUser, this.randomUtil.getArrayValue(["I know that guy!", "Cool guy, he made EFT!", "Legend", "Remember when he said webel-webel-webel-webel, classic nikita moment"])); this.mailSendService.sendUserMessageToPlayer(
sessionId,
sptFriendUser,
this.randomUtil.getArrayValue([
"I know that guy!",
"Cool guy, he made EFT!",
"Legend",
"Remember when he said webel-webel-webel-webel, classic nikita moment",
]),
);
} }
if (request.text.toLowerCase() === "are you a bot") if (request.text.toLowerCase() === "are you a bot")
{ {
this.mailSendService.sendUserMessageToPlayer(sessionId, sptFriendUser, this.randomUtil.getArrayValue(["beep boop", "**sad boop**", "probably", "sometimes", "yeah lol"])); this.mailSendService.sendUserMessageToPlayer(
sessionId,
sptFriendUser,
this.randomUtil.getArrayValue(["beep boop", "**sad boop**", "probably", "sometimes", "yeah lol"]),
);
} }
} }
@ -425,8 +473,8 @@ export class DialogueController
Level: 1, Level: 1,
MemberCategory: MemberCategory.DEVELOPER, MemberCategory: MemberCategory.DEVELOPER,
Nickname: this.coreConfig.sptFriendNickname, Nickname: this.coreConfig.sptFriendNickname,
Side: "Usec" Side: "Usec",
} },
}; };
} }
@ -440,7 +488,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(x => timeNow < (x.dt + x.maxStorageTime)); return dialogs[dialogueId].messages.filter((x) => timeNow < (x.dt + x.maxStorageTime));
} }
/** /**
@ -450,7 +498,7 @@ export class DialogueController
*/ */
protected getMessagesWithAttachments(messages: Message[]): Message[] protected getMessagesWithAttachments(messages: Message[]): Message[]
{ {
return messages.filter(x => x.items?.data?.length > 0); return messages.filter((x) => x.items?.data?.length > 0);
} }
/** /**

View File

@ -69,7 +69,7 @@ export class GameController
@inject("ItemBaseClassService") protected itemBaseClassService: ItemBaseClassService, @inject("ItemBaseClassService") protected itemBaseClassService: ItemBaseClassService,
@inject("GiftService") protected giftService: GiftService, @inject("GiftService") protected giftService: GiftService,
@inject("ApplicationContext") protected applicationContext: ApplicationContext, @inject("ApplicationContext") protected applicationContext: ApplicationContext,
@inject("ConfigServer") protected configServer: ConfigServer @inject("ConfigServer") protected configServer: ConfigServer,
) )
{ {
this.httpConfig = this.configServer.getConfig(ConfigTypes.HTTP); this.httpConfig = this.configServer.getConfig(ConfigTypes.HTTP);
@ -82,10 +82,10 @@ export class GameController
public load(): void public load(): void
{ {
// Regenerate basecache now mods are loaded and game is starting // Regenerate base cache now mods are loaded and game is starting
// Mods that add items and use the baseclass service generate the cache including their items, the next mod that add items gets left out,causing warnings // Mods that add items and use the baseClass service generate the cache including their items, the next mod that
// add items gets left out,causing warnings
this.itemBaseClassService.hydrateItemBaseClassCache(); this.itemBaseClassService.hydrateItemBaseClassCache();
this.addCustomLooseLootPositions(); this.addCustomLooseLootPositions();
} }
@ -121,9 +121,9 @@ export class GameController
this.checkTraderRepairValuesExist(); this.checkTraderRepairValuesExist();
// repeatableQuests are stored by in profile.Quests due to the responses of the client (e.g. Quests in offraidData) // repeatableQuests are stored by in profile.Quests due to the responses of the client (e.g. Quests in
// Since we don't want to clutter the Quests list, we need to remove all completed (failed / successful) repeatable quests. // offraidData). Since we don't want to clutter the Quests list, we need to remove all completed (failed or
// We also have to remove the Counters from the repeatableQuests // successful) repeatable quests. We also have to remove the Counters from the repeatableQuests
if (sessionID) if (sessionID)
{ {
const fullProfile = this.profileHelper.getFullProfile(sessionID); const fullProfile = this.profileHelper.getFullProfile(sessionID);
@ -240,7 +240,9 @@ export class GameController
const trader = this.databaseServer.getTables().traders[traderKey]; const trader = this.databaseServer.getTables().traders[traderKey];
if (!trader?.base?.repair) if (!trader?.base?.repair)
{ {
this.logger.warning(`Trader ${trader.base._id} ${trader.base.name} is missing a repair object, adding in default values`); this.logger.warning(
`Trader ${trader.base._id} ${trader.base.name} is missing a repair object, adding in default values`,
);
trader.base.repair = this.jsonUtil.clone(this.databaseServer.getTables().traders.ragfair.base.repair); trader.base.repair = this.jsonUtil.clone(this.databaseServer.getTables().traders.ragfair.base.repair);
return; return;
@ -248,8 +250,12 @@ export class GameController
if (trader?.base?.repair?.quality) if (trader?.base?.repair?.quality)
{ {
this.logger.warning(`Trader ${trader.base._id} ${trader.base.name} is missing a repair quality value, adding in default value`); this.logger.warning(
trader.base.repair.quality = this.jsonUtil.clone(this.databaseServer.getTables().traders.ragfair.base.repair.quality); `Trader ${trader.base._id} ${trader.base.name} is missing a repair quality value, adding in default value`,
);
trader.base.repair.quality = this.jsonUtil.clone(
this.databaseServer.getTables().traders.ragfair.base.repair.quality,
);
} }
} }
} }
@ -274,7 +280,9 @@ 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 => x.template.Id === positionToAdd.template.Id); const existingLootPosition = mapLooseLoot.spawnpoints.find((x) =>
x.template.Id === positionToAdd.template.Id
);
if (existingLootPosition) if (existingLootPosition)
{ {
existingLootPosition.template.Items.push(...positionToAdd.template.Items); existingLootPosition.template.Items.push(...positionToAdd.template.Items);
@ -283,7 +291,7 @@ export class GameController
continue; continue;
} }
// new postion, add entire object // New position, add entire object
mapLooseLoot.spawnpoints.push(positionToAdd); mapLooseLoot.spawnpoints.push(positionToAdd);
} }
} }
@ -303,7 +311,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}`);
@ -363,12 +371,14 @@ export class GameController
const map: ILocationData = mapsDb[mapId]; const map: ILocationData = mapsDb[mapId];
if (!map) if (!map)
{ {
this.logger.warning(this.localisationService.getText("bot-unable_to_edit_limits_of_unknown_map", mapId)); this.logger.warning(
this.localisationService.getText("bot-unable_to_edit_limits_of_unknown_map", mapId),
);
} }
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
@ -378,14 +388,12 @@ export class GameController
} }
else else
{ {
map.base.MinMaxBots.push( // Bot type not found, add new object
{ map.base.MinMaxBots.push({
// Bot type not found, add new object WildSpawnType: botToLimit.type,
WildSpawnType: botToLimit.type, min: botToLimit.min,
min: botToLimit.min, max: botToLimit.max,
max: botToLimit.max });
}
);
} }
} }
} }
@ -412,12 +420,11 @@ export class GameController
Trading: this.httpServerHelper.getBackendUrl(), Trading: this.httpServerHelper.getBackendUrl(),
Messaging: this.httpServerHelper.getBackendUrl(), Messaging: this.httpServerHelper.getBackendUrl(),
Main: this.httpServerHelper.getBackendUrl(), Main: this.httpServerHelper.getBackendUrl(),
RagFair: this.httpServerHelper.getBackendUrl() RagFair: this.httpServerHelper.getBackendUrl(),
}, },
useProtobuf: false, useProtobuf: false,
// eslint-disable-next-line @typescript-eslint/naming-convention
utc_time: new Date().getTime() / 1000, utc_time: new Date().getTime() / 1000,
totalInGame: profile.Stats?.Eft?.TotalInGameTime ?? 0 totalInGame: profile.Stats?.Eft?.TotalInGameTime ?? 0,
}; };
return config; return config;
@ -426,50 +433,43 @@ export class GameController
/** /**
* Handle client/server/list * Handle client/server/list
*/ */
// eslint-disable-next-line @typescript-eslint/no-unused-vars
public getServer(sessionId: string): IServerDetails[] public getServer(sessionId: string): IServerDetails[]
{ {
return [ return [{
{ ip: this.httpConfig.ip,
ip: this.httpConfig.ip, port: this.httpConfig.port,
port: this.httpConfig.port }];
}
];
} }
/** /**
* Handle client/match/group/current * Handle client/match/group/current
*/ */
// eslint-disable-next-line @typescript-eslint/no-unused-vars
public getCurrentGroup(sessionId: string): ICurrentGroupResponse public getCurrentGroup(sessionId: string): ICurrentGroupResponse
{ {
return { return {
squad: [] squad: [],
}; };
} }
/** /**
* Handle client/checkVersion * Handle client/checkVersion
*/ */
// eslint-disable-next-line @typescript-eslint/no-unused-vars
public getValidGameVersion(sessionId: string): ICheckVersionResponse public getValidGameVersion(sessionId: string): ICheckVersionResponse
{ {
return { return {
isvalid: true, isvalid: true,
latestVersion: this.coreConfig.compatibleTarkovVersion latestVersion: this.coreConfig.compatibleTarkovVersion,
}; };
} }
/** /**
* Handle client/game/keepalive * Handle client/game/keepalive
*/ */
// eslint-disable-next-line @typescript-eslint/no-unused-vars
public getKeepAlive(sessionId: string): IGameKeepAliveResponse public getKeepAlive(sessionId: string): IGameKeepAliveResponse
{ {
return { return {
msg: "OK", msg: "OK",
// eslint-disable-next-line @typescript-eslint/naming-convention utc_time: new Date().getTime() / 1000,
utc_time: new Date().getTime() / 1000
}; };
} }
@ -520,7 +520,7 @@ export class GameController
/** /**
* When player logs in, iterate over all active effects and reduce timer * When player logs in, iterate over all active effects and reduce timer
* TODO - add body part HP regen * // TODO: Add body part HP regeneration
* @param pmcProfile * @param pmcProfile
*/ */
protected updateProfileHealthValues(pmcProfile: IPmcData): void protected updateProfileHealthValues(pmcProfile: IPmcData): void
@ -538,14 +538,23 @@ 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(x => x.type === "EnergyRegeneration").reduce((sum, curr) => sum + curr.value, 0); energyRegenPerHour += pmcProfile.Bonuses.filter((x) => x.type === "EnergyRegeneration").reduce(
hydrationRegenPerHour += pmcProfile.Bonuses.filter(x => x.type === "HydrationRegeneration").reduce((sum, curr) => sum + curr.value, 0); (sum, curr) => sum + curr.value,
hpRegenPerHour += pmcProfile.Bonuses.filter(x => x.type === "HealthRegeneration").reduce((sum, curr) => sum + curr.value, 0); 0,
);
hydrationRegenPerHour += pmcProfile.Bonuses.filter((x) => x.type === "HydrationRegeneration").reduce(
(sum, curr) => sum + curr.value,
0,
);
hpRegenPerHour += pmcProfile.Bonuses.filter((x) => x.type === "HealthRegeneration").reduce(
(sum, curr) => sum + curr.value,
0,
);
if (pmcProfile.Health.Energy.Current !== pmcProfile.Health.Energy.Maximum) if (pmcProfile.Health.Energy.Current !== pmcProfile.Health.Energy.Maximum)
{ {
// Set new value, whatever is smallest // Set new value, whatever is smallest
pmcProfile.Health.Energy.Current += Math.round((energyRegenPerHour * (diffSeconds / 3600))); pmcProfile.Health.Energy.Current += Math.round(energyRegenPerHour * (diffSeconds / 3600));
if (pmcProfile.Health.Energy.Current > pmcProfile.Health.Energy.Maximum) if (pmcProfile.Health.Energy.Current > pmcProfile.Health.Energy.Maximum)
{ {
pmcProfile.Health.Energy.Current = pmcProfile.Health.Energy.Maximum; pmcProfile.Health.Energy.Current = pmcProfile.Health.Energy.Maximum;
@ -554,7 +563,7 @@ export class GameController
if (pmcProfile.Health.Hydration.Current !== pmcProfile.Health.Hydration.Maximum) if (pmcProfile.Health.Hydration.Current !== pmcProfile.Health.Hydration.Maximum)
{ {
pmcProfile.Health.Hydration.Current += Math.round((hydrationRegenPerHour * (diffSeconds / 3600))); pmcProfile.Health.Hydration.Current += Math.round(hydrationRegenPerHour * (diffSeconds / 3600));
if (pmcProfile.Health.Hydration.Current > pmcProfile.Health.Hydration.Maximum) if (pmcProfile.Health.Hydration.Current > pmcProfile.Health.Hydration.Maximum)
{ {
pmcProfile.Health.Hydration.Current = pmcProfile.Health.Hydration.Maximum; pmcProfile.Health.Hydration.Current = pmcProfile.Health.Hydration.Maximum;
@ -569,7 +578,7 @@ export class GameController
// Check part hp // Check part hp
if (bodyPart.Health.Current < bodyPart.Health.Maximum) if (bodyPart.Health.Current < bodyPart.Health.Maximum)
{ {
bodyPart.Health.Current += Math.round((hpRegenPerHour * (diffSeconds / 3600))); bodyPart.Health.Current += Math.round(hpRegenPerHour * (diffSeconds / 3600));
} }
if (bodyPart.Health.Current > bodyPart.Health.Maximum) if (bodyPart.Health.Current > bodyPart.Health.Maximum)
{ {
@ -618,7 +627,9 @@ export class GameController
const location: ILocationData = this.databaseServer.getTables().locations[locationKey]; const location: ILocationData = this.databaseServer.getTables().locations[locationKey];
if (!location.base) if (!location.base)
{ {
this.logger.warning(this.localisationService.getText("location-unable_to_fix_broken_waves_missing_base", locationKey)); this.logger.warning(
this.localisationService.getText("location-unable_to_fix_broken_waves_missing_base", locationKey),
);
continue; continue;
} }
@ -626,7 +637,9 @@ export class GameController
{ {
if ((wave.slots_max - wave.slots_min === 0)) if ((wave.slots_max - wave.slots_min === 0))
{ {
this.logger.debug(`Fixed ${wave.WildSpawnType} Spawn: ${locationKey} wave: ${wave.number} of type: ${wave.WildSpawnType} in zone: ${wave.SpawnPoints} with Max Slots of ${wave.slots_max}`); this.logger.debug(
`Fixed ${wave.WildSpawnType} Spawn: ${locationKey} wave: ${wave.number} of type: ${wave.WildSpawnType} in zone: ${wave.SpawnPoints} with Max Slots of ${wave.slots_max}`,
);
wave.slots_max++; wave.slots_max++;
} }
} }
@ -673,7 +686,8 @@ export class GameController
} }
/** /**
* Find and split waves with large numbers of bots into smaller waves - BSG appears to reduce the size of these waves to one bot when they're waiting to spawn for too long * Find and split waves with large numbers of bots into smaller waves - BSG appears to reduce the size of these
* waves to one bot when they're waiting to spawn for too long
*/ */
protected splitBotWavesIntoSingleWaves(): void protected splitBotWavesIntoSingleWaves(): void
{ {
@ -689,7 +703,10 @@ export class GameController
for (const wave of location.base.waves) for (const wave of location.base.waves)
{ {
// Wave has size that makes it candidate for splitting // Wave has size that makes it candidate for splitting
if (wave.slots_max - wave.slots_min >= this.locationConfig.splitWaveIntoSingleSpawnsSettings.waveSizeThreshold) if (
wave.slots_max - wave.slots_min >=
this.locationConfig.splitWaveIntoSingleSpawnsSettings.waveSizeThreshold
)
{ {
// Get count of bots to be spawned in wave // Get count of bots to be spawned in wave
const waveSize = wave.slots_max - wave.slots_min; const waveSize = wave.slots_max - wave.slots_min;
@ -700,7 +717,9 @@ export class GameController
// Get index of wave // Get index of wave
const indexOfWaveToSplit = location.base.waves.indexOf(wave); const indexOfWaveToSplit = location.base.waves.indexOf(wave);
this.logger.debug(`Splitting map: ${location.base.Id} wave: ${indexOfWaveToSplit} with ${waveSize} bots`); this.logger.debug(
`Splitting map: ${location.base.Id} wave: ${indexOfWaveToSplit} with ${waveSize} bots`,
);
// Add new waves to fill gap from bots we removed in above wave // Add new waves to fill gap from bots we removed in above wave
let wavesAddedCount = 0; let wavesAddedCount = 0;
@ -716,20 +735,23 @@ export class GameController
waveToAdd.number = index; waveToAdd.number = index;
} }
// Place wave into array in just-edited postion + 1 // Place wave into array in just-edited position + 1
location.base.waves.splice(index, 0, waveToAdd); location.base.waves.splice(index, 0, waveToAdd);
wavesAddedCount++; wavesAddedCount++;
} }
// Update subsequent wave number property to accomodate the new waves // Update subsequent wave number property to accommodate the new waves
for (let index = indexOfWaveToSplit + wavesAddedCount + 1; index < location.base.waves.length; index++) for (
let index = indexOfWaveToSplit + wavesAddedCount + 1;
index < location.base.waves.length;
index++
)
{ {
// Some waves have value of 0, leave them as-is // Some waves have value of 0, leave them as-is
if (location.base.waves[index].number !== 0) if (location.base.waves[index].number !== 0)
{ {
location.base.waves[index].number += wavesAddedCount; location.base.waves[index].number += wavesAddedCount;
} }
} }
} }
} }
@ -753,9 +775,13 @@ export class GameController
for (const modKey in activeMods) for (const modKey in activeMods)
{ {
const modDetails = activeMods[modKey]; const modDetails = activeMods[modKey];
if (fullProfile.aki.mods.some(x => x.author === modDetails.author if (
&& x.name === modDetails.name fullProfile.aki.mods.some((x) =>
&& x.version === modDetails.version)) x.author === modDetails.author &&
x.name === modDetails.name &&
x.version === modDetails.version
)
)
{ {
// Exists already, skip // Exists already, skip
continue; continue;
@ -765,13 +791,13 @@ export class GameController
author: modDetails.author, author: modDetails.author,
dateAdded: Date.now(), dateAdded: Date.now(),
name: modDetails.name, name: modDetails.name,
version: modDetails.version version: modDetails.version,
}); });
} }
} }
/** /**
* Check for any missing assorts inside each traders assort.json data, checking against traders qeustassort.json * Check for any missing assorts inside each traders assort.json data, checking against traders questassort.json
*/ */
protected validateQuestAssortUnlocksExist(): void protected validateQuestAssortUnlocksExist(): void
{ {
@ -788,19 +814,26 @@ export class GameController
} }
// Merge started/success/fail quest assorts into one dictionary // Merge started/success/fail quest assorts into one dictionary
const mergedQuestAssorts = { ...traderData.questassort["started"], ...traderData.questassort["success"], ...traderData.questassort["fail"]}; const mergedQuestAssorts = {
...traderData.questassort["started"],
...traderData.questassort["success"],
...traderData.questassort["fail"],
};
// loop over all assorts for trader // Loop over all assorts for trader
for (const [assortKey, questKey] of Object.entries(mergedQuestAssorts)) for (const [assortKey, questKey] of Object.entries(mergedQuestAssorts))
{ {
// Does assort key exist in trader assort file // Does assort key exist in trader assort file
if (!traderAssorts.loyal_level_items[assortKey]) if (!traderAssorts.loyal_level_items[assortKey])
{ {
// reverse lookup of enum key by value // Reverse lookup of enum key by value
const messageValues = { const messageValues = {
traderName: Object.keys(Traders)[Object.values(Traders).indexOf(traderId)], traderName: Object.keys(Traders)[Object.values(Traders).indexOf(traderId)],
questName: quests[questKey]?.QuestName ?? "UNKNOWN"}; questName: quests[questKey]?.QuestName ?? "UNKNOWN",
this.logger.debug(this.localisationService.getText("assort-missing_quest_assort_unlock", messageValues)); };
this.logger.debug(
this.localisationService.getText("assort-missing_quest_assort_unlock", messageValues),
);
} }
} }
} }
@ -847,7 +880,7 @@ export class GameController
*/ */
protected removePraporTestMessage(): void protected removePraporTestMessage(): void
{ {
// Iterate over all langauges (e.g. "en", "fr") // Iterate over all languages (e.g. "en", "fr")
for (const localeKey in this.databaseServer.getTables().locales.global) for (const localeKey in this.databaseServer.getTables().locales.global)
{ {
this.databaseServer.getTables().locales.global[localeKey]["61687e2c3e526901fa76baf9"] = ""; this.databaseServer.getTables().locales.global[localeKey]["61687e2c3e526901fa76baf9"] = "";
@ -860,7 +893,9 @@ 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 => x.TriggerId === "" && x.TriggerName === ""); const nonTriggerLabsBossSpawns = labsBase.BossLocationSpawn.filter((x) =>
x.TriggerId === "" && x.TriggerName === ""
);
if (nonTriggerLabsBossSpawns) if (nonTriggerLabsBossSpawns)
{ {
for (const boss of nonTriggerLabsBossSpawns) for (const boss of nonTriggerLabsBossSpawns)

View File

@ -8,9 +8,9 @@ export class HandbookController
{ {
constructor( constructor(
@inject("DatabaseServer") protected databaseServer: DatabaseServer, @inject("DatabaseServer") protected databaseServer: DatabaseServer,
@inject("HandbookHelper") protected handbookHelper: HandbookHelper @inject("HandbookHelper") protected handbookHelper: HandbookHelper,
) )
{ } {}
public load(): void public load(): void
{ {

View File

@ -31,7 +31,7 @@ export class HealthController
@inject("InventoryHelper") protected inventoryHelper: InventoryHelper, @inject("InventoryHelper") protected inventoryHelper: InventoryHelper,
@inject("LocalisationService") protected localisationService: LocalisationService, @inject("LocalisationService") protected localisationService: LocalisationService,
@inject("HttpResponseUtil") protected httpResponse: HttpResponseUtil, @inject("HttpResponseUtil") protected httpResponse: HttpResponseUtil,
@inject("HealthHelper") protected healthHelper: HealthHelper @inject("HealthHelper") protected healthHelper: HealthHelper,
) )
{} {}
@ -43,7 +43,13 @@ export class HealthController
* @param addEffects Should effects found be added or removed from profile * @param addEffects Should effects found be added or removed from profile
* @param deleteExistingEffects Should all prior effects be removed before apply new ones * @param deleteExistingEffects Should all prior effects be removed before apply new ones
*/ */
public saveVitality(pmcData: IPmcData, info: ISyncHealthRequestData, sessionID: string, addEffects = true, deleteExistingEffects = true): void public saveVitality(
pmcData: IPmcData,
info: ISyncHealthRequestData,
sessionID: string,
addEffects = true,
deleteExistingEffects = true,
): void
{ {
this.healthHelper.saveVitality(pmcData, info, sessionID, addEffects, deleteExistingEffects); this.healthHelper.saveVitality(pmcData, info, sessionID, addEffects, deleteExistingEffects);
} }
@ -60,10 +66,13 @@ export class HealthController
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("health-healing_item_not_found", healingItemToUse._id); const errorMessage = this.localisationService.getText(
"health-healing_item_not_found",
healingItemToUse._id,
);
this.logger.error(errorMessage); this.logger.error(errorMessage);
return this.httpResponse.appendErrorToOutput(output, errorMessage); return this.httpResponse.appendErrorToOutput(output, errorMessage);
@ -82,8 +91,8 @@ export class HealthController
else else
{ {
// Get max healing from db // Get max healing from db
const maxhp = this.itemHelper.getItem(healingItemToUse._tpl)[1]._props.MaxHpResource; const maxHp = this.itemHelper.getItem(healingItemToUse._tpl)[1]._props.MaxHpResource;
healingItemToUse.upd.MedKit = { HpResource: maxhp - request.count }; // Subtract amout used from max healingItemToUse.upd.MedKit = {HpResource: maxHp - request.count}; // Subtract amount used from max
} }
// Resource in medkit is spent, delete it // Resource in medkit is spent, delete it
@ -108,11 +117,14 @@ export class HealthController
let output = this.eventOutputHolder.getOutput(sessionID); let 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
return this.httpResponse.appendErrorToOutput(output, this.localisationService.getText("health-unable_to_find_item_to_consume", request.item)); return this.httpResponse.appendErrorToOutput(
output,
this.localisationService.getText("health-unable_to_find_item_to_consume", request.item),
);
} }
const consumedItemMaxResource = this.itemHelper.getItem(itemToConsume._tpl)[1]._props.MaxResource; const consumedItemMaxResource = this.itemHelper.getItem(itemToConsume._tpl)[1]._props.MaxResource;
@ -120,7 +132,7 @@ export class HealthController
{ {
if (itemToConsume.upd.FoodDrink === undefined) if (itemToConsume.upd.FoodDrink === undefined)
{ {
itemToConsume.upd.FoodDrink = { HpPercent: consumedItemMaxResource - request.count }; itemToConsume.upd.FoodDrink = {HpPercent: consumedItemMaxResource - request.count};
} }
else else
{ {
@ -147,20 +159,21 @@ export class HealthController
* @param sessionID Session id * @param sessionID Session id
* @returns IItemEventRouterResponse * @returns IItemEventRouterResponse
*/ */
public healthTreatment(pmcData: IPmcData, healthTreatmentRequest: IHealthTreatmentRequestData, sessionID: string): IItemEventRouterResponse public healthTreatment(
pmcData: IPmcData,
healthTreatmentRequest: IHealthTreatmentRequestData,
sessionID: string,
): IItemEventRouterResponse
{ {
let output = this.eventOutputHolder.getOutput(sessionID); let output = this.eventOutputHolder.getOutput(sessionID);
const payMoneyRequest: IProcessBuyTradeRequestData = { const payMoneyRequest: IProcessBuyTradeRequestData = {
Action: healthTreatmentRequest.Action, Action: healthTreatmentRequest.Action,
tid: Traders.THERAPIST, tid: Traders.THERAPIST,
// eslint-disable-next-line @typescript-eslint/naming-convention
scheme_items: healthTreatmentRequest.items, scheme_items: healthTreatmentRequest.items,
type: "", type: "",
// eslint-disable-next-line @typescript-eslint/naming-convention
item_id: "", item_id: "",
count: 0, count: 0,
// eslint-disable-next-line @typescript-eslint/naming-convention scheme_id: 0,
scheme_id: 0
}; };
output = this.paymentService.payMoney(pmcData, payMoneyRequest, sessionID, output); output = this.paymentService.payMoney(pmcData, payMoneyRequest, sessionID, output);
@ -175,7 +188,7 @@ export class HealthController
const partRequest: BodyPart = healthTreatmentRequest.difference.BodyParts[bodyPartKey]; const partRequest: BodyPart = healthTreatmentRequest.difference.BodyParts[bodyPartKey];
const profilePart = pmcData.Health.BodyParts[bodyPartKey]; const profilePart = pmcData.Health.BodyParts[bodyPartKey];
// Set profile bodypart to max // Set profile body part to max
profilePart.Health.Current = profilePart.Health.Maximum; profilePart.Health.Current = profilePart.Health.Maximum;
// Check for effects to remove // Check for effects to remove
@ -207,7 +220,6 @@ export class HealthController
* @param info Request data * @param info Request data
* @param sessionID * @param sessionID
*/ */
// eslint-disable-next-line @typescript-eslint/no-unused-vars
public applyWorkoutChanges(pmcData: IPmcData, info: IWorkoutData, sessionId: string): void public applyWorkoutChanges(pmcData: IPmcData, info: IWorkoutData, sessionId: string): void
{ {
// https://dev.sp-tarkov.com/SPT-AKI/Server/issues/2674 // https://dev.sp-tarkov.com/SPT-AKI/Server/issues/2674

View File

@ -69,7 +69,7 @@ export class HideoutController
@inject("LocalisationService") protected localisationService: LocalisationService, @inject("LocalisationService") protected localisationService: LocalisationService,
@inject("ConfigServer") protected configServer: ConfigServer, @inject("ConfigServer") protected configServer: ConfigServer,
@inject("JsonUtil") protected jsonUtil: JsonUtil, @inject("JsonUtil") protected jsonUtil: JsonUtil,
@inject("FenceService") protected fenceService: FenceService @inject("FenceService") protected fenceService: FenceService,
) )
{ {
this.hideoutConfig = this.configServer.getConfig(ConfigTypes.HIDEOUT); this.hideoutConfig = this.configServer.getConfig(ConfigTypes.HIDEOUT);
@ -83,15 +83,19 @@ export class HideoutController
* @param sessionID Session id * @param sessionID Session id
* @returns IItemEventRouterResponse * @returns IItemEventRouterResponse
*/ */
public startUpgrade(pmcData: IPmcData, request: IHideoutUpgradeRequestData, sessionID: string): IItemEventRouterResponse public startUpgrade(
pmcData: IPmcData,
request: IHideoutUpgradeRequestData,
sessionID: string,
): IItemEventRouterResponse
{ {
const output = this.eventOutputHolder.getOutput(sessionID); const output = this.eventOutputHolder.getOutput(sessionID);
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 { return {
inventoryItem: item, inventoryItem: item,
requestedItem: reqItem requestedItem: reqItem,
}; };
}); });
@ -100,14 +104,18 @@ export class HideoutController
{ {
if (!item.inventoryItem) if (!item.inventoryItem)
{ {
this.logger.error(this.localisationService.getText("hideout-unable_to_find_item_in_inventory", item.requestedItem.id)); this.logger.error(
this.localisationService.getText("hideout-unable_to_find_item_in_inventory", item.requestedItem.id),
);
return this.httpResponse.appendErrorToOutput(output); return this.httpResponse.appendErrorToOutput(output);
} }
if (this.paymentHelper.isMoneyTpl(item.inventoryItem._tpl) if (
&& item.inventoryItem.upd this.paymentHelper.isMoneyTpl(item.inventoryItem._tpl) &&
&& item.inventoryItem.upd.StackObjectsCount item.inventoryItem.upd &&
&& item.inventoryItem.upd.StackObjectsCount > item.requestedItem.count) item.inventoryItem.upd.StackObjectsCount &&
item.inventoryItem.upd.StackObjectsCount > item.requestedItem.count
)
{ {
item.inventoryItem.upd.StackObjectsCount -= item.requestedItem.count; item.inventoryItem.upd.StackObjectsCount -= item.requestedItem.count;
} }
@ -118,17 +126,21 @@ export class HideoutController
} }
// Construction time management // Construction time management
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));
return this.httpResponse.appendErrorToOutput(output); return this.httpResponse.appendErrorToOutput(output);
} }
const hideoutData = this.databaseServer.getTables().hideout.areas.find(area => area.type === request.areaType); const hideoutData = this.databaseServer.getTables().hideout.areas.find((area) =>
area.type === request.areaType
);
if (!hideoutData) if (!hideoutData)
{ {
this.logger.error(this.localisationService.getText("hideout-unable_to_find_area_in_database", request.areaType)); this.logger.error(
this.localisationService.getText("hideout-unable_to_find_area_in_database", request.areaType),
);
return this.httpResponse.appendErrorToOutput(output); return this.httpResponse.appendErrorToOutput(output);
} }
@ -152,12 +164,16 @@ export class HideoutController
* @param sessionID Session id * @param sessionID Session id
* @returns IItemEventRouterResponse * @returns IItemEventRouterResponse
*/ */
public upgradeComplete(pmcData: IPmcData, request: HideoutUpgradeCompleteRequestData, sessionID: string): IItemEventRouterResponse public upgradeComplete(
pmcData: IPmcData,
request: HideoutUpgradeCompleteRequestData,
sessionID: string,
): IItemEventRouterResponse
{ {
const output = this.eventOutputHolder.getOutput(sessionID); const output = this.eventOutputHolder.getOutput(sessionID);
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));
@ -169,10 +185,12 @@ 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.localisationService.getText("hideout-unable_to_find_area_in_database", request.areaType)); this.logger.error(
this.localisationService.getText("hideout-unable_to_find_area_in_database", request.areaType),
);
return this.httpResponse.appendErrorToOutput(output); return this.httpResponse.appendErrorToOutput(output);
} }
@ -190,17 +208,31 @@ export class HideoutController
// Upgrade includes a container improvement/addition // Upgrade includes a container improvement/addition
if (hideoutStage?.container) if (hideoutStage?.container)
{ {
this.addContainerImprovementToProfile(output, sessionID, pmcData, profileHideoutArea, hideoutData, hideoutStage); this.addContainerImprovementToProfile(
output,
sessionID,
pmcData,
profileHideoutArea,
hideoutData,
hideoutStage,
);
} }
// Upgrading water collector / med station // Upgrading water collector / med station
if (profileHideoutArea.type === HideoutAreas.WATER_COLLECTOR || profileHideoutArea.type === HideoutAreas.MEDSTATION) if (
profileHideoutArea.type === HideoutAreas.WATER_COLLECTOR ||
profileHideoutArea.type === HideoutAreas.MEDSTATION
)
{ {
this.checkAndUpgradeWall(pmcData); this.checkAndUpgradeWall(pmcData);
} }
// Add Skill Points Per Area Upgrade // Add Skill Points Per Area Upgrade
this.profileHelper.addSkillPointsToPlayer(pmcData, SkillTypes.HIDEOUT_MANAGEMENT, db.globals.config.SkillsSettings.HideoutManagement.SkillPointsPerAreaUpgrade); this.profileHelper.addSkillPointsToPlayer(
pmcData,
SkillTypes.HIDEOUT_MANAGEMENT,
db.globals.config.SkillsSettings.HideoutManagement.SkillPointsPerAreaUpgrade,
);
return output; return output;
} }
@ -211,11 +243,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;
@ -224,7 +256,6 @@ export class HideoutController
} }
/** /**
*
* @param pmcData Profile to edit * @param pmcData Profile to edit
* @param output Object to send back to client * @param output Object to send back to client
* @param sessionID Session/player id * @param sessionID Session/player id
@ -232,7 +263,14 @@ export class HideoutController
* @param dbHideoutArea Hideout area being upgraded * @param dbHideoutArea Hideout area being upgraded
* @param hideoutStage Stage hideout area is being upgraded to * @param hideoutStage Stage hideout area is being upgraded to
*/ */
protected addContainerImprovementToProfile(output: IItemEventRouterResponse, sessionID: string, pmcData: IPmcData, profileParentHideoutArea: HideoutArea, dbHideoutArea: IHideoutArea, hideoutStage: Stage): void protected addContainerImprovementToProfile(
output: IItemEventRouterResponse,
sessionID: string,
pmcData: IPmcData,
profileParentHideoutArea: HideoutArea,
dbHideoutArea: IHideoutArea,
hideoutStage: Stage,
): void
{ {
// Add key/value to `hideoutAreaStashes` dictionary - used to link hideout area to inventory stash by its id // Add key/value to `hideoutAreaStashes` dictionary - used to link hideout area to inventory stash by its id
if (!pmcData.Inventory.hideoutAreaStashes[dbHideoutArea.type]) if (!pmcData.Inventory.hideoutAreaStashes[dbHideoutArea.type])
@ -247,7 +285,9 @@ export class HideoutController
this.addContainerUpgradeToClientOutput(output, sessionID, dbHideoutArea.type, dbHideoutArea, hideoutStage); this.addContainerUpgradeToClientOutput(output, sessionID, dbHideoutArea.type, dbHideoutArea, hideoutStage);
// Some areas like gun stand have a child area linked to it, it needs to do the same as above // Some areas like gun stand have a child area linked to it, it needs to do the same as above
const childDbArea = this.databaseServer.getTables().hideout.areas.find(x => x.parentArea === dbHideoutArea._id); const childDbArea = this.databaseServer.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
@ -257,7 +297,9 @@ 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 => x.type === profileParentHideoutArea.type).level; pmcData.Hideout.Areas.find((x) => x.type === childDbArea.type).level = pmcData.Hideout.Areas.find((x) =>
x.type === profileParentHideoutArea.type
).level;
// Add/upgrade stash item in player inventory // Add/upgrade stash item in player inventory
const childDbAreaStage = childDbArea.stages[profileParentHideoutArea.level]; const childDbAreaStage = childDbArea.stages[profileParentHideoutArea.level];
@ -276,38 +318,41 @@ export class HideoutController
*/ */
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)
existingInventoryItem._tpl = hideoutStage.container; existingInventoryItem._tpl = hideoutStage.container;
return; return;
} }
// Add new item as none exists // Add new item as none exists
pmcData.Inventory.items.push({ _id: dbHideoutData._id, _tpl: hideoutStage.container }); pmcData.Inventory.items.push({_id: dbHideoutData._id, _tpl: hideoutStage.container});
} }
/** /**
*
* @param output Objet to send to client * @param output Objet to send to client
* @param sessionID Session/player id * @param sessionID Session/player id
* @param areaType Hideout area that had stash added * @param areaType Hideout area that had stash added
* @param hideoutDbData Hideout area that caused addition of stash * @param hideoutDbData Hideout area that caused addition of stash
* @param hideoutStage Hideout area upgraded to this * @param hideoutStage Hideout area upgraded to this
*/ */
protected addContainerUpgradeToClientOutput(output: IItemEventRouterResponse, sessionID: string, areaType: HideoutAreas, hideoutDbData: IHideoutArea, hideoutStage: Stage): void protected addContainerUpgradeToClientOutput(
output: IItemEventRouterResponse,
sessionID: string,
areaType: HideoutAreas,
hideoutDbData: IHideoutArea,
hideoutStage: Stage,
): void
{ {
if (!output.profileChanges[sessionID].changedHideoutStashes) if (!output.profileChanges[sessionID].changedHideoutStashes)
{ {
output.profileChanges[sessionID].changedHideoutStashes = {}; output.profileChanges[sessionID].changedHideoutStashes = {};
} }
output.profileChanges[sessionID].changedHideoutStashes[areaType] = output.profileChanges[sessionID].changedHideoutStashes[areaType] = {
{
Id: hideoutDbData._id, Id: hideoutDbData._id,
Tpl: hideoutStage.container Tpl: hideoutStage.container,
}; };
} }
@ -315,28 +360,37 @@ export class HideoutController
* Handle HideoutPutItemsInAreaSlots * Handle HideoutPutItemsInAreaSlots
* Create item in hideout slot item array, remove item from player inventory * Create item in hideout slot item array, remove item from player inventory
* @param pmcData Profile data * @param pmcData Profile data
* @param addItemToHideoutRequest reqeust from client to place item in area slot * @param addItemToHideoutRequest request from client to place item in area slot
* @param sessionID Session id * @param sessionID Session id
* @returns IItemEventRouterResponse object * @returns IItemEventRouterResponse object
*/ */
public putItemsInAreaSlots(pmcData: IPmcData, addItemToHideoutRequest: IHideoutPutItemInRequestData, sessionID: string): IItemEventRouterResponse public putItemsInAreaSlots(
pmcData: IPmcData,
addItemToHideoutRequest: IHideoutPutItemInRequestData,
sessionID: string,
): IItemEventRouterResponse
{ {
let output = this.eventOutputHolder.getOutput(sessionID); let output = this.eventOutputHolder.getOutput(sessionID);
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 { return {
inventoryItem: item, inventoryItem: item,
requestedItem: kvp[1], requestedItem: kvp[1],
slot: kvp[0] 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.localisationService.getText("hideout-unable_to_find_area_in_database", addItemToHideoutRequest.areaType)); this.logger.error(
this.localisationService.getText(
"hideout-unable_to_find_area_in_database",
addItemToHideoutRequest.areaType,
),
);
return this.httpResponse.appendErrorToOutput(output); return this.httpResponse.appendErrorToOutput(output);
} }
@ -344,17 +398,22 @@ export class HideoutController
{ {
if (!item.inventoryItem) if (!item.inventoryItem)
{ {
this.logger.error(this.localisationService.getText("hideout-unable_to_find_item_in_inventory", {itemId: item.requestedItem["id"], area: hideoutArea.type})); this.logger.error(
this.localisationService.getText("hideout-unable_to_find_item_in_inventory", {
itemId: item.requestedItem["id"],
area: hideoutArea.type,
}),
);
return this.httpResponse.appendErrorToOutput(output); return this.httpResponse.appendErrorToOutput(output);
} }
// 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, _id: item.inventoryItem._id,
_tpl: item.inventoryItem._tpl, _tpl: item.inventoryItem._tpl,
upd: item.inventoryItem.upd upd: item.inventoryItem.upd,
}]; }];
output = this.inventoryHelper.removeItem(pmcData, item.inventoryItem._id, sessionID, output); output = this.inventoryHelper.removeItem(pmcData, item.inventoryItem._id, sessionID, output);
@ -374,11 +433,15 @@ export class HideoutController
* @param sessionID Session id * @param sessionID Session id
* @returns IItemEventRouterResponse * @returns IItemEventRouterResponse
*/ */
public takeItemsFromAreaSlots(pmcData: IPmcData, request: IHideoutTakeItemOutRequestData, sessionID: string): IItemEventRouterResponse public takeItemsFromAreaSlots(
pmcData: IPmcData,
request: IHideoutTakeItemOutRequestData,
sessionID: string,
): IItemEventRouterResponse
{ {
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));
@ -387,19 +450,30 @@ export class HideoutController
if (!hideoutArea.slots || hideoutArea.slots.length === 0) if (!hideoutArea.slots || hideoutArea.slots.length === 0)
{ {
this.logger.error(this.localisationService.getText("hideout-unable_to_find_item_to_remove_from_area", hideoutArea.type)); this.logger.error(
this.localisationService.getText("hideout-unable_to_find_item_to_remove_from_area", hideoutArea.type),
);
return this.httpResponse.appendErrorToOutput(output); return this.httpResponse.appendErrorToOutput(output);
} }
// Handle areas that have resources that can be placed in/taken out of slots from the area // Handle areas that have resources that can be placed in/taken out of slots from the area
if ([HideoutAreas.AIR_FILTERING, HideoutAreas.WATER_COLLECTOR, HideoutAreas.GENERATOR, HideoutAreas.BITCOIN_FARM].includes(hideoutArea.type)) if (
[
HideoutAreas.AIR_FILTERING,
HideoutAreas.WATER_COLLECTOR,
HideoutAreas.GENERATOR,
HideoutAreas.BITCOIN_FARM,
].includes(hideoutArea.type)
)
{ {
const response = this.removeResourceFromArea(sessionID, pmcData, request, output, hideoutArea); const response = this.removeResourceFromArea(sessionID, pmcData, request, output, hideoutArea);
this.update(); this.update();
return response; return response;
} }
throw new Error(this.localisationService.getText("hideout-unhandled_remove_item_from_area_request", hideoutArea.type)); throw new Error(
this.localisationService.getText("hideout-unhandled_remove_item_from_area_request", hideoutArea.type),
);
} }
/** /**
@ -411,22 +485,36 @@ export class HideoutController
* @param hideoutArea Area fuel is being removed from * @param hideoutArea Area fuel is being removed from
* @returns IItemEventRouterResponse response * @returns IItemEventRouterResponse response
*/ */
protected removeResourceFromArea(sessionID: string, pmcData: IPmcData, removeResourceRequest: IHideoutTakeItemOutRequestData, output: IItemEventRouterResponse, hideoutArea: HideoutArea): IItemEventRouterResponse protected removeResourceFromArea(
sessionID: string,
pmcData: IPmcData,
removeResourceRequest: IHideoutTakeItemOutRequestData,
output: IItemEventRouterResponse,
hideoutArea: HideoutArea,
): IItemEventRouterResponse
{ {
const slotIndexToRemove = removeResourceRequest.slots[0]; const slotIndexToRemove = removeResourceRequest.slots[0];
const itemToReturn = hideoutArea.slots.find(x => x.locationIndex === slotIndexToRemove).item[0]; const itemToReturn = hideoutArea.slots.find((x) => x.locationIndex === slotIndexToRemove).item[0];
const newReq = { const newReq = {
items: [{ items: [{
// eslint-disable-next-line @typescript-eslint/naming-convention // eslint-disable-next-line @typescript-eslint/naming-convention
item_id: itemToReturn._tpl, item_id: itemToReturn._tpl,
count: 1 count: 1,
}], }],
tid: "ragfair" tid: "ragfair",
}; };
output = this.inventoryHelper.addItem(pmcData, newReq, output, sessionID, null, !!itemToReturn.upd.SpawnedInSession, itemToReturn.upd); output = this.inventoryHelper.addItem(
pmcData,
newReq,
output,
sessionID,
null,
!!itemToReturn.upd.SpawnedInSession,
itemToReturn.upd,
);
// If addItem returned with errors, drop out // If addItem returned with errors, drop out
if (output.warnings && output.warnings.length > 0) if (output.warnings && output.warnings.length > 0)
@ -435,7 +523,7 @@ export class HideoutController
} }
// Remove items from slot, locationIndex remains // Remove items from slot, locationIndex remains
const hideoutSlotIndex = hideoutArea.slots.findIndex(x => x.locationIndex === slotIndexToRemove); const hideoutSlotIndex = hideoutArea.slots.findIndex((x) => x.locationIndex === slotIndexToRemove);
hideoutArea.slots[hideoutSlotIndex].item = undefined; hideoutArea.slots[hideoutSlotIndex].item = undefined;
return output; return output;
@ -449,14 +537,18 @@ export class HideoutController
* @param sessionID Session id * @param sessionID Session id
* @returns IItemEventRouterResponse * @returns IItemEventRouterResponse
*/ */
public toggleArea(pmcData: IPmcData, request: IHideoutToggleAreaRequestData, sessionID: string): IItemEventRouterResponse public toggleArea(
pmcData: IPmcData,
request: IHideoutToggleAreaRequestData,
sessionID: string,
): IItemEventRouterResponse
{ {
const output = this.eventOutputHolder.getOutput(sessionID); const output = this.eventOutputHolder.getOutput(sessionID);
// Force a production update (occur before area is toggled as it could be generator and doing it after generator enabled would cause incorrect calculaton of production progress) // 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));
@ -472,27 +564,31 @@ export class HideoutController
* Handle HideoutSingleProductionStart event * Handle HideoutSingleProductionStart event
* Start production for an item from hideout area * Start production for an item from hideout area
* @param pmcData Player profile * @param pmcData Player profile
* @param body Start prodution of single item request * @param body Start production of single item request
* @param sessionID Session id * @param sessionID Session id
* @returns IItemEventRouterResponse * @returns IItemEventRouterResponse
*/ */
public singleProductionStart(pmcData: IPmcData, body: IHideoutSingleProductionStartRequestData, sessionID: string): IItemEventRouterResponse public singleProductionStart(
pmcData: IPmcData,
body: IHideoutSingleProductionStartRequestData,
sessionID: string,
): IItemEventRouterResponse
{ {
// Start production // Start production
this.registerProduction(pmcData, body, sessionID); this.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 requirements = this.jsonUtil.clone(recipe.requirements.filter(i => i.type === "Item")); const requirements = this.jsonUtil.clone(recipe.requirements.filter((i) => i.type === "Item"));
const output = this.eventOutputHolder.getOutput(sessionID); const output = this.eventOutputHolder.getOutput(sessionID);
for (const itemToDelete of body.items) for (const itemToDelete of body.items)
{ {
const itemToCheck = pmcData.Inventory.items.find(i => i._id === itemToDelete.id); const itemToCheck = pmcData.Inventory.items.find((i) => i._id === itemToDelete.id);
const requirement = requirements.find(requirement => requirement.templateId === itemToCheck._tpl); const requirement = requirements.find((requirement) => requirement.templateId === itemToCheck._tpl);
if (requirement.count <= 0) if (requirement.count <= 0)
{ {
continue; continue;
@ -513,21 +609,32 @@ export class HideoutController
* @param sessionID session id * @param sessionID session id
* @returns item event router response * @returns item event router response
*/ */
public scavCaseProductionStart(pmcData: IPmcData, body: IHideoutScavCaseStartRequestData, sessionID: string): IItemEventRouterResponse public scavCaseProductionStart(
pmcData: IPmcData,
body: IHideoutScavCaseStartRequestData,
sessionID: string,
): IItemEventRouterResponse
{ {
let output = this.eventOutputHolder.getOutput(sessionID); let output = this.eventOutputHolder.getOutput(sessionID);
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.localisationService.getText("hideout-unable_to_find_scavcase_requested_item_in_profile_inventory", requestedItem.id)); this.logger.error(
this.localisationService.getText(
"hideout-unable_to_find_scavcase_requested_item_in_profile_inventory",
requestedItem.id,
),
);
return this.httpResponse.appendErrorToOutput(output); return this.httpResponse.appendErrorToOutput(output);
} }
if (inventoryItem.upd?.StackObjectsCount if (
&& inventoryItem.upd.StackObjectsCount > requestedItem.count) inventoryItem.upd?.StackObjectsCount &&
inventoryItem.upd.StackObjectsCount > requestedItem.count
)
{ {
inventoryItem.upd.StackObjectsCount -= requestedItem.count; inventoryItem.upd.StackObjectsCount -= requestedItem.count;
} }
@ -537,10 +644,12 @@ 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.localisationService.getText("hideout-unable_to_find_scav_case_recipie_in_database", body.recipeId)); this.logger.error(
this.localisationService.getText("hideout-unable_to_find_scav_case_recipie_in_database", body.recipeId),
);
return this.httpResponse.appendErrorToOutput(output); return this.httpResponse.appendErrorToOutput(output);
} }
@ -549,7 +658,11 @@ export class HideoutController
// - scav case recipe: Production time value is stored in attribute "ProductionType" with capital "P" // - scav case recipe: Production time value is stored in attribute "ProductionType" with capital "P"
const modifiedScavCaseTime = this.getScavCaseTime(pmcData, recipe.ProductionTime); const modifiedScavCaseTime = this.getScavCaseTime(pmcData, recipe.ProductionTime);
pmcData.Hideout.Production[body.recipeId] = this.hideoutHelper.initProduction(body.recipeId, modifiedScavCaseTime, false); pmcData.Hideout.Production[body.recipeId] = this.hideoutHelper.initProduction(
body.recipeId,
modifiedScavCaseTime,
false,
);
pmcData.Hideout.Production[body.recipeId].sptIsScavCase = true; pmcData.Hideout.Production[body.recipeId].sptIsScavCase = true;
return output; return output;
@ -569,7 +682,6 @@ export class HideoutController
{ {
return productionTime; return productionTime;
} }
return productionTime * fenceLevel.ScavCaseTimeModifier; return productionTime * fenceLevel.ScavCaseTimeModifier;
} }
@ -582,21 +694,24 @@ export class HideoutController
protected addScavCaseRewardsToProfile(pmcData: IPmcData, rewards: Product[], recipeId: string): void protected addScavCaseRewardsToProfile(pmcData: IPmcData, rewards: Product[], recipeId: string): void
{ {
pmcData.Hideout.Production[`ScavCase${recipeId}`] = { pmcData.Hideout.Production[`ScavCase${recipeId}`] = {
Products: rewards Products: rewards,
}; };
} }
/** /**
* Start production of continuously created item * Start production of continuously created item
* @param pmcData Player profile * @param pmcData Player profile
* @param request Continious production request * @param request Continuous production request
* @param sessionID Session id * @param sessionID Session id
* @returns IItemEventRouterResponse * @returns IItemEventRouterResponse
*/ */
public continuousProductionStart(pmcData: IPmcData, request: IHideoutContinuousProductionStartRequestData, sessionID: string): IItemEventRouterResponse public continuousProductionStart(
pmcData: IPmcData,
request: IHideoutContinuousProductionStartRequestData,
sessionID: string,
): IItemEventRouterResponse
{ {
this.registerProduction(pmcData, request, sessionID); this.registerProduction(pmcData, request, sessionID);
return this.eventOutputHolder.getOutput(sessionID); return this.eventOutputHolder.getOutput(sessionID);
} }
@ -608,7 +723,11 @@ export class HideoutController
* @param sessionID Session id * @param sessionID Session id
* @returns IItemEventRouterResponse * @returns IItemEventRouterResponse
*/ */
public takeProduction(pmcData: IPmcData, request: IHideoutTakeProductionRequestData, sessionID: string): IItemEventRouterResponse public takeProduction(
pmcData: IPmcData,
request: IHideoutTakeProductionRequestData,
sessionID: string,
): IItemEventRouterResponse
{ {
const output = this.eventOutputHolder.getOutput(sessionID); const output = this.eventOutputHolder.getOutput(sessionID);
@ -617,19 +736,24 @@ export class HideoutController
return this.hideoutHelper.getBTC(pmcData, request, sessionID); return this.hideoutHelper.getBTC(pmcData, request, sessionID);
} }
const recipe = this.databaseServer.getTables().hideout.production.find(r => r._id === request.recipeId); const recipe = this.databaseServer.getTables().hideout.production.find((r) => r._id === request.recipeId);
if (recipe) if (recipe)
{ {
return this.handleRecipe(sessionID, recipe, pmcData, request, output); return this.handleRecipe(sessionID, recipe, pmcData, request, output);
} }
const scavCase = this.databaseServer.getTables().hideout.scavcase.find(r => r._id === request.recipeId); const scavCase = this.databaseServer.getTables().hideout.scavcase.find((r) => r._id === request.recipeId);
if (scavCase) if (scavCase)
{ {
return this.handleScavCase(sessionID, pmcData, request, output); return this.handleScavCase(sessionID, pmcData, request, output);
} }
this.logger.error(this.localisationService.getText("hideout-unable_to_find_production_in_profile_by_recipie_id", request.recipeId)); this.logger.error(
this.localisationService.getText(
"hideout-unable_to_find_production_in_profile_by_recipie_id",
request.recipeId,
),
);
return this.httpResponse.appendErrorToOutput(output); return this.httpResponse.appendErrorToOutput(output);
} }
@ -643,9 +767,15 @@ export class HideoutController
* @param output Output object to update * @param output Output object to update
* @returns IItemEventRouterResponse * @returns IItemEventRouterResponse
*/ */
protected handleRecipe(sessionID: string, recipe: IHideoutProduction, pmcData: IPmcData, request: IHideoutTakeProductionRequestData, output: IItemEventRouterResponse): IItemEventRouterResponse protected handleRecipe(
sessionID: string,
recipe: IHideoutProduction,
pmcData: IPmcData,
request: IHideoutTakeProductionRequestData,
output: IItemEventRouterResponse,
): IItemEventRouterResponse
{ {
// Variables for managemnet of skill // Variables for management of skill
let craftingExpAmount = 0; let craftingExpAmount = 0;
// ? move the logic of BackendCounters in new method? // ? move the logic of BackendCounters in new method?
@ -654,13 +784,12 @@ export class HideoutController
{ {
pmcData.BackendCounters[HideoutController.nameBackendCountersCrafting] = { pmcData.BackendCounters[HideoutController.nameBackendCountersCrafting] = {
id: HideoutController.nameBackendCountersCrafting, id: HideoutController.nameBackendCountersCrafting,
value: 0 value: 0,
}; };
counterHoursCrafting = pmcData.BackendCounters[HideoutController.nameBackendCountersCrafting]; counterHoursCrafting = pmcData.BackendCounters[HideoutController.nameBackendCountersCrafting];
} }
let hoursCrafting = counterHoursCrafting.value; let hoursCrafting = counterHoursCrafting.value;
// create item and throw it into profile // create item and throw it into profile
let id = recipe.endProduct; let id = recipe.endProduct;
@ -672,18 +801,18 @@ export class HideoutController
const newReq = { const newReq = {
items: [{ items: [{
// eslint-disable-next-line @typescript-eslint/naming-convention
item_id: id, item_id: id,
count: recipe.count count: recipe.count,
}], }],
tid: "ragfair" tid: "ragfair",
}; };
const entries = Object.entries(pmcData.Hideout.Production); const entries = Object.entries(pmcData.Hideout.Production);
let prodId: string; let prodId: string;
for (const x of entries) for (const x of entries)
{ {
if (this.hideoutHelper.isProductionType(x[1])) // Production or ScavCase // Production or ScavCase
if (this.hideoutHelper.isProductionType(x[1]))
{ {
if ((x[1] as Production).RecipeId === request.recipeId) if ((x[1] as Production).RecipeId === request.recipeId)
{ {
@ -695,7 +824,12 @@ export class HideoutController
if (prodId === undefined) if (prodId === undefined)
{ {
this.logger.error(this.localisationService.getText("hideout-unable_to_find_production_in_profile_by_recipie_id", request.recipeId)); this.logger.error(
this.localisationService.getText(
"hideout-unable_to_find_production_in_profile_by_recipie_id",
request.recipeId,
),
);
return this.httpResponse.appendErrorToOutput(output); return this.httpResponse.appendErrorToOutput(output);
} }
@ -712,9 +846,9 @@ export class HideoutController
hoursCrafting += recipe.productionTime; hoursCrafting += recipe.productionTime;
if ((hoursCrafting / this.hideoutConfig.hoursForSkillCrafting) >= 1) if ((hoursCrafting / this.hideoutConfig.hoursForSkillCrafting) >= 1)
{ {
const multiplierCrafting = Math.floor((hoursCrafting / this.hideoutConfig.hoursForSkillCrafting)); const multiplierCrafting = Math.floor(hoursCrafting / this.hideoutConfig.hoursForSkillCrafting);
craftingExpAmount += (1 * multiplierCrafting); craftingExpAmount += 1 * multiplierCrafting;
hoursCrafting -= (this.hideoutConfig.hoursForSkillCrafting * multiplierCrafting); hoursCrafting -= this.hideoutConfig.hoursForSkillCrafting * multiplierCrafting;
} }
// increment // increment
@ -726,12 +860,21 @@ export class HideoutController
// manager Hideout skill // manager Hideout skill
// ? use a configuration variable for the value? // ? use a configuration variable for the value?
const globals = this.databaseServer.getTables().globals; const globals = this.databaseServer.getTables().globals;
this.profileHelper.addSkillPointsToPlayer(pmcData, SkillTypes.HIDEOUT_MANAGEMENT, globals.config.SkillsSettings.HideoutManagement.SkillPointsPerCraft, true); this.profileHelper.addSkillPointsToPlayer(
//manager Crafting skill pmcData,
SkillTypes.HIDEOUT_MANAGEMENT,
globals.config.SkillsSettings.HideoutManagement.SkillPointsPerCraft,
true,
);
// manager Crafting skill
if (craftingExpAmount > 0) if (craftingExpAmount > 0)
{ {
this.profileHelper.addSkillPointsToPlayer(pmcData, SkillTypes.CRAFTING, craftingExpAmount); this.profileHelper.addSkillPointsToPlayer(pmcData, SkillTypes.CRAFTING, craftingExpAmount);
this.profileHelper.addSkillPointsToPlayer(pmcData, SkillTypes.INTELLECT, 0.5 * (Math.round(craftingExpAmount / 15))); this.profileHelper.addSkillPointsToPlayer(
pmcData,
SkillTypes.INTELLECT,
0.5 * (Math.round(craftingExpAmount / 15)),
);
} }
area.lastRecipe = request.recipeId; area.lastRecipe = request.recipeId;
counterHoursCrafting.value = hoursCrafting; counterHoursCrafting.value = hoursCrafting;
@ -747,7 +890,7 @@ export class HideoutController
if (recipe.isEncoded) if (recipe.isEncoded)
{ {
const upd: Upd = { const upd: Upd = {
RecodableComponent: { IsEncoded: true} RecodableComponent: {IsEncoded: true},
}; };
return this.inventoryHelper.addItem(pmcData, newReq, output, sessionID, callback, true, upd); return this.inventoryHelper.addItem(pmcData, newReq, output, sessionID, callback, true, upd);
@ -764,13 +907,19 @@ export class HideoutController
* @param output Output object to update * @param output Output object to update
* @returns IItemEventRouterResponse * @returns IItemEventRouterResponse
*/ */
protected handleScavCase(sessionID: string, pmcData: IPmcData, request: IHideoutTakeProductionRequestData, output: IItemEventRouterResponse): IItemEventRouterResponse protected handleScavCase(
sessionID: string,
pmcData: IPmcData,
request: IHideoutTakeProductionRequestData,
output: IItemEventRouterResponse,
): IItemEventRouterResponse
{ {
const ongoingProductions = Object.entries(pmcData.Hideout.Production); const ongoingProductions = Object.entries(pmcData.Hideout.Production);
let prodId: string; let prodId: string;
for (const production of ongoingProductions) for (const production of ongoingProductions)
{ {
if (this.hideoutHelper.isProductionType(production[1])) // Production or ScavCase // Production or ScavCase
if (this.hideoutHelper.isProductionType(production[1]))
{ {
if ((production[1] as ScavCase).RecipeId === request.recipeId) if ((production[1] as ScavCase).RecipeId === request.recipeId)
{ {
@ -782,7 +931,12 @@ export class HideoutController
if (prodId === undefined) if (prodId === undefined)
{ {
this.logger.error(this.localisationService.getText("hideout-unable_to_find_production_in_profile_by_recipie_id", request.recipeId)); this.logger.error(
this.localisationService.getText(
"hideout-unable_to_find_production_in_profile_by_recipie_id",
request.recipeId,
),
);
return this.httpResponse.appendErrorToOutput(output); return this.httpResponse.appendErrorToOutput(output);
} }
@ -795,28 +949,29 @@ export class HideoutController
// Remove the old production from output object before its sent to client // Remove the old production from output object before its sent to client
delete output.profileChanges[sessionID].production[request.recipeId]; delete output.profileChanges[sessionID].production[request.recipeId];
const itemsToAdd = pmcData.Hideout.Production[prodId].Products.map((x: { _tpl: string; upd?: { StackObjectsCount?: number; }; }) => const itemsToAdd = pmcData.Hideout.Production[prodId].Products.map(
{ (x: {_tpl: string; upd?: {StackObjectsCount?: number;};}) =>
let id = x._tpl;
if (this.presetHelper.hasPreset(id))
{ {
id = this.presetHelper.getDefaultPreset(id)._id; let id = x._tpl;
} if (this.presetHelper.hasPreset(id))
const numOfItems = !x.upd?.StackObjectsCount {
? 1 id = this.presetHelper.getDefaultPreset(id)._id;
: x.upd.StackObjectsCount; }
// eslint-disable-next-line @typescript-eslint/naming-convention const numOfItems = !x.upd?.StackObjectsCount ?
return { item_id: id, count: numOfItems }; 1 :
}); x.upd.StackObjectsCount;
return {item_id: id, count: numOfItems};
},
);
const newReq = { const newReq = {
items: itemsToAdd, items: itemsToAdd,
tid: "ragfair" tid: "ragfair",
}; };
const callback = () => const callback = () =>
{ {
// Null production data now it's complete - will be cleaned up later by update() process // Null production data now it's complete - will be cleaned up later by update() process
pmcData.Hideout.Production[prodId] = null; pmcData.Hideout.Production[prodId] = null;
}; };
@ -831,18 +986,21 @@ export class HideoutController
* @param sessionID Session id * @param sessionID Session id
* @returns IItemEventRouterResponse * @returns IItemEventRouterResponse
*/ */
public registerProduction(pmcData: IPmcData, request: IHideoutSingleProductionStartRequestData | IHideoutContinuousProductionStartRequestData, sessionID: string): IItemEventRouterResponse public registerProduction(
pmcData: IPmcData,
request: IHideoutSingleProductionStartRequestData | IHideoutContinuousProductionStartRequestData,
sessionID: string,
): IItemEventRouterResponse
{ {
return this.hideoutHelper.registerProduction(pmcData, request, sessionID); return this.hideoutHelper.registerProduction(pmcData, request, sessionID);
} }
/** /**
* Get quick time event list for hideout * Get quick time event list for hideout
* // TODO - implement this * // TODO: Implement this
* @param sessionId Session id * @param sessionId Session id
* @returns IQteData array * @returns IQteData array
*/ */
// eslint-disable-next-line @typescript-eslint/no-unused-vars
public getQteList(sessionId: string): IQteData[] public getQteList(sessionId: string): IQteData[]
{ {
return this.databaseServer.getTables().hideout.qte; return this.databaseServer.getTables().hideout.qte;
@ -855,8 +1013,11 @@ export class HideoutController
* @param pmcData Profile to adjust * @param pmcData Profile to adjust
* @param request QTE result object * @param request QTE result object
*/ */
// eslint-disable-next-line @typescript-eslint/no-unused-vars public handleQTEEventOutcome(
public handleQTEEventOutcome(sessionId: string, pmcData: IPmcData, request: IHandleQTEEventRequestData): IItemEventRouterResponse sessionId: string,
pmcData: IPmcData,
request: IHandleQTEEventRequestData,
): IItemEventRouterResponse
{ {
// { // {
// Action: "HideoutQuickTimeEvent", // Action: "HideoutQuickTimeEvent",
@ -890,22 +1051,28 @@ export class HideoutController
* @param request shooting range score request * @param request shooting range score request
* @returns IItemEventRouterResponse * @returns IItemEventRouterResponse
*/ */
public recordShootingRangePoints(sessionId: string, pmcData: IPmcData, request: IRecordShootingRangePoints): IItemEventRouterResponse public recordShootingRangePoints(
sessionId: string,
pmcData: IPmcData,
request: IRecordShootingRangePoints,
): IItemEventRouterResponse
{ {
// Check if counter exists, add placeholder if it doesnt // Check if counter exists, add placeholder if it doesn't
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({ pmcData.Stats.Eft.OverallCounters.Items.push({
Key: ["ShootingRangePoints"], Key: ["ShootingRangePoints"],
Value: 0 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 => x.Key.includes("ShootingRangePoints")); const shootingRangeHighScore = pmcData.Stats.Eft.OverallCounters.Items.find((x) =>
x.Key.includes("ShootingRangePoints")
);
shootingRangeHighScore.Value = request.points; shootingRangeHighScore.Value = request.points;
// Check against live, maybe a response isnt necessary // Check against live, maybe a response isn't necessary
return this.eventOutputHolder.getOutput(sessionId); return this.eventOutputHolder.getOutput(sessionId);
} }
@ -915,17 +1082,21 @@ export class HideoutController
* @param pmcData Profile to improve area in * @param pmcData Profile to improve area in
* @param request Improve area request data * @param request Improve area request data
*/ */
public improveArea(sessionId: string, pmcData: IPmcData, request: IHideoutImproveAreaRequestData): IItemEventRouterResponse public improveArea(
sessionId: string,
pmcData: IPmcData,
request: IHideoutImproveAreaRequestData,
): IItemEventRouterResponse
{ {
const output = this.eventOutputHolder.getOutput(sessionId); const output = this.eventOutputHolder.getOutput(sessionId);
// Create mapping of required item with corrisponding item from player inventory // Create mapping of required item with corresponding 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 { return {
inventoryItem: item, inventoryItem: item,
requestedItem: reqItem requestedItem: reqItem,
}; };
}); });
@ -934,14 +1105,18 @@ export class HideoutController
{ {
if (!item.inventoryItem) if (!item.inventoryItem)
{ {
this.logger.error(this.localisationService.getText("hideout-unable_to_find_item_in_inventory", item.requestedItem.id)); this.logger.error(
this.localisationService.getText("hideout-unable_to_find_item_in_inventory", item.requestedItem.id),
);
return this.httpResponse.appendErrorToOutput(output); return this.httpResponse.appendErrorToOutput(output);
} }
if (this.paymentHelper.isMoneyTpl(item.inventoryItem._tpl) if (
&& item.inventoryItem.upd this.paymentHelper.isMoneyTpl(item.inventoryItem._tpl) &&
&& item.inventoryItem.upd.StackObjectsCount item.inventoryItem.upd &&
&& item.inventoryItem.upd.StackObjectsCount > item.requestedItem.count) item.inventoryItem.upd.StackObjectsCount &&
item.inventoryItem.upd.StackObjectsCount > item.requestedItem.count
)
{ {
item.inventoryItem.upd.StackObjectsCount -= item.requestedItem.count; item.inventoryItem.upd.StackObjectsCount -= item.requestedItem.count;
} }
@ -951,21 +1126,23 @@ 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.localisationService.getText("hideout-unable_to_find_area_in_database", request.areaType)); this.logger.error(
this.localisationService.getText("hideout-unable_to_find_area_in_database", request.areaType),
);
return this.httpResponse.appendErrorToOutput(output); return this.httpResponse.appendErrorToOutput(output);
} }
// Add all improvemets to output object // Add all improvements to output object
const improvements = hideoutDbData.stages[profileHideoutArea.level].improvements; const improvements = hideoutDbData.stages[profileHideoutArea.level].improvements;
const timestamp = this.timeUtil.getTimestamp(); const timestamp = this.timeUtil.getTimestamp();
for (const improvement of improvements) for (const improvement of improvements)
@ -975,7 +1152,10 @@ export class HideoutController
output.profileChanges[sessionId].improvements = {}; output.profileChanges[sessionId].improvements = {};
} }
const improvementDetails = {completed: false, improveCompleteTimestamp: timestamp + improvement.improvementTime}; const improvementDetails = {
completed: false,
improveCompleteTimestamp: timestamp + improvement.improvementTime,
};
output.profileChanges[sessionId].improvements[improvement.id] = improvementDetails; output.profileChanges[sessionId].improvements[improvement.id] = improvementDetails;
pmcData.Hideout.Improvement[improvement.id] = improvementDetails; pmcData.Hideout.Improvement[improvement.id] = improvementDetails;
} }
@ -990,7 +1170,11 @@ export class HideoutController
* @param request Cancel production request data * @param request Cancel production request data
* @returns IItemEventRouterResponse * @returns IItemEventRouterResponse
*/ */
public cancelProduction(sessionId: string, pmcData: IPmcData, request: IHideoutCancelProductionRequestData): IItemEventRouterResponse public cancelProduction(
sessionId: string,
pmcData: IPmcData,
request: IHideoutCancelProductionRequestData,
): IItemEventRouterResponse
{ {
const output = this.eventOutputHolder.getOutput(sessionId); const output = this.eventOutputHolder.getOutput(sessionId);
@ -1006,7 +1190,7 @@ export class HideoutController
// Null out production data so client gets informed when response send back // Null out production data so client gets informed when response send back
pmcData.Hideout.Production[request.recipeId] = null; pmcData.Hideout.Production[request.recipeId] = null;
// TODO - handle timestamp somehow? // TODO: handle timestamp somehow?
return output; return output;
} }

View File

@ -56,7 +56,7 @@ export class InraidController
@inject("InsuranceService") protected insuranceService: InsuranceService, @inject("InsuranceService") protected insuranceService: InsuranceService,
@inject("InRaidHelper") protected inRaidHelper: InRaidHelper, @inject("InRaidHelper") protected inRaidHelper: InRaidHelper,
@inject("ApplicationContext") protected applicationContext: ApplicationContext, @inject("ApplicationContext") protected applicationContext: ApplicationContext,
@inject("ConfigServer") protected configServer: ConfigServer @inject("ConfigServer") protected configServer: ConfigServer,
) )
{ {
this.airdropConfig = this.configServer.getConfig(ConfigTypes.AIRDROP); this.airdropConfig = this.configServer.getConfig(ConfigTypes.AIRDROP);
@ -124,7 +124,12 @@ export class InraidController
// Check for exit status // Check for exit status
this.markOrRemoveFoundInRaidItems(postRaidRequest); this.markOrRemoveFoundInRaidItems(postRaidRequest);
postRaidRequest.profile.Inventory.items = this.itemHelper.replaceIDs(postRaidRequest.profile, postRaidRequest.profile.Inventory.items, serverPmcData.InsuredItems, postRaidRequest.profile.Inventory.fastPanel); postRaidRequest.profile.Inventory.items = this.itemHelper.replaceIDs(
postRaidRequest.profile,
postRaidRequest.profile.Inventory.items,
serverPmcData.InsuredItems,
postRaidRequest.profile.Inventory.fastPanel,
);
this.inRaidHelper.addUpdToMoneyFromRaid(postRaidRequest.profile.Inventory.items); this.inRaidHelper.addUpdToMoneyFromRaid(postRaidRequest.profile.Inventory.items);
// Purge profile of equipment/container items // Purge profile of equipment/container items
@ -146,22 +151,30 @@ 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 = serverPmcData?.Stats.Eft.OverallCounters.Items.find(x => x.Key.includes("UsecRaidRemainKills")); const remainingCounter = serverPmcData?.Stats.Eft.OverallCounters.Items.find((x) =>
x.Key.includes("UsecRaidRemainKills")
);
if (remainingCounter?.Value > 0) if (remainingCounter?.Value > 0)
{ {
remainingCounter.Value --; remainingCounter.Value--;
} }
} }
if (isDead) if (isDead)
{ {
this.pmcChatResponseService.sendKillerResponse(sessionID, serverPmcData, postRaidRequest.profile.Stats.Eft.Aggressor); this.pmcChatResponseService.sendKillerResponse(
sessionID,
serverPmcData,
postRaidRequest.profile.Stats.Eft.Aggressor,
);
this.matchBotDetailsCacheService.clearCache(); this.matchBotDetailsCacheService.clearCache();
serverPmcData = this.performPostRaidActionsWhenDead(postRaidRequest, serverPmcData, sessionID); serverPmcData = this.performPostRaidActionsWhenDead(postRaidRequest, serverPmcData, sessionID);
} }
const victims = postRaidRequest.profile.Stats.Eft.Victims.filter(x => ["sptbear", "sptusec"].includes(x.Role.toLowerCase())); const victims = postRaidRequest.profile.Stats.Eft.Victims.filter((x) =>
["sptbear", "sptusec"].includes(x.Role.toLowerCase())
);
if (victims?.length > 0) if (victims?.length > 0)
{ {
this.pmcChatResponseService.sendVictimResponse(sessionID, victims, serverPmcData); this.pmcChatResponseService.sendVictimResponse(sessionID, victims, serverPmcData);
@ -175,26 +188,37 @@ export class InraidController
/** /**
* Make changes to pmc profile after they've died in raid, * Make changes to pmc profile after they've died in raid,
* Alter bodypart hp, handle insurance, delete inventory items, remove carried quest items * Alter body part hp, handle insurance, delete inventory items, remove carried quest items
* @param postRaidSaveRequest Post-raid save request * @param postRaidSaveRequest Post-raid save request
* @param pmcData Pmc profile * @param pmcData Pmc profile
* @param sessionID Session id * @param sessionID Session id
* @returns Updated profile object * @returns Updated profile object
*/ */
protected performPostRaidActionsWhenDead(postRaidSaveRequest: ISaveProgressRequestData, pmcData: IPmcData, sessionID: string): IPmcData protected performPostRaidActionsWhenDead(
postRaidSaveRequest: ISaveProgressRequestData,
pmcData: IPmcData,
sessionID: string,
): IPmcData
{ {
this.updatePmcHealthPostRaid(postRaidSaveRequest, pmcData); this.updatePmcHealthPostRaid(postRaidSaveRequest, pmcData);
this.inRaidHelper.deleteInventory(pmcData, sessionID); this.inRaidHelper.deleteInventory(pmcData, sessionID);
if (this.inRaidHelper.removeQuestItemsOnDeath()) if (this.inRaidHelper.removeQuestItemsOnDeath())
{ {
// Find and remove the completed condition from profile if player died, otherwise quest is stuck in limbo and quest items cannot be picked up again // Find and remove the completed condition from profile if player died, otherwise quest is stuck in limbo
// and quest items cannot be picked up again
const allQuests = this.questHelper.getQuestsFromDb(); const allQuests = this.questHelper.getQuestsFromDb();
const activeQuestIdsInProfile = pmcData.Quests.filter(x => ![QuestStatus.AvailableForStart, QuestStatus.Success, QuestStatus.Expired].includes(x.status)).map(x => x.qid); const activeQuestIdsInProfile = pmcData.Quests.filter((x) =>
![QuestStatus.AvailableForStart, QuestStatus.Success, QuestStatus.Expired].includes(x.status)
).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
const questAndFindItemConditionId = this.questHelper.getFindItemConditionByQuestItem(questItem, activeQuestIdsInProfile, allQuests); const questAndFindItemConditionId = this.questHelper.getFindItemConditionByQuestItem(
questItem,
activeQuestIdsInProfile,
allQuests,
);
if (questAndFindItemConditionId) if (questAndFindItemConditionId)
{ {
this.profileHelper.removeCompletedQuestConditionFromProfile(pmcData, questAndFindItemConditionId); this.profileHelper.removeCompletedQuestConditionFromProfile(pmcData, questAndFindItemConditionId);
@ -209,7 +233,7 @@ export class InraidController
} }
/** /**
* Adjust player characters bodypart hp post-raid * Adjust player characters body part hp post-raid
* @param postRaidSaveRequest post raid data * @param postRaidSaveRequest post raid data
* @param pmcData player profile * @param pmcData player profile
*/ */
@ -234,13 +258,13 @@ export class InraidController
/** /**
* Reduce body part hp to % of max * Reduce body part hp to % of max
* @param pmcData profile to edit * @param pmcData profile to edit
* @param multipler multipler to apply to max health * @param multiplier multiplier to apply to max health
*/ */
protected reducePmcHealthToPercent(pmcData: IPmcData, multipler: number): void protected reducePmcHealthToPercent(pmcData: IPmcData, multiplier: number): void
{ {
for (const bodyPart of Object.values(pmcData.Health.BodyParts)) for (const bodyPart of Object.values(pmcData.Health.BodyParts))
{ {
(<BodyPartHealth>bodyPart).Health.Current = (<BodyPartHealth>bodyPart).Health.Maximum * multipler; (<BodyPartHealth>bodyPart).Health.Current = (<BodyPartHealth>bodyPart).Health.Maximum * multiplier;
} }
} }
@ -269,7 +293,12 @@ export class InraidController
// Check for exit status // Check for exit status
this.markOrRemoveFoundInRaidItems(postRaidRequest); this.markOrRemoveFoundInRaidItems(postRaidRequest);
postRaidRequest.profile.Inventory.items = this.itemHelper.replaceIDs(postRaidRequest.profile, postRaidRequest.profile.Inventory.items, pmcData.InsuredItems, postRaidRequest.profile.Inventory.fastPanel); postRaidRequest.profile.Inventory.items = this.itemHelper.replaceIDs(
postRaidRequest.profile,
postRaidRequest.profile.Inventory.items,
pmcData.InsuredItems,
postRaidRequest.profile.Inventory.fastPanel,
);
this.inRaidHelper.addUpdToMoneyFromRaid(postRaidRequest.profile.Inventory.items); this.inRaidHelper.addUpdToMoneyFromRaid(postRaidRequest.profile.Inventory.items);
this.handlePostRaidPlayerScavProcess(scavData, sessionID, postRaidRequest, pmcData, isDead); this.handlePostRaidPlayerScavProcess(scavData, sessionID, postRaidRequest, pmcData, isDead);
@ -286,7 +315,6 @@ export class InraidController
{ {
return false; return false;
} }
return profile.ConditionCounters.Counters.length > 0; return profile.ConditionCounters.Counters.length > 0;
} }
@ -294,7 +322,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}`);
@ -303,9 +331,14 @@ export class InraidController
// Post-raid status is enum word e.g. `Started` but pmc quest status is number e.g. 2 // Post-raid status is enum word e.g. `Started` but pmc quest status is number e.g. 2
// Status values mismatch or statusTimers counts mismatch // Status values mismatch or statusTimers counts mismatch
if (quest.status !== <any>QuestStatus[pmcQuest.status] || quest.statusTimers.length !== pmcQuest.statusTimers.length) if (
quest.status !== <any>QuestStatus[pmcQuest.status] ||
quest.statusTimers.length !== pmcQuest.statusTimers.length
)
{ {
this.logger.warning(`Quest: ${quest.qid} found in PMC profile has different status/statustimer. Scav: ${quest.status} vs PMC: ${pmcQuest.status}`); this.logger.warning(
`Quest: ${quest.qid} found in PMC profile has different status/statustimer. Scav: ${quest.status} vs PMC: ${pmcQuest.status}`,
);
pmcQuest.status = <any>QuestStatus[quest.status]; pmcQuest.status = <any>QuestStatus[quest.status];
// Copy status timers over + fix bad enum key for each // Copy status timers over + fix bad enum key for each
@ -324,17 +357,20 @@ export class InraidController
// Loop over all scav counters and add into pmc profile // Loop over all scav counters and add into pmc profile
for (const scavCounter of scavProfile.ConditionCounters.Counters) for (const scavCounter of scavProfile.ConditionCounters.Counters)
{ {
this.logger.warning(`Processing counter: ${scavCounter.id} value:${scavCounter.value} quest:${scavCounter.qid}`); this.logger.warning(
const counterInPmcProfile = pmcProfile.ConditionCounters.Counters.find(x => x.id === scavCounter.id); `Processing counter: ${scavCounter.id} value:${scavCounter.value} quest:${scavCounter.qid}`,
);
const counterInPmcProfile = pmcProfile.ConditionCounters.Counters.find((x) => x.id === scavCounter.id);
if (!counterInPmcProfile) if (!counterInPmcProfile)
{ {
// Doesn't exist yet, push it straight in // Doesn't exist yet, push it straight in
pmcProfile.ConditionCounters.Counters.push(scavCounter); pmcProfile.ConditionCounters.Counters.push(scavCounter);
continue; continue;
} }
this.logger.warning(`Counter id: ${scavCounter.id} already exists in pmc profile! with value: ${counterInPmcProfile.value} for quest: ${counterInPmcProfile.qid}`); this.logger.warning(
`Counter id: ${scavCounter.id} already exists in pmc profile! with value: ${counterInPmcProfile.value} for quest: ${counterInPmcProfile.qid}`,
);
// Only adjust counter value if its changed // Only adjust counter value if its changed
if (counterInPmcProfile.value !== scavCounter.value) if (counterInPmcProfile.value !== scavCounter.value)
@ -363,7 +399,7 @@ export class InraidController
{ {
if (offraidData.exit !== PlayerRaidEndState.SURVIVED) if (offraidData.exit !== PlayerRaidEndState.SURVIVED)
{ {
// Remove FIR status if the player havn't survived // Remove FIR status if the player hasn't survived
offraidData.profile = this.inRaidHelper.removeSpawnedInSessionPropertyFromItems(offraidData.profile); offraidData.profile = this.inRaidHelper.removeSpawnedInSessionPropertyFromItems(offraidData.profile);
} }
} }
@ -376,7 +412,13 @@ export class InraidController
* @param pmcData Pmc profile * @param pmcData Pmc profile
* @param isDead Is player dead * @param isDead Is player dead
*/ */
protected handlePostRaidPlayerScavProcess(scavData: IPmcData, sessionID: string, offraidData: ISaveProgressRequestData, pmcData: IPmcData, isDead: boolean): void protected handlePostRaidPlayerScavProcess(
scavData: IPmcData,
sessionID: string,
offraidData: ISaveProgressRequestData,
pmcData: IPmcData,
isDead: boolean,
): void
{ {
// Update scav profile inventory // Update scav profile inventory
scavData = this.inRaidHelper.setInventory(sessionID, scavData, offraidData.profile); scavData = this.inRaidHelper.setInventory(sessionID, scavData, offraidData.profile);
@ -411,7 +453,10 @@ export class InraidController
let fenceStanding = Number(pmcData.TradersInfo[fenceId].standing); let fenceStanding = Number(pmcData.TradersInfo[fenceId].standing);
this.logger.debug(`Old fence standing: ${fenceStanding}`); this.logger.debug(`Old fence standing: ${fenceStanding}`);
fenceStanding = this.inRaidHelper.calculateFenceStandingChangeFromKills(fenceStanding, offraidData.profile.Stats.Eft.Victims); fenceStanding = this.inRaidHelper.calculateFenceStandingChangeFromKills(
fenceStanding,
offraidData.profile.Stats.Eft.Victims,
);
// Successful extract with scav adds 0.01 standing // Successful extract with scav adds 0.01 standing
if (offraidData.exit === PlayerRaidEndState.SURVIVED) if (offraidData.exit === PlayerRaidEndState.SURVIVED)

View File

@ -10,7 +10,7 @@ import { IGetInsuranceCostRequestData } from "@spt-aki/models/eft/insurance/IGet
import { IGetInsuranceCostResponseData } from "@spt-aki/models/eft/insurance/IGetInsuranceCostResponseData"; import { IGetInsuranceCostResponseData } from "@spt-aki/models/eft/insurance/IGetInsuranceCostResponseData";
import { IInsureRequestData } from "@spt-aki/models/eft/insurance/IInsureRequestData"; import { IInsureRequestData } from "@spt-aki/models/eft/insurance/IInsureRequestData";
import { IItemEventRouterResponse } from "@spt-aki/models/eft/itemEvent/IItemEventRouterResponse"; import { IItemEventRouterResponse } from "@spt-aki/models/eft/itemEvent/IItemEventRouterResponse";
import { ISystemData, Insurance } from "@spt-aki/models/eft/profile/IAkiProfile"; import { Insurance, ISystemData } from "@spt-aki/models/eft/profile/IAkiProfile";
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 { MessageType } from "@spt-aki/models/enums/MessageType"; import { MessageType } from "@spt-aki/models/enums/MessageType";
@ -24,9 +24,9 @@ import { SaveServer } from "@spt-aki/servers/SaveServer";
import { InsuranceService } from "@spt-aki/services/InsuranceService"; import { InsuranceService } from "@spt-aki/services/InsuranceService";
import { MailSendService } from "@spt-aki/services/MailSendService"; import { MailSendService } from "@spt-aki/services/MailSendService";
import { PaymentService } from "@spt-aki/services/PaymentService"; import { PaymentService } from "@spt-aki/services/PaymentService";
import { MathUtil } from "@spt-aki/utils/MathUtil";
import { RandomUtil } from "@spt-aki/utils/RandomUtil"; import { RandomUtil } from "@spt-aki/utils/RandomUtil";
import { TimeUtil } from "@spt-aki/utils/TimeUtil"; import { TimeUtil } from "@spt-aki/utils/TimeUtil";
import { MathUtil } from "@spt-aki/utils/MathUtil";
@injectable() @injectable()
export class InsuranceController export class InsuranceController
@ -48,7 +48,7 @@ export class InsuranceController
@inject("PaymentService") protected paymentService: PaymentService, @inject("PaymentService") protected paymentService: PaymentService,
@inject("InsuranceService") protected insuranceService: InsuranceService, @inject("InsuranceService") protected insuranceService: InsuranceService,
@inject("MailSendService") protected mailSendService: MailSendService, @inject("MailSendService") protected mailSendService: MailSendService,
@inject("ConfigServer") protected configServer: ConfigServer @inject("ConfigServer") protected configServer: ConfigServer,
) )
{ {
this.insuranceConfig = this.configServer.getConfig(ConfigTypes.INSURANCE); this.insuranceConfig = this.configServer.getConfig(ConfigTypes.INSURANCE);
@ -58,7 +58,7 @@ export class InsuranceController
* Process insurance items of all profiles prior to being given back to the player through the mail service. * Process insurance items of all profiles prior to being given back to the player through the mail service.
* *
* @returns void * @returns void
*/ */
public processReturn(): void public processReturn(): void
{ {
// Process each installed profile. // Process each installed profile.
@ -72,7 +72,7 @@ export class InsuranceController
* Process insurance items of a single profile prior to being given back to the player through the mail service. * Process insurance items of a single profile prior to being given back to the player through the mail service.
* *
* @returns void * @returns void
*/ */
public processReturnByProfile(sessionID: string): void public processReturnByProfile(sessionID: string): void
{ {
// Filter out items that don't need to be processed yet. // Filter out items that don't need to be processed yet.
@ -117,7 +117,11 @@ export class InsuranceController
*/ */
protected processInsuredItems(insuranceDetails: Insurance[], sessionID: string): void protected processInsuredItems(insuranceDetails: Insurance[], sessionID: string): void
{ {
this.logger.debug(`Processing ${insuranceDetails.length} insurance packages, which includes a total of ${this.countAllInsuranceItems(insuranceDetails)} items, in profile ${sessionID}`); this.logger.debug(
`Processing ${insuranceDetails.length} insurance packages, which includes a total of ${
this.countAllInsuranceItems(insuranceDetails)
} items, in profile ${sessionID}`,
);
// Iterate over each of the insurance packages. // Iterate over each of the insurance packages.
for (const insured of insuranceDetails) for (const insured of insuranceDetails)
@ -146,7 +150,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));
} }
/** /**
@ -159,13 +163,15 @@ export class InsuranceController
protected removeInsurancePackageFromProfile(sessionID: string, packageInfo: ISystemData): void protected removeInsurancePackageFromProfile(sessionID: string, packageInfo: ISystemData): 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) =>
insurance.messageContent.systemData.date !== packageInfo.date || insurance.messageContent.systemData.date !== packageInfo.date ||
insurance.messageContent.systemData.time !== packageInfo.time || insurance.messageContent.systemData.time !== packageInfo.time ||
insurance.messageContent.systemData.location !== packageInfo.location insurance.messageContent.systemData.location !== packageInfo.location
); );
this.logger.debug(`Removed insurance package with date: ${packageInfo.date}, time: ${packageInfo.time}, and location: ${packageInfo.location} from profile ${sessionID}. Remaining packages: ${profile.insurance.length}`); this.logger.debug(
`Removed insurance package with date: ${packageInfo.date}, time: ${packageInfo.time}, and location: ${packageInfo.location} from profile ${sessionID}. Remaining packages: ${profile.insurance.length}`,
);
} }
/** /**
@ -184,7 +190,9 @@ export class InsuranceController
const parentAttachmentsMap = this.populateParentAttachmentsMap(insured, itemsMap); const parentAttachmentsMap = this.populateParentAttachmentsMap(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 => !this.itemHelper.isAttachmentAttached(item)); const hasRegularItems = Array.from(itemsMap.values()).some((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.
if (hasRegularItems) if (hasRegularItems)
@ -238,12 +246,14 @@ 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 !== this.fetchHideoutItemParent(insured.items)) if (!parentItem && insuredItem.parentId !== this.fetchHideoutItemParent(insured.items))
{ {
this.logger.warning(`Could not find parent for insured item - ID: ${insuredItem._id}, Template: ${insuredItem._tpl}, Parent ID: ${insuredItem.parentId}`); this.logger.warning(
`Could not find parent for insured item - ID: ${insuredItem._id}, Template: ${insuredItem._tpl}, Parent ID: ${insuredItem.parentId}`,
);
continue; continue;
} }
@ -261,7 +271,9 @@ export class InsuranceController
if (!mainParent) if (!mainParent)
{ {
// Odd. The parent couldn't be found. Skip this attachment and warn. // Odd. The parent couldn't be found. Skip this attachment and warn.
this.logger.warning(`Could not find main-parent for insured attachment - ID: ${insuredItem._id}, Template: ${insuredItem._tpl}, Parent ID: ${insuredItem.parentId}`); this.logger.warning(
`Could not find main-parent for insured attachment - ID: ${insuredItem._id}, Template: ${insuredItem._tpl}, Parent ID: ${insuredItem.parentId}`,
);
continue; continue;
} }
@ -310,8 +322,10 @@ export class InsuranceController
// Check if the item has any children and mark those for deletion as well, but only if those // Check if the item has any children and mark those for deletion as well, but only if those
// children are currently attached attachments. // children are currently attached attachments.
const directChildren = insured.items.filter(item => item.parentId === insuredItem._id); const directChildren = insured.items.filter((item) => item.parentId === insuredItem._id);
const allChildrenAreAttachments = directChildren.every(child => this.itemHelper.isAttachmentAttached(child)); const allChildrenAreAttachments = directChildren.every((child) =>
this.itemHelper.isAttachmentAttached(child)
);
if (allChildrenAreAttachments) if (allChildrenAreAttachments)
{ {
for (const item of itemAndChildren) for (const item of itemAndChildren)
@ -331,9 +345,14 @@ export class InsuranceController
* @param traderId The trader ID from the Insurance object. * @param traderId The trader ID from the Insurance object.
* @param toDelete A Set object to keep track of items marked for deletion. * @param toDelete A Set object to keep track of items marked for deletion.
*/ */
protected processAttachments(mainParentToAttachmentsMap: Map<string, Item[]>, itemsMap: Map<string, Item>, traderId: string, toDelete: Set<string>): void protected processAttachments(
mainParentToAttachmentsMap: Map<string, Item[]>,
itemsMap: Map<string, Item>,
traderId: string,
toDelete: Set<string>,
): void
{ {
for (const [ parentId, attachmentItems ] of mainParentToAttachmentsMap) for (const [parentId, attachmentItems] of mainParentToAttachmentsMap)
{ {
// Log the parent item's name. // Log the parent item's name.
const parentItem = itemsMap.get(parentId); const parentItem = itemsMap.get(parentId);
@ -375,10 +394,10 @@ export class InsuranceController
*/ */
protected sortAttachmentsByPrice(attachments: Item[]): EnrichedItem[] protected sortAttachmentsByPrice(attachments: Item[]): EnrichedItem[]
{ {
return attachments.map(item => ({ return attachments.map((item) => ({
...item, ...item,
name: this.itemHelper.getItemName(item._tpl), name: this.itemHelper.getItemName(item._tpl),
maxPrice: this.itemHelper.getItemMaxPrice(item._tpl) maxPrice: this.itemHelper.getItemMaxPrice(item._tpl),
})).sort((a, b) => b.maxPrice - a.maxPrice); })).sort((a, b) => b.maxPrice - a.maxPrice);
} }
@ -389,7 +408,7 @@ export class InsuranceController
*/ */
protected logAttachmentsDetails(attachments: EnrichedItem[]): void protected logAttachmentsDetails(attachments: EnrichedItem[]): void
{ {
for ( const attachment of attachments) for (const attachment of attachments)
{ {
this.logger.debug(`Child Item - Name: ${attachment.name}, Max Price: ${attachment.maxPrice}`); this.logger.debug(`Child Item - Name: ${attachment.name}, Max Price: ${attachment.maxPrice}`);
} }
@ -404,7 +423,7 @@ export class InsuranceController
*/ */
protected countSuccessfulRolls(attachments: Item[], traderId: string): number protected countSuccessfulRolls(attachments: Item[], traderId: string): number
{ {
const rolls = Array.from({ length: attachments.length }, () => this.rollForDelete(traderId)); const rolls = Array.from({length: attachments.length}, () => this.rollForDelete(traderId));
return rolls.filter(Boolean).length; return rolls.filter(Boolean).length;
} }
@ -415,16 +434,20 @@ export class InsuranceController
* @param successfulRolls The number of successful rolls. * @param successfulRolls The number of successful rolls.
* @param toDelete The array that accumulates the IDs of the items to be deleted. * @param toDelete The array that accumulates the IDs of the items to be deleted.
*/ */
protected attachmentDeletionByValue(attachments: EnrichedItem[], successfulRolls: number, toDelete: Set<string>): void protected attachmentDeletionByValue(
attachments: EnrichedItem[],
successfulRolls: number,
toDelete: Set<string>,
): void
{ {
const valuableToDelete = attachments.slice(0, successfulRolls).map(({ _id }) => _id); const valuableToDelete = attachments.slice(0, successfulRolls).map(({_id}) => _id);
for (const attachmentsId of valuableToDelete) for (const attachmentsId of valuableToDelete)
{ {
const valuableChild = attachments.find(({ _id }) => _id === attachmentsId); const valuableChild = attachments.find(({_id}) => _id === attachmentsId);
if (valuableChild) if (valuableChild)
{ {
const { name, maxPrice } = valuableChild; const {name, maxPrice} = valuableChild;
this.logger.debug(`Marked for removal - Child Item: ${name}, Max Price: ${maxPrice}`); this.logger.debug(`Marked for removal - Child Item: ${name}, Max Price: ${maxPrice}`);
toDelete.add(attachmentsId); toDelete.add(attachmentsId);
} }
@ -440,7 +463,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));
} }
/** /**
@ -457,7 +480,7 @@ export class InsuranceController
for (const item of insured.items) for (const item of insured.items)
{ {
// Check if the item's parent exists in the insured items list. // Check if the item's parent exists in the insured items list.
const parentExists = insured.items.some(parentItem => parentItem._id === item.parentId); const parentExists = insured.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. // If the parent does not exist and the item is not already a 'hideout' item, adopt the orphaned item.
if (!parentExists && item.parentId !== hideoutParentId && item.slotId !== "hideout") if (!parentExists && item.parentId !== hideoutParentId && item.slotId !== "hideout")
@ -478,7 +501,7 @@ export class InsuranceController
*/ */
protected fetchHideoutItemParent(items: Item[]): string protected fetchHideoutItemParent(items: Item[]): string
{ {
const hideoutItem = items.find(item => item.slotId === "hideout"); const hideoutItem = items.find((item) => item.slotId === "hideout");
const hideoutParentId = hideoutItem ? hideoutItem?.parentId : ""; const hideoutParentId = hideoutItem ? hideoutItem?.parentId : "";
if (hideoutParentId === "") if (hideoutParentId === "")
@ -502,7 +525,8 @@ export class InsuranceController
// successfully "failed" to return anything and an appropriate message should be sent to the player. // successfully "failed" to return anything and an appropriate message should be sent to the player.
if (insurance.items.length === 0) if (insurance.items.length === 0)
{ {
const insuranceFailedTemplates = this.databaseServer.getTables().traders[insurance.traderId].dialogue.insuranceFailed; const insuranceFailedTemplates =
this.databaseServer.getTables().traders[insurance.traderId].dialogue.insuranceFailed;
insurance.messageContent.templateId = this.randomUtil.getArrayValue(insuranceFailedTemplates); insurance.messageContent.templateId = this.randomUtil.getArrayValue(insuranceFailedTemplates);
} }
@ -514,7 +538,7 @@ export class InsuranceController
insurance.messageContent.templateId, insurance.messageContent.templateId,
insurance.items, insurance.items,
insurance.messageContent.maxStorageTime, insurance.messageContent.maxStorageTime,
insurance.messageContent.systemData insurance.messageContent.systemData,
); );
} }
@ -544,7 +568,9 @@ export class InsuranceController
// Log the roll with as much detail as possible. // Log the roll with as much detail as possible.
const itemName = insuredItem ? ` for "${this.itemHelper.getItemName(insuredItem._tpl)}"` : ""; const itemName = insuredItem ? ` for "${this.itemHelper.getItemName(insuredItem._tpl)}"` : "";
const status = roll ? "Delete" : "Keep"; const status = roll ? "Delete" : "Keep";
this.logger.debug(`Rolling deletion${itemName} with ${trader} - Return ${traderReturnChance}% - Roll: ${returnChance} - Status: ${status}`); this.logger.debug(
`Rolling deletion${itemName} with ${trader} - Return ${traderReturnChance}% - Roll: ${returnChance} - Status: ${status}`,
);
return roll; return roll;
} }
@ -575,21 +601,18 @@ export class InsuranceController
{ {
itemsToPay.push({ itemsToPay.push({
id: inventoryItemsHash[key]._id, id: inventoryItemsHash[key]._id,
count: Math.round(this.insuranceService.getPremium(pmcData, inventoryItemsHash[key], body.tid)) count: Math.round(this.insuranceService.getPremium(pmcData, inventoryItemsHash[key], body.tid)),
}); });
} }
const options: IProcessBuyTradeRequestData = { const options: IProcessBuyTradeRequestData = {
// eslint-disable-next-line @typescript-eslint/naming-convention
scheme_items: itemsToPay, scheme_items: itemsToPay,
tid: body.tid, tid: body.tid,
Action: "", Action: "",
type: "", type: "",
// eslint-disable-next-line @typescript-eslint/naming-convention
item_id: "", item_id: "",
count: 0, count: 0,
// eslint-disable-next-line @typescript-eslint/naming-convention scheme_id: 0,
scheme_id: 0
}; };
// pay for the item insurance // pay for the item insurance
@ -604,7 +627,7 @@ export class InsuranceController
{ {
pmcData.InsuredItems.push({ pmcData.InsuredItems.push({
tid: body.tid, tid: body.tid,
itemId: inventoryItemsHash[key]._id itemId: inventoryItemsHash[key]._id,
}); });
} }
@ -645,7 +668,9 @@ export class InsuranceController
this.logger.debug(`Item with id: ${itemId} missing from player inventory, skipping`); this.logger.debug(`Item with id: ${itemId} missing from player inventory, skipping`);
continue; continue;
} }
items[inventoryItemsHash[itemId]._tpl] = Math.round(this.insuranceService.getPremium(pmcData, inventoryItemsHash[itemId], trader)); items[inventoryItemsHash[itemId]._tpl] = Math.round(
this.insuranceService.getPremium(pmcData, inventoryItemsHash[itemId], trader),
);
} }
output[trader] = items; output[trader] = items;

View File

@ -62,21 +62,25 @@ export class InventoryController
@inject("LocalisationService") protected localisationService: LocalisationService, @inject("LocalisationService") protected localisationService: LocalisationService,
@inject("LootGenerator") protected lootGenerator: LootGenerator, @inject("LootGenerator") protected lootGenerator: LootGenerator,
@inject("EventOutputHolder") protected eventOutputHolder: EventOutputHolder, @inject("EventOutputHolder") protected eventOutputHolder: EventOutputHolder,
@inject("HttpResponseUtil") protected httpResponseUtil: HttpResponseUtil @inject("HttpResponseUtil") protected httpResponseUtil: HttpResponseUtil,
) )
{} {}
/** /**
* Move Item * Move Item
* change location of item with parentId and slotId * change location of item with parentId and slotId
* transfers items from one profile to another if fromOwner/toOwner is set in the body. * transfers items from one profile to another if fromOwner/toOwner is set in the body.
* otherwise, move is contained within the same profile_f. * otherwise, move is contained within the same profile_f.
* @param pmcData Profile * @param pmcData Profile
* @param moveRequest Move request data * @param moveRequest Move request data
* @param sessionID Session id * @param sessionID Session id
* @returns IItemEventRouterResponse * @returns IItemEventRouterResponse
*/ */
public moveItem(pmcData: IPmcData, moveRequest: IInventoryMoveRequestData, sessionID: string): IItemEventRouterResponse public moveItem(
pmcData: IPmcData,
moveRequest: IInventoryMoveRequestData,
sessionID: string,
): IItemEventRouterResponse
{ {
const output = this.eventOutputHolder.getOutput(sessionID); const output = this.eventOutputHolder.getOutput(sessionID);
@ -89,14 +93,14 @@ export class InventoryController
const ownerInventoryItems = this.inventoryHelper.getOwnerInventoryItems(moveRequest, sessionID); const ownerInventoryItems = this.inventoryHelper.getOwnerInventoryItems(moveRequest, sessionID);
if (ownerInventoryItems.sameInventory) if (ownerInventoryItems.sameInventory)
{ {
// Dont move items from trader to profile, this can happen when editing a traders preset weapons // Don't move items from trader to profile, this can happen when editing a traders preset weapons
if (moveRequest.fromOwner?.type === "Trader" && !ownerInventoryItems.isMail) if (moveRequest.fromOwner?.type === "Trader" && !ownerInventoryItems.isMail)
{ {
return this.getTraderExploitErrorResponse(output); return this.getTraderExploitErrorResponse(output);
} }
// Check for item in inventory before allowing internal transfer // Check for item in inventory before allowing internal transfer
const originalItemLocation = ownerInventoryItems.from.find(x => x._id === moveRequest.item); const originalItemLocation = ownerInventoryItems.from.find((x) => x._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
@ -123,14 +127,23 @@ export class InventoryController
*/ */
protected getTraderExploitErrorResponse(output: IItemEventRouterResponse): IItemEventRouterResponse protected getTraderExploitErrorResponse(output: IItemEventRouterResponse): IItemEventRouterResponse
{ {
return this.httpResponseUtil.appendErrorToOutput(output, this.localisationService.getText("inventory-edit_trader_item"), <BackendErrorCodes>228); return this.httpResponseUtil.appendErrorToOutput(
output,
this.localisationService.getText("inventory-edit_trader_item"),
<BackendErrorCodes>228,
);
} }
/** /**
* Remove Item from Profile * Remove Item from Profile
* Deep tree item deletion, also removes items from insurance list * Deep tree item deletion, also removes items from insurance list
*/ */
public removeItem(pmcData: IPmcData, itemId: string, sessionID: string, output: IItemEventRouterResponse = undefined): IItemEventRouterResponse public removeItem(
pmcData: IPmcData,
itemId: string,
sessionID: string,
output: IItemEventRouterResponse = undefined,
): IItemEventRouterResponse
{ {
return this.inventoryHelper.removeItem(pmcData, itemId, sessionID, output); return this.inventoryHelper.removeItem(pmcData, itemId, sessionID, output);
} }
@ -140,29 +153,46 @@ export class InventoryController
* Implements functionality "Discard" from Main menu (Stash etc.) * Implements functionality "Discard" from Main menu (Stash etc.)
* Removes item from PMC Profile * Removes item from PMC Profile
*/ */
public discardItem(pmcData: IPmcData, body: IInventoryRemoveRequestData, sessionID: string): IItemEventRouterResponse public discardItem(
pmcData: IPmcData,
body: IInventoryRemoveRequestData,
sessionID: string,
): IItemEventRouterResponse
{ {
if (body.fromOwner?.type === "Mail") if (body.fromOwner?.type === "Mail")
{ {
return this.inventoryHelper.removeItemAndChildrenFromMailRewards(sessionID, body, this.eventOutputHolder.getOutput(sessionID)); return this.inventoryHelper.removeItemAndChildrenFromMailRewards(
sessionID,
body,
this.eventOutputHolder.getOutput(sessionID),
);
} }
const profileToRemoveItemFrom = (!body.fromOwner || body.fromOwner.id === pmcData._id) const profileToRemoveItemFrom = (!body.fromOwner || body.fromOwner.id === pmcData._id) ?
? pmcData pmcData :
: this.profileHelper.getFullProfile(sessionID).characters.scav; this.profileHelper.getFullProfile(sessionID).characters.scav;
return this.inventoryHelper.removeItem(profileToRemoveItemFrom, body.item, sessionID, this.eventOutputHolder.getOutput(sessionID)); return this.inventoryHelper.removeItem(
profileToRemoveItemFrom,
body.item,
sessionID,
this.eventOutputHolder.getOutput(sessionID),
);
} }
/** /**
* Split Item * Split Item
* spliting 1 stack into 2 * splitting 1 stack into 2
* @param pmcData Player profile (unused, getOwnerInventoryItems() gets profile) * @param pmcData Player profile (unused, getOwnerInventoryItems() gets profile)
* @param request Split request * @param request Split request
* @param sessionID Session/player id * @param sessionID Session/player id
* @returns IItemEventRouterResponse * @returns IItemEventRouterResponse
*/ */
public splitItem(pmcData: IPmcData, request: IInventorySplitRequestData, sessionID: string): IItemEventRouterResponse public splitItem(
pmcData: IPmcData,
request: IInventorySplitRequestData,
sessionID: string,
): IItemEventRouterResponse
{ {
const output = this.eventOutputHolder.getOutput(sessionID); const output = this.eventOutputHolder.getOutput(sessionID);
@ -172,15 +202,16 @@ 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
const itemToSplit = inventoryItems.from.find(x => x._id === request.splitItem); // correct one.
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`;
this.logger.error(errorMessage); this.logger.error(errorMessage);
return this.httpResponseUtil.appendErrorToOutput(output, errorMessage); return this.httpResponseUtil.appendErrorToOutput(output, errorMessage);
@ -197,7 +228,7 @@ export class InventoryController
output.profileChanges[sessionID].items.new.push({ output.profileChanges[sessionID].items.new.push({
_id: request.newItem, _id: request.newItem,
_tpl: itemToSplit._tpl, _tpl: itemToSplit._tpl,
upd: updatedUpd upd: updatedUpd,
}); });
// Update player inventory // Update player inventory
@ -207,7 +238,7 @@ export class InventoryController
parentId: request.container.id, parentId: request.container.id,
slotId: request.container.container, slotId: request.container.container,
location: request.container.location, location: request.container.location,
upd: updatedUpd upd: updatedUpd,
}); });
return output; return output;
@ -229,20 +260,20 @@ 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`;
this.logger.error(errorMessage); this.logger.error(errorMessage);
return this.httpResponseUtil.appendErrorToOutput(output, errorMessage); return this.httpResponseUtil.appendErrorToOutput(output, errorMessage);
} }
// 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`;
this.logger.error(errorMessage); this.logger.error(errorMessage);
return this.httpResponseUtil.appendErrorToOutput(output, errorMessage); return this.httpResponseUtil.appendErrorToOutput(output, errorMessage);
@ -250,29 +281,29 @@ export class InventoryController
if (!(destinationItem.upd?.StackObjectsCount)) if (!(destinationItem.upd?.StackObjectsCount))
{ {
// No stackcount on destination, add one // No stack count on destination, add one
destinationItem.upd = { StackObjectsCount: 1 }; destinationItem.upd = {StackObjectsCount: 1};
} }
if (!sourceItem.upd) if (!sourceItem.upd)
{ {
sourceItem.upd = { sourceItem.upd = {
StackObjectsCount: 1 StackObjectsCount: 1,
}; };
} }
else if (!sourceItem.upd.StackObjectsCount) else if (!sourceItem.upd.StackObjectsCount)
{ {
// Items pulled out of raid can have no stackcount if the stack should be 1 // Items pulled out of raid can have no stack count if the stack should be 1
sourceItem.upd.StackObjectsCount = 1; sourceItem.upd.StackObjectsCount = 1;
} }
destinationItem.upd.StackObjectsCount += sourceItem.upd.StackObjectsCount; // Add source stackcount to destination destinationItem.upd.StackObjectsCount += sourceItem.upd.StackObjectsCount; // Add source stack count 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`;
this.logger.error(errorMessage); this.logger.error(errorMessage);
return this.httpResponseUtil.appendErrorToOutput(output, errorMessage); return this.httpResponseUtil.appendErrorToOutput(output, errorMessage);
@ -283,8 +314,8 @@ export class InventoryController
} }
/** /**
* TODO: Adds no data to output to send to client, is this by design? * // TODO: Adds no data to output to send to client, is this by design?
* TODO: should make use of getOwnerInventoryItems(), stack being transferred may not always be on pmc * // TODO: should make use of getOwnerInventoryItems(), stack being transferred may not always be on pmc
* Transfer items from one stack into another while keeping original stack * Transfer items from one stack into another while keeping original stack
* Used to take items from scav inventory into stash or to insert ammo into mags (shotgun ones) and reloading weapon by clicking "Reload" * Used to take items from scav inventory into stash or to insert ammo into mags (shotgun ones) and reloading weapon by clicking "Reload"
* @param pmcData Player profile * @param pmcData Player profile
@ -292,7 +323,11 @@ export class InventoryController
* @param sessionID Session id * @param sessionID Session id
* @returns IItemEventRouterResponse * @returns IItemEventRouterResponse
*/ */
public transferItem(pmcData: IPmcData, body: IInventoryTransferRequestData, sessionID: string): IItemEventRouterResponse public transferItem(
pmcData: IPmcData,
body: IInventoryTransferRequestData,
sessionID: string,
): IItemEventRouterResponse
{ {
const output = this.eventOutputHolder.getOutput(sessionID); const output = this.eventOutputHolder.getOutput(sessionID);
@ -359,7 +394,7 @@ export class InventoryController
} }
else else
{ {
Object.assign(destinationItem, { upd: { StackObjectsCount: 1 } }); Object.assign(destinationItem, {upd: {StackObjectsCount: 1}});
} }
destinationItem.upd.StackObjectsCount = destinationStackCount + body.count; destinationItem.upd.StackObjectsCount = destinationStackCount + body.count;
@ -368,28 +403,28 @@ export class InventoryController
} }
/** /**
* Swap Item * Swap Item
* 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}`);
} }
// to.id is the parentid // to.id is the parentId
itemOne.parentId = request.to.id; itemOne.parentId = request.to.id;
// to.container is the slotid // to.container is the slotId
itemOne.slotId = request.to.container; itemOne.slotId = request.to.container;
// Request object has location data, add it in, otherwise remove existing location from object // Request object has location data, add it in, otherwise remove existing location from object
@ -423,9 +458,11 @@ export class InventoryController
public foldItem(pmcData: IPmcData, body: IInventoryFoldRequestData, sessionID: string): IItemEventRouterResponse public foldItem(pmcData: IPmcData, body: IInventoryFoldRequestData, sessionID: string): IItemEventRouterResponse
{ {
// Fix for folding weapons while on they're in the Scav inventory // Fix for folding weapons while on they're in the Scav inventory
if (body.fromOwner if (
&& body.fromOwner.type === "Profile" body.fromOwner &&
&& body.fromOwner.id !== pmcData._id) body.fromOwner.type === "Profile" &&
body.fromOwner.id !== pmcData._id
)
{ {
pmcData = this.profileHelper.getScavProfile(sessionID); pmcData = this.profileHelper.getScavProfile(sessionID);
} }
@ -434,19 +471,19 @@ export class InventoryController
{ {
if (item._id && item._id === body.item) if (item._id && item._id === body.item)
{ {
item.upd.Foldable = { Folded: body.value }; item.upd.Foldable = {Folded: body.value};
return this.eventOutputHolder.getOutput(sessionID); return this.eventOutputHolder.getOutput(sessionID);
} }
} }
return { return {
warnings: [], warnings: [],
profileChanges: {} profileChanges: {},
}; };
} }
/** /**
* Toggles "Toggleable" items like night vision goggles and face shields. * Toggles "toggleable" items like night vision goggles and face shields.
* @param pmcData player profile * @param pmcData player profile
* @param body Toggle request * @param body Toggle request
* @param sessionID Session id * @param sessionID Session id
@ -460,27 +497,31 @@ export class InventoryController
pmcData = this.profileHelper.getScavProfile(sessionID); pmcData = this.profileHelper.getScavProfile(sessionID);
} }
const itemToToggle = pmcData.Inventory.items.find(x => x._id === body.item); const itemToToggle = pmcData.Inventory.items.find((x) => x._id === body.item);
if (itemToToggle) if (itemToToggle)
{ {
if (!itemToToggle.upd) if (!itemToToggle.upd)
{ {
this.logger.warning(this.localisationService.getText("inventory-item_to_toggle_missing_upd", itemToToggle._id)); this.logger.warning(
this.localisationService.getText("inventory-item_to_toggle_missing_upd", itemToToggle._id),
);
itemToToggle.upd = {}; itemToToggle.upd = {};
} }
itemToToggle.upd.Togglable = { On: body.value }; itemToToggle.upd.Togglable = {On: body.value};
return this.eventOutputHolder.getOutput(sessionID); return this.eventOutputHolder.getOutput(sessionID);
} }
else else
{ {
this.logger.warning(this.localisationService.getText("inventory-unable_to_toggle_item_not_found", body.item)); this.logger.warning(
this.localisationService.getText("inventory-unable_to_toggle_item_not_found", body.item),
);
} }
return { return {
warnings: [], warnings: [],
profileChanges: {} profileChanges: {},
}; };
} }
@ -499,11 +540,11 @@ export class InventoryController
{ {
if ("upd" in item) if ("upd" in item)
{ {
item.upd.Tag = { Color: body.TagColor, Name: body.TagName }; item.upd.Tag = {Color: body.TagColor, Name: body.TagName};
} }
else else
{ {
item.upd = { Tag: { Color: body.TagColor, Name: body.TagName } }; item.upd = {Tag: {Color: body.TagColor, Name: body.TagName}};
} }
return this.eventOutputHolder.getOutput(sessionID); return this.eventOutputHolder.getOutput(sessionID);
@ -512,18 +553,22 @@ export class InventoryController
return { return {
warnings: [], warnings: [],
profileChanges: {} profileChanges: {},
}; };
} }
/** /**
* Bind an inventory item to the quick access menu at bottom of player screen * Bind an inventory item to the quick access menu at bottom of player screen
* @param pmcData Player profile * @param pmcData Player profile
* @param bindRequest Reqeust object * @param bindRequest Request object
* @param sessionID Session id * @param sessionID Session id
* @returns IItemEventRouterResponse * @returns IItemEventRouterResponse
*/ */
public bindItem(pmcData: IPmcData, bindRequest: IInventoryBindRequestData, sessionID: string): IItemEventRouterResponse public bindItem(
pmcData: IPmcData,
bindRequest: IInventoryBindRequestData,
sessionID: string,
): IItemEventRouterResponse
{ {
for (const index in pmcData.Inventory.fastPanel) for (const index in pmcData.Inventory.fastPanel)
{ {
@ -538,7 +583,6 @@ export class InventoryController
return this.eventOutputHolder.getOutput(sessionID); return this.eventOutputHolder.getOutput(sessionID);
} }
/** /**
* Handles examining an item * Handles examining an item
* @param pmcData player profile * @param pmcData player profile
@ -546,7 +590,11 @@ export class InventoryController
* @param sessionID session id * @param sessionID session id
* @returns response * @returns response
*/ */
public examineItem(pmcData: IPmcData, body: IInventoryExamineRequestData, sessionID: string): IItemEventRouterResponse public examineItem(
pmcData: IPmcData,
body: IInventoryExamineRequestData,
sessionID: string,
): IItemEventRouterResponse
{ {
let itemId = ""; let itemId = "";
if ("fromOwner" in body) if ("fromOwner" in body)
@ -606,9 +654,9 @@ export class InventoryController
} }
/** /**
* Get the tplid of an item from the examine request object * Get the tplId of an item from the examine request object
* @param body response request * @param body response request
* @returns tplid * @returns string
*/ */
protected getExaminedItemTpl(body: IInventoryExamineRequestData): string protected getExaminedItemTpl(body: IInventoryExamineRequestData): string
{ {
@ -618,49 +666,55 @@ export class InventoryController
} }
else if (body.fromOwner.id === Traders.FENCE) else if (body.fromOwner.id === Traders.FENCE)
{ {
// get tpl from fence assorts // Get tpl from fence assorts
return this.fenceService.getRawFenceAssorts().items.find(x => x._id === body.item)._tpl; return this.fenceService.getRawFenceAssorts().items.find((x) => x._id === body.item)._tpl;
} }
else if (body.fromOwner.type === "Trader") // not fence else if (body.fromOwner.type === "Trader")
{ {
// get tpl from trader assort // Not fence
return this.databaseServer.getTables().traders[body.fromOwner.id].assort.items.find(item => item._id === body.item)._tpl; // Get tpl from trader assort
return this.databaseServer.getTables().traders[body.fromOwner.id].assort.items.find((item) =>
item._id === body.item
)._tpl;
} }
else if (body.fromOwner.type === "RagFair") else if (body.fromOwner.type === "RagFair")
{ {
// try to get tplid from items.json first // try to get tplId from items.json first
const item = this.databaseServer.getTables().templates.items[body.item]; const item = this.databaseServer.getTables().templates.items[body.item];
if (item) if (item)
{ {
return item._id; return item._id;
} }
// try alternate way of getting offer if first approach fails // Try alternate way of getting offer if first approach fails
let offer = this.ragfairOfferService.getOfferByOfferId(body.item); let offer = this.ragfairOfferService.getOfferByOfferId(body.item);
if (!offer) if (!offer)
{ {
offer = this.ragfairOfferService.getOfferByOfferId(body.fromOwner.id); offer = this.ragfairOfferService.getOfferByOfferId(body.fromOwner.id);
} }
// try find examine item inside offer items array // Try find examine item inside offer items array
const matchingItem = offer.items.find(x => x._id === body.item); const matchingItem = offer.items.find((x) => x._id === body.item);
if (matchingItem) if (matchingItem)
{ {
return matchingItem._tpl; return matchingItem._tpl;
} }
// unable to find item in database or ragfair // Unable to find item in database or ragfair
throw new Error(this.localisationService.getText("inventory-unable_to_find_item", body.item)); throw new Error(this.localisationService.getText("inventory-unable_to_find_item", body.item));
} }
} }
public readEncyclopedia(pmcData: IPmcData, body: IInventoryReadEncyclopediaRequestData, sessionID: string): IItemEventRouterResponse public readEncyclopedia(
pmcData: IPmcData,
body: IInventoryReadEncyclopediaRequestData,
sessionID: string,
): IItemEventRouterResponse
{ {
for (const id of body.ids) for (const id of body.ids)
{ {
pmcData.Encyclopedia[id] = true; pmcData.Encyclopedia[id] = true;
} }
return this.eventOutputHolder.getOutput(sessionID); return this.eventOutputHolder.getOutput(sessionID);
} }
@ -672,15 +726,20 @@ export class InventoryController
* @param sessionID Session id * @param sessionID Session id
* @returns IItemEventRouterResponse * @returns IItemEventRouterResponse
*/ */
public sortInventory(pmcData: IPmcData, request: IInventorySortRequestData, sessionID: string): IItemEventRouterResponse public sortInventory(
pmcData: IPmcData,
request: IInventorySortRequestData,
sessionID: string,
): IItemEventRouterResponse
{ {
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(`Unable to find inventory item: ${change._id} to auto-sort, YOU MUST RELOAD YOUR GAME`); this.logger.error(
`Unable to find inventory item: ${change._id} to auto-sort, YOU MUST RELOAD YOUR GAME`,
);
continue; continue;
} }
@ -707,13 +766,17 @@ export class InventoryController
* @param sessionID Session id * @param sessionID Session id
* @returns IItemEventRouterResponse * @returns IItemEventRouterResponse
*/ */
public createMapMarker(pmcData: IPmcData, request: IInventoryCreateMarkerRequestData, sessionID: string): IItemEventRouterResponse public createMapMarker(
pmcData: IPmcData,
request: IInventoryCreateMarkerRequestData,
sessionID: string,
): IItemEventRouterResponse
{ {
// 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: []};
request.mapMarker.Note = this.sanitiseMapMarkerText(request.mapMarker.Note); request.mapMarker.Note = this.sanitiseMapMarkerText(request.mapMarker.Note);
mapItem.upd.Map.Markers.push(request.mapMarker); mapItem.upd.Map.Markers.push(request.mapMarker);
@ -731,10 +794,14 @@ export class InventoryController
* @param sessionID Session id * @param sessionID Session id
* @returns IItemEventRouterResponse * @returns IItemEventRouterResponse
*/ */
public deleteMapMarker(pmcData: IPmcData, request: IInventoryDeleteMarkerRequestData, sessionID: string): IItemEventRouterResponse public deleteMapMarker(
pmcData: IPmcData,
request: IInventoryDeleteMarkerRequestData,
sessionID: string,
): IItemEventRouterResponse
{ {
// 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) =>
@ -756,13 +823,17 @@ export class InventoryController
* @param sessionID Session id * @param sessionID Session id
* @returns IItemEventRouterResponse * @returns IItemEventRouterResponse
*/ */
public editMapMarker(pmcData: IPmcData, request: IInventoryEditMarkerRequestData, sessionID: string): IItemEventRouterResponse public editMapMarker(
pmcData: IPmcData,
request: IInventoryEditMarkerRequestData,
sessionID: string,
): IItemEventRouterResponse
{ {
// 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;
@ -791,15 +862,19 @@ export class InventoryController
* @param sessionID Session id * @param sessionID Session id
* @returns IItemEventRouterResponse * @returns IItemEventRouterResponse
*/ */
public openRandomLootContainer(pmcData: IPmcData, body: IOpenRandomLootContainerRequestData, sessionID: string): IItemEventRouterResponse public openRandomLootContainer(
pmcData: IPmcData,
body: IOpenRandomLootContainerRequestData,
sessionID: string,
): IItemEventRouterResponse
{ {
const openedItem = pmcData.Inventory.items.find(x => x._id === body.item); const openedItem = pmcData.Inventory.items.find((x) => x._id === body.item);
const containerDetails = this.itemHelper.getItem(openedItem._tpl); const containerDetails = this.itemHelper.getItem(openedItem._tpl);
const isSealedWeaponBox = containerDetails[1]._name.includes("event_container_airdrop"); const isSealedWeaponBox = containerDetails[1]._name.includes("event_container_airdrop");
const newItemRequest: IAddItemRequestData = { const newItemRequest: IAddItemRequestData = {
tid: "RandomLootContainer", tid: "RandomLootContainer",
items: [] items: [],
}; };
let foundInRaid = false; let foundInRaid = false;

View File

@ -30,7 +30,7 @@ export class LauncherController
@inject("DatabaseServer") protected databaseServer: DatabaseServer, @inject("DatabaseServer") protected databaseServer: DatabaseServer,
@inject("LocalisationService") protected localisationService: LocalisationService, @inject("LocalisationService") protected localisationService: LocalisationService,
@inject("PreAkiModLoader") protected preAkiModLoader: PreAkiModLoader, @inject("PreAkiModLoader") protected preAkiModLoader: PreAkiModLoader,
@inject("ConfigServer") protected configServer: ConfigServer @inject("ConfigServer") protected configServer: ConfigServer,
) )
{ {
this.coreConfig = this.configServer.getConfig(ConfigTypes.CORE); this.coreConfig = this.configServer.getConfig(ConfigTypes.CORE);
@ -42,30 +42,26 @@ export class LauncherController
backendUrl: this.httpServerHelper.getBackendUrl(), backendUrl: this.httpServerHelper.getBackendUrl(),
name: this.coreConfig.serverName, name: this.coreConfig.serverName,
editions: Object.keys(this.databaseServer.getTables().templates.profiles), editions: Object.keys(this.databaseServer.getTables().templates.profiles),
profileDescriptions: this.getProfileDescriptions() profileDescriptions: this.getProfileDescriptions(),
}; };
} }
/** /**
* Get descriptive text for each of the profile edtions a player can choose * Get descriptive text for each of the profile editions a player can choose
* @returns * @returns
*/ */
protected getProfileDescriptions(): Record<string, string> protected getProfileDescriptions(): Record<string, string>
{ {
return { return {
/* eslint-disable @typescript-eslint/naming-convention */
Standard: this.localisationService.getText("launcher-profile_standard"), Standard: this.localisationService.getText("launcher-profile_standard"),
// eslint-disable-next-line @typescript-eslint/naming-convention
"Left Behind": this.localisationService.getText("launcher-profile_leftbehind"), "Left Behind": this.localisationService.getText("launcher-profile_leftbehind"),
// eslint-disable-next-line @typescript-eslint/naming-convention
"Prepare To Escape": this.localisationService.getText("launcher-profile_preparetoescape"), "Prepare To Escape": this.localisationService.getText("launcher-profile_preparetoescape"),
// eslint-disable-next-line @typescript-eslint/naming-convention
"Edge Of Darkness": this.localisationService.getText("launcher-edgeofdarkness"), "Edge Of Darkness": this.localisationService.getText("launcher-edgeofdarkness"),
// eslint-disable-next-line @typescript-eslint/naming-convention
"SPT Easy start": this.localisationService.getText("launcher-profile_spteasystart"), "SPT Easy start": this.localisationService.getText("launcher-profile_spteasystart"),
// eslint-disable-next-line @typescript-eslint/naming-convention
"SPT Zero to hero": this.localisationService.getText("launcher-profile_sptzerotohero"), "SPT Zero to hero": this.localisationService.getText("launcher-profile_sptzerotohero"),
// eslint-disable-next-line @typescript-eslint/naming-convention "SPT Developer": this.localisationService.getText("launcher-profile_sptdeveloper"),
"SPT Developer": this.localisationService.getText("launcher-profile_sptdeveloper") /* eslint-enable @typescript-eslint/naming-convention */
}; };
} }
@ -115,12 +111,13 @@ export class LauncherController
username: info.username, username: info.username,
password: info.password, password: info.password,
wipe: true, wipe: true,
edition: info.edition edition: info.edition,
}; };
this.saveServer.createProfile(newProfileDetails); this.saveServer.createProfile(newProfileDetails);
this.saveServer.loadProfile(sessionID); this.saveServer.loadProfile(sessionID);
this.saveServer.saveProfile(sessionID); this.saveServer.saveProfile(sessionID);
return sessionID; return sessionID;
} }

View File

@ -41,7 +41,7 @@ export class LocationController
@inject("LootGenerator") protected lootGenerator: LootGenerator, @inject("LootGenerator") protected lootGenerator: LootGenerator,
@inject("DatabaseServer") protected databaseServer: DatabaseServer, @inject("DatabaseServer") protected databaseServer: DatabaseServer,
@inject("TimeUtil") protected timeUtil: TimeUtil, @inject("TimeUtil") protected timeUtil: TimeUtil,
@inject("ConfigServer") protected configServer: ConfigServer @inject("ConfigServer") protected configServer: ConfigServer,
) )
{ {
this.airdropConfig = this.configServer.getConfig(ConfigTypes.AIRDROP); this.airdropConfig = this.configServer.getConfig(ConfigTypes.AIRDROP);
@ -89,16 +89,22 @@ export class LocationController
const staticLoot = this.locationGenerator.generateStaticContainers(location.base, staticAmmoDist); const staticLoot = this.locationGenerator.generateStaticContainers(location.base, staticAmmoDist);
output.Loot.push(...staticLoot); output.Loot.push(...staticLoot);
// Add dyanmic loot to output loot // Add dynamic loot to output loot
const dynamicLootDist: ILooseLoot = this.jsonUtil.clone(location.looseLoot); const dynamicLootDist: ILooseLoot = this.jsonUtil.clone(location.looseLoot);
const dynamicSpawnPoints: SpawnpointTemplate[] = this.locationGenerator.generateDynamicLoot(dynamicLootDist, staticAmmoDist, name); const dynamicSpawnPoints: SpawnpointTemplate[] = this.locationGenerator.generateDynamicLoot(
dynamicLootDist,
staticAmmoDist,
name,
);
for (const spawnPoint of dynamicSpawnPoints) for (const spawnPoint of dynamicSpawnPoints)
{ {
output.Loot.push(spawnPoint); output.Loot.push(spawnPoint);
} }
// Done generating, log results // Done generating, log results
this.logger.success(this.localisationService.getText("location-dynamic_items_spawned_success", dynamicSpawnPoints.length)); this.logger.success(
this.localisationService.getText("location-dynamic_items_spawned_success", dynamicSpawnPoints.length),
);
this.logger.success(this.localisationService.getText("location-generated_success", name)); this.logger.success(this.localisationService.getText("location-generated_success", name));
return output; return output;
@ -110,7 +116,6 @@ export class LocationController
* @param sessionId Players Id * @param sessionId Players Id
* @returns ILocationsGenerateAllResponse * @returns ILocationsGenerateAllResponse
*/ */
// eslint-disable-next-line @typescript-eslint/no-unused-vars
public generateAll(sessionId: string): ILocationsGenerateAllResponse public generateAll(sessionId: string): ILocationsGenerateAllResponse
{ {
const locationsFromDb = this.databaseServer.getTables().locations; const locationsFromDb = this.databaseServer.getTables().locations;
@ -130,15 +135,15 @@ export class LocationController
locations[mapBase._Id] = mapBase; locations[mapBase._Id] = mapBase;
} }
return { return {
locations: locations, locations: locations,
paths: locationsFromDb.base.paths paths: locationsFromDb.base.paths,
}; };
} }
/** /**
* Handle client/location/getAirdropLoot * Handle client/location/getAirdropLoot
* Get loot for an airdop container * Get loot for an airdrop container
* Generates it randomly based on config/airdrop.json values * Generates it randomly based on config/airdrop.json values
* @returns Array of LootItem objects * @returns Array of LootItem objects
*/ */
@ -174,7 +179,9 @@ export class LocationController
let lootSettingsByType = this.airdropConfig.loot[airdropType]; let lootSettingsByType = this.airdropConfig.loot[airdropType];
if (!lootSettingsByType) if (!lootSettingsByType)
{ {
this.logger.error(this.localisationService.getText("location-unable_to_find_airdrop_drop_config_of_type", airdropType)); this.logger.error(
this.localisationService.getText("location-unable_to_find_airdrop_drop_config_of_type", airdropType),
);
lootSettingsByType = this.airdropConfig.loot[AirdropTypeEnum.MIXED]; lootSettingsByType = this.airdropConfig.loot[AirdropTypeEnum.MIXED];
} }
@ -187,7 +194,7 @@ export class LocationController
itemLimits: lootSettingsByType.itemLimits, itemLimits: lootSettingsByType.itemLimits,
itemStackLimits: lootSettingsByType.itemStackLimits, itemStackLimits: lootSettingsByType.itemStackLimits,
armorLevelWhitelist: lootSettingsByType.armorLevelWhitelist, armorLevelWhitelist: lootSettingsByType.armorLevelWhitelist,
allowBossItems: lootSettingsByType.allowBossItems allowBossItems: lootSettingsByType.allowBossItems,
}; };
} }
} }

View File

@ -56,7 +56,7 @@ export class MatchController
@inject("BotGenerationCacheService") protected botGenerationCacheService: BotGenerationCacheService, @inject("BotGenerationCacheService") protected botGenerationCacheService: BotGenerationCacheService,
@inject("MailSendService") protected mailSendService: MailSendService, @inject("MailSendService") protected mailSendService: MailSendService,
@inject("LootGenerator") protected lootGenerator: LootGenerator, @inject("LootGenerator") protected lootGenerator: LootGenerator,
@inject("ApplicationContext") protected applicationContext: ApplicationContext @inject("ApplicationContext") protected applicationContext: ApplicationContext,
) )
{ {
this.matchConfig = this.configServer.getConfig(ConfigTypes.MATCH); this.matchConfig = this.configServer.getConfig(ConfigTypes.MATCH);
@ -99,12 +99,11 @@ export class MatchController
} }
/** Handle match/group/start_game */ /** Handle match/group/start_game */
// eslint-disable-next-line @typescript-eslint/no-unused-vars
public joinMatch(info: IJoinMatchRequestData, sessionId: string): IJoinMatchResult public joinMatch(info: IJoinMatchRequestData, sessionId: string): IJoinMatchResult
{ {
const output: IJoinMatchResult = { const output: IJoinMatchResult = {
maxPveCountExceeded: false, maxPveCountExceeded: false,
profiles: [] profiles: [],
}; };
// get list of players joining into the match // get list of players joining into the match
@ -120,20 +119,18 @@ export class MatchController
raidMode: "Online", raidMode: "Online",
mode: "deathmatch", mode: "deathmatch",
shortid: null, shortid: null,
// eslint-disable-next-line @typescript-eslint/naming-convention additional_info: null,
additional_info: null
}); });
return output; return output;
} }
/** Handle client/match/group/status */ /** Handle client/match/group/status */
// eslint-disable-next-line @typescript-eslint/no-unused-vars
public getGroupStatus(info: IGetGroupStatusRequestData): any public getGroupStatus(info: IGetGroupStatusRequestData): any
{ {
return { return {
players: [], players: [],
maxPveCountExceeded: false maxPveCountExceeded: false,
}; };
} }
@ -147,12 +144,14 @@ export class MatchController
// Store request data for access during bot generation // Store request data for access during bot generation
this.applicationContext.addValue(ContextVariableType.RAID_CONFIGURATION, request); this.applicationContext.addValue(ContextVariableType.RAID_CONFIGURATION, request);
//TODO: add code to strip PMC of equipment now they've started the raid // TODO: add code to strip PMC of equipment now they've started the raid
// Set pmcs to difficulty set in pre-raid screen if override in bot config isnt enabled // Set PMCs to difficulty set in pre-raid screen if override in bot config isn't enabled
if (!this.pmcConfig.useDifficultyOverride) if (!this.pmcConfig.useDifficultyOverride)
{ {
this.pmcConfig.difficulty = this.convertDifficultyDropdownIntoBotDifficulty(request.wavesSettings.botDifficulty); this.pmcConfig.difficulty = this.convertDifficultyDropdownIntoBotDifficulty(
request.wavesSettings.botDifficulty,
);
} }
// Store the profile as-is for later use on the post-raid exp screen // Store the profile as-is for later use on the post-raid exp screen
@ -235,9 +234,9 @@ export class MatchController
parentId: parentId, parentId: parentId,
upd: { upd: {
StackObjectsCount: item.stackCount, StackObjectsCount: item.stackCount,
SpawnedInSession: true SpawnedInSession: true,
} },
} },
); );
} }
@ -248,7 +247,7 @@ export class MatchController
MessageType.MESSAGE_WITH_ITEMS, MessageType.MESSAGE_WITH_ITEMS,
this.randomUtil.getArrayValue(this.traderConfig.fence.coopExtractGift.messageLocaleIds), this.randomUtil.getArrayValue(this.traderConfig.fence.coopExtractGift.messageLocaleIds),
mailableLoot, mailableLoot,
this.timeUtil.getHoursAsSeconds(this.traderConfig.fence.coopExtractGift.giftExpiryHours) this.timeUtil.getHoursAsSeconds(this.traderConfig.fence.coopExtractGift.giftExpiryHours),
); );
} }
@ -296,7 +295,9 @@ export class MatchController
this.traderHelper.lvlUp(fenceId, pmcData); this.traderHelper.lvlUp(fenceId, pmcData);
pmcData.TradersInfo[fenceId].loyaltyLevel = Math.max(pmcData.TradersInfo[fenceId].loyaltyLevel, 1); pmcData.TradersInfo[fenceId].loyaltyLevel = Math.max(pmcData.TradersInfo[fenceId].loyaltyLevel, 1);
this.logger.debug(`Car extract: ${extractName} used, total times taken: ${pmcData.CarExtractCounts[extractName]}`); this.logger.debug(
`Car extract: ${extractName} used, total times taken: ${pmcData.CarExtractCounts[extractName]}`,
);
} }
/** /**
@ -309,8 +310,8 @@ export class MatchController
{ {
let fenceStanding = Number(pmcData.TradersInfo[fenceId].standing); let fenceStanding = Number(pmcData.TradersInfo[fenceId].standing);
// Not exact replica of Live behaviour // Not exact replica of Live behaviour... Simplified for now. No real reason to do the whole (unconfirmed)
// Simplified for now, no real reason to do the whole (unconfirmed) extra 0.01 standing per day regeneration mechanic // extra 0.01 standing per day regeneration mechanic.
const baseGain: number = this.inraidConfig.carExtractBaseStandingGain; const baseGain: number = this.inraidConfig.carExtractBaseStandingGain;
const extractCount: number = pmcData.CarExtractCounts[extractName]; const extractCount: number = pmcData.CarExtractCounts[extractName];

View File

@ -10,15 +10,15 @@ import { EventOutputHolder } from "@spt-aki/routers/EventOutputHolder";
export class NoteController export class NoteController
{ {
constructor( constructor(
@inject("EventOutputHolder") protected eventOutputHolder: EventOutputHolder @inject("EventOutputHolder") protected eventOutputHolder: EventOutputHolder,
) )
{ } {}
public addNote(pmcData: IPmcData, body: INoteActionData, sessionID: string): IItemEventRouterResponse public addNote(pmcData: IPmcData, body: INoteActionData, sessionID: string): IItemEventRouterResponse
{ {
const newNote: Note = { const newNote: Note = {
Time: body.note.Time, Time: body.note.Time,
Text: body.note.Text Text: body.note.Text,
}; };
pmcData.Notes.Notes.push(newNote); pmcData.Notes.Notes.push(newNote);

View File

@ -14,7 +14,7 @@ export class NotifierController
constructor( constructor(
@inject("NotifierHelper") protected notifierHelper: NotifierHelper, @inject("NotifierHelper") protected notifierHelper: NotifierHelper,
@inject("HttpServerHelper") protected httpServerHelper: HttpServerHelper, @inject("HttpServerHelper") protected httpServerHelper: HttpServerHelper,
@inject("NotificationService") protected notificationService: NotificationService @inject("NotificationService") protected notificationService: NotificationService,
) )
{} {}
@ -82,11 +82,10 @@ export class NotifierController
{ {
return { return {
server: this.httpServerHelper.buildUrl(), server: this.httpServerHelper.buildUrl(),
// eslint-disable-next-line @typescript-eslint/naming-convention
channel_id: sessionID, channel_id: sessionID,
url: "", url: "",
notifierServer: this.getServer(sessionID), notifierServer: this.getServer(sessionID),
ws: this.notifierHelper.getWebSocketServer(sessionID) ws: this.notifierHelper.getWebSocketServer(sessionID),
}; };
} }
} }

View File

@ -23,9 +23,9 @@ export class PresetBuildController
@inject("JsonUtil") protected jsonUtil: JsonUtil, @inject("JsonUtil") protected jsonUtil: JsonUtil,
@inject("DatabaseServer") protected databaseServer: DatabaseServer, @inject("DatabaseServer") protected databaseServer: DatabaseServer,
@inject("ItemHelper") protected itemHelper: ItemHelper, @inject("ItemHelper") protected itemHelper: ItemHelper,
@inject("SaveServer") protected saveServer: SaveServer @inject("SaveServer") protected saveServer: SaveServer,
) )
{ } {}
/** Handle client/handbook/builds/my/list */ /** Handle client/handbook/builds/my/list */
public getUserBuilds(sessionID: string): IUserBuilds public getUserBuilds(sessionID: string): IUserBuilds
@ -35,21 +35,27 @@ export class PresetBuildController
{ {
profile.userbuilds = { profile.userbuilds = {
equipmentBuilds: [], equipmentBuilds: [],
weaponBuilds: [] weaponBuilds: [],
}; };
} }
// Ensure the secure container in the default presets match what the player has equipped // Ensure the secure container in the default presets match what the player has equipped
const defaultEquipmentPresets = this.jsonUtil.clone(this.databaseServer.getTables().templates.defaultEquipmentPresets); const defaultEquipmentPresets = this.jsonUtil.clone(
const playerSecureContainer = profile.characters.pmc.Inventory.items?.find(x => x.slotId === "SecuredContainer"); this.databaseServer.getTables().templates.defaultEquipmentPresets,
const firstDefaultItemsSecureContainer = defaultEquipmentPresets[0]?.items?.find(x => x.slotId === "SecuredContainer"); );
const playerSecureContainer = profile.characters.pmc.Inventory.items?.find((x) =>
x.slotId === "SecuredContainer"
);
const firstDefaultItemsSecureContainer = defaultEquipmentPresets[0]?.items?.find((x) =>
x.slotId === "SecuredContainer"
);
if (playerSecureContainer && playerSecureContainer?._tpl !== firstDefaultItemsSecureContainer?._tpl) if (playerSecureContainer && playerSecureContainer?._tpl !== firstDefaultItemsSecureContainer?._tpl)
{ {
// Default equipment presets' secure container tpl doesnt match players secure container tpl // Default equipment presets' secure container tpl doesn't match players secure container tpl
for (const defaultPreset of defaultEquipmentPresets) for (const defaultPreset of defaultEquipmentPresets)
{ {
// Find presets secure container // Find presets secure container
const secureContainer = defaultPreset.items.find(x => x.slotId === "SecuredContainer"); const secureContainer = defaultPreset.items.find((x) => x.slotId === "SecuredContainer");
if (secureContainer) if (secureContainer)
{ {
secureContainer._tpl = playerSecureContainer._tpl; secureContainer._tpl = playerSecureContainer._tpl;
@ -65,9 +71,13 @@ export class PresetBuildController
} }
/** Handle SaveWeaponBuild event */ /** Handle SaveWeaponBuild event */
public saveWeaponBuild(pmcData: IPmcData, body: IPresetBuildActionRequestData, sessionId: string): IItemEventRouterResponse public saveWeaponBuild(
pmcData: IPmcData,
body: IPresetBuildActionRequestData,
sessionId: string,
): IItemEventRouterResponse
{ {
// TODO - could be merged into saveBuild, maybe // TODO: Could be merged into saveBuild, maybe
const output = this.eventOutputHolder.getOutput(sessionId); const output = this.eventOutputHolder.getOutput(sessionId);
// Replace duplicate Id's. The first item is the base item. // Replace duplicate Id's. The first item is the base item.
@ -82,15 +92,19 @@ export class PresetBuildController
name: body.name, name: body.name,
root: body.root, root: body.root,
items: body.items, items: body.items,
type: "weapon" type: "weapon",
}; };
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(savedWeaponBuilds.indexOf(existingBuild), 1, newBuild); this.saveServer.getProfile(sessionId).userbuilds.weaponBuilds.splice(
savedWeaponBuilds.indexOf(existingBuild),
1,
newBuild,
);
} }
else else
{ {
@ -105,12 +119,21 @@ export class PresetBuildController
} }
/** Handle SaveEquipmentBuild event */ /** Handle SaveEquipmentBuild event */
public saveEquipmentBuild(pmcData: IPmcData, body: IPresetBuildActionRequestData, sessionID: string): IItemEventRouterResponse public saveEquipmentBuild(
pmcData: IPmcData,
body: IPresetBuildActionRequestData,
sessionID: string,
): IItemEventRouterResponse
{ {
return this.saveBuild(pmcData, body, sessionID, "equipmentBuilds"); return this.saveBuild(pmcData, body, sessionID, "equipmentBuilds");
} }
protected saveBuild(pmcData: IPmcData, body: IPresetBuildActionRequestData, sessionID: string, buildType: string): IItemEventRouterResponse protected saveBuild(
pmcData: IPmcData,
body: IPresetBuildActionRequestData,
sessionID: string,
buildType: string,
): IItemEventRouterResponse
{ {
const output = this.eventOutputHolder.getOutput(sessionID); const output = this.eventOutputHolder.getOutput(sessionID);
const existingSavedBuilds: any[] = this.saveServer.getProfile(sessionID).userbuilds[buildType]; const existingSavedBuilds: any[] = this.saveServer.getProfile(sessionID).userbuilds[buildType];
@ -125,14 +148,18 @@ export class PresetBuildController
buildType: "Custom", buildType: "Custom",
root: body.root, root: body.root,
fastPanel: [], fastPanel: [],
items: body.items items: body.items,
}; };
const existingBuild = existingSavedBuilds.find(x => x.name === body.name); const existingBuild = existingSavedBuilds.find((x) => x.name === body.name);
if (existingBuild) if (existingBuild)
{ {
// Already exists, replace // Already exists, replace
this.saveServer.getProfile(sessionID).userbuilds[buildType].splice(existingSavedBuilds.indexOf(existingBuild), 1, newBuild); this.saveServer.getProfile(sessionID).userbuilds[buildType].splice(
existingSavedBuilds.indexOf(existingBuild),
1,
newBuild,
);
} }
else else
{ {
@ -152,16 +179,24 @@ export class PresetBuildController
} }
/** Handle RemoveWeaponBuild event*/ /** Handle RemoveWeaponBuild event*/
public removeWeaponBuild(pmcData: IPmcData, body: IPresetBuildActionRequestData, sessionID: string): IItemEventRouterResponse public removeWeaponBuild(
pmcData: IPmcData,
body: IPresetBuildActionRequestData,
sessionID: string,
): IItemEventRouterResponse
{ {
// todo - does this get called? // TODO: Does this get called?
return this.removePlayerBuild(pmcData, body.id, sessionID); return this.removePlayerBuild(pmcData, body.id, sessionID);
} }
/** Handle RemoveEquipmentBuild event*/ /** Handle RemoveEquipmentBuild event*/
public removeEquipmentBuild(pmcData: IPmcData, body: IPresetBuildActionRequestData, sessionID: string): IItemEventRouterResponse public removeEquipmentBuild(
pmcData: IPmcData,
body: IPresetBuildActionRequestData,
sessionID: string,
): IItemEventRouterResponse
{ {
// todo - does this get called? // TODO: Does this get called?
return this.removePlayerBuild(pmcData, body.id, sessionID); return this.removePlayerBuild(pmcData, body.id, sessionID);
} }
@ -171,7 +206,7 @@ export class PresetBuildController
const equipmentBuilds = this.saveServer.getProfile(sessionID).userbuilds.equipmentBuilds; const equipmentBuilds = this.saveServer.getProfile(sessionID).userbuilds.equipmentBuilds;
// Check for id in weapon array first // Check for id in weapon array first
const matchingWeaponBuild = weaponBuilds.find(x => x.id === id); const matchingWeaponBuild = weaponBuilds.find((x) => x.id === id);
if (matchingWeaponBuild) if (matchingWeaponBuild)
{ {
weaponBuilds.splice(weaponBuilds.indexOf(matchingWeaponBuild), 1); weaponBuilds.splice(weaponBuilds.indexOf(matchingWeaponBuild), 1);
@ -180,7 +215,7 @@ export class PresetBuildController
} }
// Id not found in weapons, try equipment // Id not found in weapons, try equipment
const matchingEquipmentBuild = equipmentBuilds.find(x => x.id === id); const matchingEquipmentBuild = equipmentBuilds.find((x) => x.id === id);
if (matchingEquipmentBuild) if (matchingEquipmentBuild)
{ {
equipmentBuilds.splice(equipmentBuilds.indexOf(matchingEquipmentBuild), 1); equipmentBuilds.splice(equipmentBuilds.indexOf(matchingEquipmentBuild), 1);
@ -192,7 +227,6 @@ export class PresetBuildController
this.logger.error(`Unable to delete preset, cannot find ${id} in weapon or equipment presets`); this.logger.error(`Unable to delete preset, cannot find ${id} in weapon or equipment presets`);
} }
return this.eventOutputHolder.getOutput(sessionID); return this.eventOutputHolder.getOutput(sessionID);
} }
} }

View File

@ -9,9 +9,9 @@ export class PresetController
{ {
constructor( constructor(
@inject("PresetHelper") protected presetHelper: PresetHelper, @inject("PresetHelper") protected presetHelper: PresetHelper,
@inject("DatabaseServer") protected databaseServer: DatabaseServer @inject("DatabaseServer") protected databaseServer: DatabaseServer,
) )
{ } {}
public initialize(): void public initialize(): void
{ {

View File

@ -47,9 +47,9 @@ export class ProfileController
@inject("TraderHelper") protected traderHelper: TraderHelper, @inject("TraderHelper") protected traderHelper: TraderHelper,
@inject("DialogueHelper") protected dialogueHelper: DialogueHelper, @inject("DialogueHelper") protected dialogueHelper: DialogueHelper,
@inject("QuestHelper") protected questHelper: QuestHelper, @inject("QuestHelper") protected questHelper: QuestHelper,
@inject("ProfileHelper") protected profileHelper: ProfileHelper @inject("ProfileHelper") protected profileHelper: ProfileHelper,
) )
{ } {}
/** /**
* Handle /launcher/profiles * Handle /launcher/profiles
@ -76,7 +76,7 @@ export class ProfileController
const pmc = profile.characters.pmc; const pmc = profile.characters.pmc;
// make sure character completed creation // make sure character completed creation
if (!((pmc?.Info?.Level))) if (!(pmc?.Info?.Level))
{ {
return { return {
username: profile.info.username, username: profile.info.username,
@ -87,7 +87,7 @@ export class ProfileController
prevexp: 0, prevexp: 0,
nextlvl: 0, nextlvl: 0,
maxlvl: maxlvl, maxlvl: maxlvl,
akiData: this.profileHelper.getDefaultAkiDataObject() akiData: this.profileHelper.getDefaultAkiDataObject(),
}; };
} }
@ -102,7 +102,7 @@ export class ProfileController
prevexp: (currlvl === 0) ? 0 : this.profileHelper.getExperience(currlvl), prevexp: (currlvl === 0) ? 0 : this.profileHelper.getExperience(currlvl),
nextlvl: nextlvl, nextlvl: nextlvl,
maxlvl: maxlvl, maxlvl: maxlvl,
akiData: profile.aki akiData: profile.aki,
}; };
return result; return result;
@ -122,7 +122,8 @@ export class ProfileController
public createProfile(info: IProfileCreateRequestData, sessionID: string): void public createProfile(info: IProfileCreateRequestData, sessionID: string): void
{ {
const account = this.saveServer.getProfile(sessionID).info; const account = this.saveServer.getProfile(sessionID).info;
const profile: TemplateSide = this.databaseServer.getTables().templates.profiles[account.edition][info.side.toLowerCase()]; const profile: TemplateSide =
this.databaseServer.getTables().templates.profiles[account.edition][info.side.toLowerCase()];
const pmcData = profile.character; const pmcData = profile.character;
// Delete existing profile // Delete existing profile
@ -148,11 +149,16 @@ export class ProfileController
if (!pmcData.UnlockedInfo) if (!pmcData.UnlockedInfo)
{ {
pmcData.UnlockedInfo = { unlockedProductionRecipe: [] }; pmcData.UnlockedInfo = {unlockedProductionRecipe: []};
} }
// Change item id's to be unique // Change item id's to be unique
pmcData.Inventory.items = this.itemHelper.replaceIDs(pmcData, pmcData.Inventory.items, null, pmcData.Inventory.fastPanel); pmcData.Inventory.items = this.itemHelper.replaceIDs(
pmcData,
pmcData.Inventory.items,
null,
pmcData.Inventory.fastPanel,
);
pmcData.Inventory.hideoutAreaStashes = {}; pmcData.Inventory.hideoutAreaStashes = {};
// Create profile // Create profile
@ -160,7 +166,7 @@ export class ProfileController
info: account, info: account,
characters: { characters: {
pmc: pmcData, pmc: pmcData,
scav: {} as IPmcData scav: {} as IPmcData,
}, },
suits: profile.suits, suits: profile.suits,
userbuilds: profile.userbuilds, userbuilds: profile.userbuilds,
@ -169,7 +175,7 @@ export class ProfileController
vitality: {} as Vitality, vitality: {} as Vitality,
inraid: {} as Inraid, inraid: {} as Inraid,
insurance: [], insurance: [],
traderPurchases: {} traderPurchases: {},
}; };
this.profileFixerService.checkForAndFixPmcProfileIssues(profileDetails.characters.pmc); this.profileFixerService.checkForAndFixPmcProfileIssues(profileDetails.characters.pmc);
@ -185,7 +191,11 @@ export class ProfileController
// Profile is flagged as wanting quests set to ready to hand in and collect rewards // Profile is flagged as wanting quests set to ready to hand in and collect rewards
if (profile.trader.setQuestsAvailableForFinish) if (profile.trader.setQuestsAvailableForFinish)
{ {
this.questHelper.addAllQuestsToProfile(profileDetails.characters.pmc, [QuestStatus.AvailableForStart, QuestStatus.Started, QuestStatus.AvailableForFinish]); this.questHelper.addAllQuestsToProfile(profileDetails.characters.pmc, [
QuestStatus.AvailableForStart,
QuestStatus.Started,
QuestStatus.AvailableForFinish,
]);
// Make unused response so applyQuestReward works // Make unused response so applyQuestReward works
const response = this.eventOutputHolder.getOutput(sessionID); const response = this.eventOutputHolder.getOutput(sessionID);
@ -219,7 +229,9 @@ export class ProfileController
} }
else else
{ {
this.logger.warning(this.localisationService.getText("profile-unable_to_find_profile_by_id_cannot_delete", sessionID)); this.logger.warning(
this.localisationService.getText("profile-unable_to_find_profile_by_id_cannot_delete", sessionID),
);
} }
} }
@ -230,7 +242,11 @@ export class ProfileController
* @param sessionID Session id * @param sessionID Session id
* @param response Event router response * @param response Event router response
*/ */
protected givePlayerStartingQuestRewards(profileDetails: IAkiProfile, sessionID: string, response: IItemEventRouterResponse): void protected givePlayerStartingQuestRewards(
profileDetails: IAkiProfile,
sessionID: string,
response: IItemEventRouterResponse,
): void
{ {
for (const quest of profileDetails.characters.pmc.Quests) for (const quest of profileDetails.characters.pmc.Quests)
{ {
@ -238,8 +254,17 @@ export class ProfileController
// Get messageId of text to send to player as text message in game // Get messageId of text to send to player as text message in game
// Copy of code from QuestController.acceptQuest() // Copy of code from QuestController.acceptQuest()
const messageId = this.questHelper.getMessageIdForQuestStart(questFromDb.startedMessageText, questFromDb.description); const messageId = this.questHelper.getMessageIdForQuestStart(
const itemRewards = this.questHelper.applyQuestReward(profileDetails.characters.pmc, quest.qid, QuestStatus.Started, sessionID, response); questFromDb.startedMessageText,
questFromDb.description,
);
const itemRewards = this.questHelper.applyQuestReward(
profileDetails.characters.pmc,
quest.qid,
QuestStatus.Started,
sessionID,
response,
);
this.mailSendService.sendLocalisedNpcMessageToPlayer( this.mailSendService.sendLocalisedNpcMessageToPlayer(
sessionID, sessionID,
@ -247,7 +272,8 @@ export class ProfileController
MessageType.QUEST_START, MessageType.QUEST_START,
messageId, messageId,
itemRewards, itemRewards,
this.timeUtil.getHoursAsSeconds(100)); this.timeUtil.getHoursAsSeconds(100),
);
} }
} }
@ -323,18 +349,15 @@ export class ProfileController
/** /**
* Handle client/game/profile/search * Handle client/game/profile/search
*/ */
// eslint-disable-next-line @typescript-eslint/no-unused-vars
public getFriends(info: ISearchFriendRequestData, sessionID: string): ISearchFriendResponse[] public getFriends(info: ISearchFriendRequestData, sessionID: string): ISearchFriendResponse[]
{ {
return [ return [{
{ _id: this.hashUtil.generate(),
_id: this.hashUtil.generate(), Info: {
Info: { Level: 1,
Level: 1, Side: "Bear",
Side: "Bear", Nickname: info.nickname,
Nickname: info.nickname },
} }];
}
];
} }
} }

View File

@ -57,7 +57,7 @@ export class QuestController
@inject("LocaleService") protected localeService: LocaleService, @inject("LocaleService") protected localeService: LocaleService,
@inject("SeasonalEventService") protected seasonalEventService: SeasonalEventService, @inject("SeasonalEventService") protected seasonalEventService: SeasonalEventService,
@inject("LocalisationService") protected localisationService: LocalisationService, @inject("LocalisationService") protected localisationService: LocalisationService,
@inject("ConfigServer") protected configServer: ConfigServer @inject("ConfigServer") protected configServer: ConfigServer,
) )
{ {
this.questConfig = this.configServer.getConfig(ConfigTypes.QUEST); this.questConfig = this.configServer.getConfig(ConfigTypes.QUEST);
@ -79,7 +79,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;
@ -105,8 +105,12 @@ export class QuestController
} }
const questRequirements = this.questConditionHelper.getQuestConditions(quest.conditions.AvailableForStart); const questRequirements = this.questConditionHelper.getQuestConditions(quest.conditions.AvailableForStart);
const loyaltyRequirements = this.questConditionHelper.getLoyaltyConditions(quest.conditions.AvailableForStart); const loyaltyRequirements = this.questConditionHelper.getLoyaltyConditions(
const standingRequirements = this.questConditionHelper.getStandingConditions(quest.conditions.AvailableForStart); quest.conditions.AvailableForStart,
);
const standingRequirements = this.questConditionHelper.getStandingConditions(
quest.conditions.AvailableForStart,
);
// 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)
@ -122,14 +126,14 @@ 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(pq => pq.qid === conditionToFulfil._props.target); const prerequisiteQuest = profile.Quests.find((pq) => pq.qid === conditionToFulfil._props.target);
if (!prerequisiteQuest) if (!prerequisiteQuest)
{ {
haveCompletedPreviousQuest = false; haveCompletedPreviousQuest = false;
break; break;
} }
// Prereq does not have its status requirement fulfilled // Prerequisite does not have its status requirement fulfilled
if (!conditionToFulfil._props.status.includes(prerequisiteQuest.status)) if (!conditionToFulfil._props.status.includes(prerequisiteQuest.status))
{ {
haveCompletedPreviousQuest = false; haveCompletedPreviousQuest = false;
@ -144,7 +148,11 @@ export class QuestController
const unlockTime = previousQuestCompleteTime + conditionToFulfil._props.availableAfter; const unlockTime = previousQuestCompleteTime + conditionToFulfil._props.availableAfter;
if (unlockTime > this.timeUtil.getTimestamp()) if (unlockTime > this.timeUtil.getTimestamp())
{ {
this.logger.debug(`Quest ${quest.QuestName} is locked for another ${unlockTime - this.timeUtil.getTimestamp()} seconds`); this.logger.debug(
`Quest ${quest.QuestName} is locked for another ${
unlockTime - this.timeUtil.getTimestamp()
} seconds`,
);
} }
} }
} }
@ -221,19 +229,28 @@ export class QuestController
const isHalloweenEventActive = this.seasonalEventService.halloweenEventEnabled(); const isHalloweenEventActive = this.seasonalEventService.halloweenEventEnabled();
// Not christmas + quest is for christmas // Not christmas + quest is for christmas
if (!isChristmasEventActive && this.seasonalEventService.isQuestRelatedToEvent(questId, SeasonalEventType.CHRISTMAS)) if (
!isChristmasEventActive &&
this.seasonalEventService.isQuestRelatedToEvent(questId, SeasonalEventType.CHRISTMAS)
)
{ {
return false; return false;
} }
// Not halloween + quest is for halloween // Not halloween + quest is for halloween
if (!isHalloweenEventActive && this.seasonalEventService.isQuestRelatedToEvent(questId, SeasonalEventType.HALLOWEEN)) if (
!isHalloweenEventActive &&
this.seasonalEventService.isQuestRelatedToEvent(questId, SeasonalEventType.HALLOWEEN)
)
{ {
return false; return false;
} }
// Should non-season event quests be shown to player // Should non-season event quests be shown to player
if (!this.questConfig.showNonSeasonalEventQuests && this.seasonalEventService.isQuestRelatedToEvent(questId, SeasonalEventType.NONE)) if (
!this.questConfig.showNonSeasonalEventQuests &&
this.seasonalEventService.isQuestRelatedToEvent(questId, SeasonalEventType.NONE)
)
{ {
return false; return false;
} }
@ -274,7 +291,11 @@ export class QuestController
* @param sessionID Session id * @param sessionID Session id
* @returns client response * @returns client response
*/ */
public acceptQuest(pmcData: IPmcData, acceptedQuest: IAcceptQuestRequestData, sessionID: string): IItemEventRouterResponse public acceptQuest(
pmcData: IPmcData,
acceptedQuest: IAcceptQuestRequestData,
sessionID: string,
): IItemEventRouterResponse
{ {
const acceptQuestResponse = this.eventOutputHolder.getOutput(sessionID); const acceptQuestResponse = this.eventOutputHolder.getOutput(sessionID);
@ -282,7 +303,7 @@ export class QuestController
const newQuest = this.questHelper.getQuestReadyForProfile(pmcData, startedState, acceptedQuest); const newQuest = this.questHelper.getQuestReadyForProfile(pmcData, startedState, acceptedQuest);
// Does quest exist in profile // Does quest exist in profile
if (pmcData.Quests.find(x => x.qid === acceptedQuest.qid)) if (pmcData.Quests.find((x) => x.qid === acceptedQuest.qid))
{ {
// Update existing // Update existing
this.questHelper.updateQuestState(pmcData, QuestStatus.Started, acceptedQuest.qid); this.questHelper.updateQuestState(pmcData, QuestStatus.Started, acceptedQuest.qid);
@ -297,8 +318,17 @@ export class QuestController
// Note that for starting quests, the correct locale field is "description", not "startedMessageText". // Note that for starting quests, the correct locale field is "description", not "startedMessageText".
const questFromDb = this.questHelper.getQuestFromDb(acceptedQuest.qid, pmcData); const questFromDb = this.questHelper.getQuestFromDb(acceptedQuest.qid, pmcData);
// Get messageId of text to send to player as text message in game // Get messageId of text to send to player as text message in game
const messageId = this.questHelper.getMessageIdForQuestStart(questFromDb.startedMessageText, questFromDb.description); const messageId = this.questHelper.getMessageIdForQuestStart(
const startedQuestRewards = this.questHelper.applyQuestReward(pmcData, acceptedQuest.qid, QuestStatus.Started, sessionID, acceptQuestResponse); questFromDb.startedMessageText,
questFromDb.description,
);
const startedQuestRewards = this.questHelper.applyQuestReward(
pmcData,
acceptedQuest.qid,
QuestStatus.Started,
sessionID,
acceptQuestResponse,
);
this.mailSendService.sendLocalisedNpcMessageToPlayer( this.mailSendService.sendLocalisedNpcMessageToPlayer(
sessionID, sessionID,
@ -306,9 +336,11 @@ export class QuestController
MessageType.QUEST_START, MessageType.QUEST_START,
messageId, messageId,
startedQuestRewards, startedQuestRewards,
this.timeUtil.getHoursAsSeconds(this.questConfig.redeemTime)); this.timeUtil.getHoursAsSeconds(this.questConfig.redeemTime),
);
acceptQuestResponse.profileChanges[sessionID].quests = this.questHelper.getNewlyAccessibleQuestsWhenStartingQuest(acceptedQuest.qid, sessionID); acceptQuestResponse.profileChanges[sessionID].quests = this.questHelper
.getNewlyAccessibleQuestsWhenStartingQuest(acceptedQuest.qid, sessionID);
return acceptQuestResponse; return acceptQuestResponse;
} }
@ -322,7 +354,11 @@ export class QuestController
* @param sessionID Session id * @param sessionID Session id
* @returns IItemEventRouterResponse * @returns IItemEventRouterResponse
*/ */
public acceptRepeatableQuest(pmcData: IPmcData, acceptedQuest: IAcceptQuestRequestData, sessionID: string): IItemEventRouterResponse public acceptRepeatableQuest(
pmcData: IPmcData,
acceptedQuest: IAcceptQuestRequestData,
sessionID: string,
): IItemEventRouterResponse
{ {
const acceptQuestResponse = this.eventOutputHolder.getOutput(sessionID); const acceptQuestResponse = this.eventOutputHolder.getOutput(sessionID);
@ -333,13 +369,21 @@ export class QuestController
const repeatableQuestProfile = this.getRepeatableQuestFromProfile(pmcData, acceptedQuest); const repeatableQuestProfile = this.getRepeatableQuestFromProfile(pmcData, acceptedQuest);
if (!repeatableQuestProfile) if (!repeatableQuestProfile)
{ {
this.logger.error(this.localisationService.getText("repeatable-accepted_repeatable_quest_not_found_in_active_quests", acceptedQuest.qid)); this.logger.error(
this.localisationService.getText(
"repeatable-accepted_repeatable_quest_not_found_in_active_quests",
acceptedQuest.qid,
),
);
throw new Error(this.localisationService.getText("repeatable-unable_to_accept_quest_see_log")); throw new Error(this.localisationService.getText("repeatable-unable_to_accept_quest_see_log"));
} }
// Some scav quests need to be added to scav profile for them to show up in-raid // Some scav quests need to be added to scav profile for them to show up in-raid
if (repeatableQuestProfile.side === "Scav" && ["PickUp", "Exploration", "Elimination"].includes(repeatableQuestProfile.type)) if (
repeatableQuestProfile.side === "Scav" &&
["PickUp", "Exploration", "Elimination"].includes(repeatableQuestProfile.type)
)
{ {
const fullProfile = this.profileHelper.getFullProfile(sessionID); const fullProfile = this.profileHelper.getFullProfile(sessionID);
if (!fullProfile.characters.scav.Quests) if (!fullProfile.characters.scav.Quests)
@ -351,46 +395,67 @@ export class QuestController
} }
const locale = this.localeService.getLocaleDb(); const locale = this.localeService.getLocaleDb();
const questStartedMessageKey = this.questHelper.getMessageIdForQuestStart(repeatableQuestProfile.startedMessageText, repeatableQuestProfile.description); const questStartedMessageKey = this.questHelper.getMessageIdForQuestStart(
repeatableQuestProfile.startedMessageText,
repeatableQuestProfile.description,
);
// Can be started text or description text based on above function result // Can be started text or description text based on above function result
let questStartedMessageText = locale[questStartedMessageKey]; let questStartedMessageText = locale[questStartedMessageKey];
// TODO: remove this whole if statement, possibly not required? // TODO: Remove this whole if statement, possibly not required?
if (!questStartedMessageText) if (!questStartedMessageText)
{ {
this.logger.debug(`Unable to accept quest ${acceptedQuest.qid}, cannot find the quest started message text with id ${questStartedMessageKey}. attempting to find it in en locale instead`); this.logger.debug(
`Unable to accept quest ${acceptedQuest.qid}, cannot find the quest started message text with id ${questStartedMessageKey}. attempting to find it in en locale instead`,
);
// For some reason non-en locales dont have repeatable quest ids, fall back to en and grab it if possible // For some reason non-en locales don't have repeatable quest ids, fall back to en and grab it if possible
const enLocale = this.databaseServer.getTables().locales.global["en"]; const enLocale = this.databaseServer.getTables().locales.global["en"];
questStartedMessageText = enLocale[repeatableQuestProfile.startedMessageText]; questStartedMessageText = enLocale[repeatableQuestProfile.startedMessageText];
if (!questStartedMessageText) if (!questStartedMessageText)
{ {
this.logger.error(this.localisationService.getText("repeatable-unable_to_accept_quest_starting_message_not_found", {questId: acceptedQuest.qid, messageId: questStartedMessageKey})); this.logger.error(
this.localisationService.getText("repeatable-unable_to_accept_quest_starting_message_not_found", {
questId: acceptedQuest.qid,
messageId: questStartedMessageKey,
}),
);
return this.httpResponseUtil.appendErrorToOutput(acceptQuestResponse, this.localisationService.getText("repeatable-unable_to_accept_quest_see_log")); return this.httpResponseUtil.appendErrorToOutput(
acceptQuestResponse,
this.localisationService.getText("repeatable-unable_to_accept_quest_see_log"),
);
} }
} }
const questRewards = this.questHelper.getQuestRewardItems(<IQuest><unknown>repeatableQuestProfile, desiredQuestState); const questRewards = this.questHelper.getQuestRewardItems(
<IQuest><unknown>repeatableQuestProfile,
desiredQuestState,
);
this.mailSendService.sendLocalisedNpcMessageToPlayer( this.mailSendService.sendLocalisedNpcMessageToPlayer(
sessionID, sessionID,
this.traderHelper.getTraderById(repeatableQuestProfile.traderId), this.traderHelper.getTraderById(repeatableQuestProfile.traderId),
MessageType.QUEST_START, MessageType.QUEST_START,
questStartedMessageKey, questStartedMessageKey,
questRewards, questRewards,
this.timeUtil.getHoursAsSeconds(this.questConfig.redeemTime)); this.timeUtil.getHoursAsSeconds(this.questConfig.redeemTime),
);
const repeatableSettings = pmcData.RepeatableQuests.find(x => x.name === repeatableQuestProfile.sptRepatableGroupName); const repeatableSettings = pmcData.RepeatableQuests.find((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 => x.name === repeatableQuestProfile.sptRepatableGroupName).id, id: repeatableSettings.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,
activeQuests: [repeatableQuestProfile], activeQuests: [repeatableQuestProfile],
inactiveQuests: [] inactiveQuests: [],
}; };
acceptQuestResponse.profileChanges[sessionID].repeatableQuests = [responseData]; acceptQuestResponse.profileChanges[sessionID].repeatableQuests = [responseData];
@ -407,7 +472,7 @@ export class QuestController
{ {
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}`);
@ -430,7 +495,11 @@ export class QuestController
* @param sessionID Session id * @param sessionID Session id
* @returns ItemEvent client response * @returns ItemEvent client response
*/ */
public completeQuest(pmcData: IPmcData, body: ICompleteQuestRequestData, sessionID: string): IItemEventRouterResponse public completeQuest(
pmcData: IPmcData,
body: ICompleteQuestRequestData,
sessionID: string,
): IItemEventRouterResponse
{ {
const completeQuestResponse = this.eventOutputHolder.getOutput(sessionID); const completeQuestResponse = this.eventOutputHolder.getOutput(sessionID);
@ -442,7 +511,13 @@ export class QuestController
const newQuestState = QuestStatus.Success; const newQuestState = QuestStatus.Success;
this.questHelper.updateQuestState(pmcData, newQuestState, completedQuestId); this.questHelper.updateQuestState(pmcData, newQuestState, completedQuestId);
const questRewards = this.questHelper.applyQuestReward(pmcData, body.qid, newQuestState, sessionID, completeQuestResponse); const questRewards = this.questHelper.applyQuestReward(
pmcData,
body.qid,
newQuestState,
sessionID,
completeQuestResponse,
);
// Check for linked failed + unrestartable quests // Check for linked failed + unrestartable quests
const questsToFail = this.getQuestsFailedByCompletingQuest(completedQuestId); const questsToFail = this.getQuestsFailedByCompletingQuest(completedQuestId);
@ -457,7 +532,7 @@ export class QuestController
// Add diff of quests before completion vs after for client response // Add diff of quests before completion vs after for client response
const questDelta = this.questHelper.getDeltaQuests(beforeQuests, this.getClientQuests(sessionID)); const questDelta = this.questHelper.getDeltaQuests(beforeQuests, this.getClientQuests(sessionID));
// Check newly available + failed quests for timegates and add them to profile // Check newly available + failed quests for time gates and add them to profile
this.addTimeLockedQuestsToProfile(pmcData, [...questDelta, ...questsToFail], body.qid); this.addTimeLockedQuestsToProfile(pmcData, [...questDelta, ...questsToFail], body.qid);
// Inform client of quest changes // Inform client of quest changes
@ -466,10 +541,12 @@ export class QuestController
// Check if it's a repeatable quest. If so, remove from Quests and repeatable.activeQuests list + move to repeatable.inactiveQuests // Check if it's a repeatable quest. If so, remove from Quests and repeatable.activeQuests list + move to repeatable.inactiveQuests
for (const currentRepeatable of pmcData.RepeatableQuests) for (const currentRepeatable of pmcData.RepeatableQuests)
{ {
const repeatableQuest = currentRepeatable.activeQuests.find(x => x._id === completedQuestId); const repeatableQuest = currentRepeatable.activeQuests.find((x) => x._id === completedQuestId);
if (repeatableQuest) if (repeatableQuest)
{ {
currentRepeatable.activeQuests = currentRepeatable.activeQuests.filter(x => x._id !== completedQuestId); currentRepeatable.activeQuests = currentRepeatable.activeQuests.filter((x) =>
x._id !== completedQuestId
);
currentRepeatable.inactiveQuests.push(repeatableQuest); currentRepeatable.inactiveQuests.push(repeatableQuest);
// Need to remove redundant scav quest object as its no longer necessary, is tracked in pmc profile // Need to remove redundant scav quest object as its no longer necessary, is tracked in pmc profile
@ -498,31 +575,39 @@ 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(`Unable to remove quest: ${questIdToRemove} from profile as scav profile cannot be found`); this.logger.warning(
`Unable to remove quest: ${questIdToRemove} from profile as scav profile cannot be found`,
);
return; return;
} }
fullProfile.characters.scav.Quests.splice(fullProfile.characters.scav.Quests.indexOf(repeatableInScavProfile), 1); fullProfile.characters.scav.Quests.splice(
fullProfile.characters.scav.Quests.indexOf(repeatableInScavProfile),
1,
);
} }
/** /**
* Return quests that have different statuses * Return quests that have different statuses
* @param preQuestStatusus Quests before * @param preQuestStatuses Quests before
* @param postQuestStatuses Quests after * @param postQuestStatuses Quests after
* @returns QuestStatusChange array * @returns QuestStatusChange array
*/ */
protected getQuestsWithDifferentStatuses(preQuestStatusus: IQuestStatus[], postQuestStatuses: IQuestStatus[]): IQuestStatus[] protected getQuestsWithDifferentStatuses(
preQuestStatuses: IQuestStatus[],
postQuestStatuses: IQuestStatus[],
): IQuestStatus[]
{ {
const result: IQuestStatus[] = []; const result: IQuestStatus[] = [];
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 = preQuestStatuses.find((x) => x.qid === quest.qid);
if (!preQuest || preQuest.status !== quest.status) if (!preQuest || preQuest.status !== quest.status)
{ {
result.push(quest); result.push(quest);
@ -544,7 +629,12 @@ export class QuestController
* @param completedQuestId Completed quest id * @param completedQuestId Completed quest id
* @param questRewards Rewards given to player * @param questRewards Rewards given to player
*/ */
protected sendSuccessDialogMessageOnQuestComplete(sessionID: string, pmcData: IPmcData, completedQuestId: string, questRewards: Reward[]): void protected sendSuccessDialogMessageOnQuestComplete(
sessionID: string,
pmcData: IPmcData,
completedQuestId: string,
questRewards: Reward[],
): void
{ {
const quest = this.questHelper.getQuestFromDb(completedQuestId, pmcData); const quest = this.questHelper.getQuestFromDb(completedQuestId, pmcData);
@ -554,7 +644,8 @@ export class QuestController
MessageType.QUEST_SUCCESS, MessageType.QUEST_SUCCESS,
quest.successMessageText, quest.successMessageText,
questRewards, questRewards,
this.timeUtil.getHoursAsSeconds(this.questConfig.redeemTime)); this.timeUtil.getHoursAsSeconds(this.questConfig.redeemTime),
);
} }
/** /**
@ -568,15 +659,18 @@ export class QuestController
// Iterate over quests, look for quests with right criteria // Iterate over quests, look for quests with right criteria
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 prerequisite of completed quest + availableAfter value > 0 (quest has wait time)
const nextQuestWaitCondition = quest.conditions.AvailableForStart.find(x => x._props.target === completedQuestId && x._props.availableAfter > 0); const nextQuestWaitCondition = quest.conditions.AvailableForStart.find((x) =>
x._props.target === completedQuestId && x._props.availableAfter > 0
);
if (nextQuestWaitCondition) if (nextQuestWaitCondition)
{ {
// Now + wait time // Now + wait time
const availableAfterTimestamp = this.timeUtil.getTimestamp() + nextQuestWaitCondition._props.availableAfter; const availableAfterTimestamp = this.timeUtil.getTimestamp() +
nextQuestWaitCondition._props.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;
@ -592,10 +686,9 @@ export class QuestController
startTime: 0, startTime: 0,
status: QuestStatus.AvailableAfter, status: QuestStatus.AvailableAfter,
statusTimers: { statusTimers: {
// eslint-disable-next-line @typescript-eslint/naming-convention "9": this.timeUtil.getTimestamp(), // eslint-disable-line @typescript-eslint/naming-convention
"9": this.timeUtil.getTimestamp()
}, },
availableAfter: availableAfterTimestamp availableAfter: availableAfterTimestamp,
}); });
} }
} }
@ -617,7 +710,7 @@ export class QuestController
return false; return false;
} }
return x.conditions.Fail.some(y => y._props.target === completedQuestId); return x.conditions.Fail.some((y) => y._props.target === completedQuestId);
}); });
} }
@ -629,23 +722,28 @@ export class QuestController
* @param questsToFail quests to fail * @param questsToFail quests to fail
* @param output Client output * @param output Client output
*/ */
protected failQuests(sessionID: string, pmcData: IPmcData, questsToFail: IQuest[], output: IItemEventRouterResponse): void protected failQuests(
sessionID: string,
pmcData: IPmcData,
questsToFail: IQuest[],
output: IItemEventRouterResponse,
): void
{ {
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._props.status?.some(y => y !== QuestStatus.Success))) if (questToFail.conditions.Fail?.some((x) => x._props.status?.some((y) => y !== QuestStatus.Success)))
{ {
continue; continue;
} }
const isActiveQuestInPlayerProfile = pmcData.Quests.find(y => y.qid === questToFail._id); const isActiveQuestInPlayerProfile = pmcData.Quests.find((y) => y.qid === questToFail._id);
if (isActiveQuestInPlayerProfile) if (isActiveQuestInPlayerProfile)
{ {
const failBody: IFailQuestRequestData = { const failBody: IFailQuestRequestData = {
Action: "QuestComplete", Action: "QuestComplete",
qid: questToFail._id, qid: questToFail._id,
removeExcessItems: true removeExcessItems: true,
}; };
this.questHelper.failQuest(pmcData, failBody, sessionID, output); this.questHelper.failQuest(pmcData, failBody, sessionID, output);
} }
@ -657,7 +755,7 @@ export class QuestController
qid: questToFail._id, qid: questToFail._id,
startTime: this.timeUtil.getTimestamp(), startTime: this.timeUtil.getTimestamp(),
statusTimers: statusTimers, statusTimers: statusTimers,
status: QuestStatus.Fail status: QuestStatus.Fail,
}; };
pmcData.Quests.push(questData); pmcData.Quests.push(questData);
} }
@ -671,7 +769,11 @@ export class QuestController
* @param sessionID Session id * @param sessionID Session id
* @returns IItemEventRouterResponse * @returns IItemEventRouterResponse
*/ */
public handoverQuest(pmcData: IPmcData, handoverQuestRequest: IHandoverQuestRequestData, sessionID: string): IItemEventRouterResponse public handoverQuest(
pmcData: IPmcData,
handoverQuestRequest: IHandoverQuestRequestData,
sessionID: string,
): IItemEventRouterResponse
{ {
const quest = this.questHelper.getQuestFromDb(handoverQuestRequest.qid, pmcData); const quest = this.questHelper.getQuestFromDb(handoverQuestRequest.qid, pmcData);
const handoverQuestTypes = ["HandoverItem", "WeaponAssembly"]; const handoverQuestTypes = ["HandoverItem", "WeaponAssembly"];
@ -684,20 +786,33 @@ export class QuestController
let handoverRequirements: AvailableForConditions; let handoverRequirements: AvailableForConditions;
for (const condition of quest.conditions.AvailableForFinish) for (const condition of quest.conditions.AvailableForFinish)
{ {
if (condition._props.id === handoverQuestRequest.conditionId && handoverQuestTypes.includes(condition._parent)) if (
condition._props.id === handoverQuestRequest.conditionId &&
handoverQuestTypes.includes(condition._parent)
)
{ {
handedInCount = Number.parseInt(<string>condition._props.value); handedInCount = Number.parseInt(<string>condition._props.value);
isItemHandoverQuest = condition._parent === handoverQuestTypes[0]; isItemHandoverQuest = condition._parent === handoverQuestTypes[0];
handoverRequirements = condition; handoverRequirements = condition;
const profileCounter = (handoverQuestRequest.conditionId in pmcData.BackendCounters) const profileCounter = (handoverQuestRequest.conditionId in pmcData.BackendCounters) ?
? pmcData.BackendCounters[handoverQuestRequest.conditionId].value pmcData.BackendCounters[handoverQuestRequest.conditionId].value :
: 0; 0;
handedInCount -= profileCounter; handedInCount -= profileCounter;
if (handedInCount <= 0) if (handedInCount <= 0)
{ {
this.logger.error(this.localisationService.getText("repeatable-quest_handover_failed_condition_already_satisfied", {questId: handoverQuestRequest.qid, conditionId: handoverQuestRequest.conditionId, profileCounter: profileCounter, value: handedInCount})); this.logger.error(
this.localisationService.getText(
"repeatable-quest_handover_failed_condition_already_satisfied",
{
questId: handoverQuestRequest.qid,
conditionId: handoverQuestRequest.conditionId,
profileCounter: profileCounter,
value: handedInCount,
},
),
);
return output; return output;
} }
@ -714,11 +829,16 @@ 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(x => x._id === itemHandover.id); const matchingItemInProfile = pmcData.Inventory.items.find((x) => x._id === itemHandover.id);
if (!handoverRequirements._props.target.includes(matchingItemInProfile._tpl)) if (!handoverRequirements._props.target.includes(matchingItemInProfile._tpl))
{ {
// Item handed in by player doesnt match what was requested // Item handed in by player doesn't match what was requested
return this.showQuestItemHandoverMatchError(handoverQuestRequest, matchingItemInProfile, handoverRequirements, output); return this.showQuestItemHandoverMatchError(
handoverQuestRequest,
matchingItemInProfile,
handoverRequirements,
output,
);
} }
// Remove the right quantity of given items // Remove the right quantity of given items
@ -727,7 +847,13 @@ export class QuestController
if (itemHandover.count - itemCountToRemove > 0) if (itemHandover.count - itemCountToRemove > 0)
{ {
// Remove single item with no children // Remove single item with no children
this.questHelper.changeItemStack(pmcData, itemHandover.id, itemHandover.count - itemCountToRemove, sessionID, output); this.questHelper.changeItemStack(
pmcData,
itemHandover.id,
itemHandover.count - itemCountToRemove,
sessionID,
output,
);
if (totalItemCountToRemove === handedInCount) if (totalItemCountToRemove === handedInCount)
{ {
break; break;
@ -740,7 +866,7 @@ export class QuestController
let index = pmcData.Inventory.items.length; let index = pmcData.Inventory.items.length;
// Important: don't tell the client to remove the attachments, it will handle it // Important: don't tell the client to remove the attachments, it will handle it
output.profileChanges[sessionID].items.del.push({ _id: itemHandover.id }); output.profileChanges[sessionID].items.del.push({_id: itemHandover.id});
// Important: loop backward when removing items from the array we're looping on // Important: loop backward when removing items from the array we're looping on
while (index-- > 0) while (index-- > 0)
@ -753,7 +879,12 @@ export class QuestController
} }
} }
this.updateProfileBackendCounterValue(pmcData, handoverQuestRequest.conditionId, handoverQuestRequest.qid, totalItemCountToRemove); this.updateProfileBackendCounterValue(
pmcData,
handoverQuestRequest.conditionId,
handoverQuestRequest.qid,
totalItemCountToRemove,
);
return output; return output;
} }
@ -764,9 +895,15 @@ export class QuestController
* @param output Response to send to user * @param output Response to send to user
* @returns IItemEventRouterResponse * @returns IItemEventRouterResponse
*/ */
protected showRepeatableQuestInvalidConditionError(handoverQuestRequest: IHandoverQuestRequestData, output: IItemEventRouterResponse): IItemEventRouterResponse protected showRepeatableQuestInvalidConditionError(
handoverQuestRequest: IHandoverQuestRequestData,
output: IItemEventRouterResponse,
): IItemEventRouterResponse
{ {
const errorMessage = this.localisationService.getText("repeatable-quest_handover_failed_condition_invalid", { questId: handoverQuestRequest.qid, conditionId: handoverQuestRequest.conditionId }); const errorMessage = this.localisationService.getText("repeatable-quest_handover_failed_condition_invalid", {
questId: handoverQuestRequest.qid,
conditionId: handoverQuestRequest.conditionId,
});
this.logger.error(errorMessage); this.logger.error(errorMessage);
return this.httpResponseUtil.appendErrorToOutput(output, errorMessage); return this.httpResponseUtil.appendErrorToOutput(output, errorMessage);
@ -780,9 +917,18 @@ export class QuestController
* @param output Response to send to user * @param output Response to send to user
* @returns IItemEventRouterResponse * @returns IItemEventRouterResponse
*/ */
protected showQuestItemHandoverMatchError(handoverQuestRequest: IHandoverQuestRequestData, itemHandedOver: Item, handoverRequirements: AvailableForConditions, output: IItemEventRouterResponse): IItemEventRouterResponse protected showQuestItemHandoverMatchError(
handoverQuestRequest: IHandoverQuestRequestData,
itemHandedOver: Item,
handoverRequirements: AvailableForConditions,
output: IItemEventRouterResponse,
): IItemEventRouterResponse
{ {
const errorMessage = this.localisationService.getText("quest-handover_wrong_item", { questId: handoverQuestRequest.qid, handedInTpl: itemHandedOver._tpl, requiredTpl: handoverRequirements._props.target[0] }); const errorMessage = this.localisationService.getText("quest-handover_wrong_item", {
questId: handoverQuestRequest.qid,
handedInTpl: itemHandedOver._tpl,
requiredTpl: handoverRequirements._props.target[0],
});
this.logger.error(errorMessage); this.logger.error(errorMessage);
return this.httpResponseUtil.appendErrorToOutput(output, errorMessage); return this.httpResponseUtil.appendErrorToOutput(output, errorMessage);
@ -796,7 +942,12 @@ export class QuestController
* @param questId quest id counter is associated with * @param questId quest id counter is associated with
* @param counterValue value to increment the backend counter with * @param counterValue value to increment the backend counter with
*/ */
protected updateProfileBackendCounterValue(pmcData: IPmcData, conditionId: string, questId: string, counterValue: number): void protected updateProfileBackendCounterValue(
pmcData: IPmcData,
conditionId: string,
questId: string,
counterValue: number,
): void
{ {
if (pmcData.BackendCounters[conditionId] !== undefined) if (pmcData.BackendCounters[conditionId] !== undefined)
{ {
@ -807,7 +958,7 @@ export class QuestController
pmcData.BackendCounters[conditionId] = { pmcData.BackendCounters[conditionId] = {
id: conditionId, id: conditionId,
qid: questId, qid: questId,
value: counterValue value: counterValue,
}; };
} }
} }

View File

@ -76,7 +76,7 @@ export class RagfairController
@inject("RagfairRequiredItemsService") protected ragfairRequiredItemsService: RagfairRequiredItemsService, @inject("RagfairRequiredItemsService") protected ragfairRequiredItemsService: RagfairRequiredItemsService,
@inject("RagfairOfferGenerator") protected ragfairOfferGenerator: RagfairOfferGenerator, @inject("RagfairOfferGenerator") protected ragfairOfferGenerator: RagfairOfferGenerator,
@inject("LocalisationService") protected localisationService: LocalisationService, @inject("LocalisationService") protected localisationService: LocalisationService,
@inject("ConfigServer") protected configServer: ConfigServer @inject("ConfigServer") protected configServer: ConfigServer,
) )
{ {
this.ragfairConfig = this.configServer.getConfig(ConfigTypes.RAGFAIR); this.ragfairConfig = this.configServer.getConfig(ConfigTypes.RAGFAIR);
@ -89,7 +89,7 @@ export class RagfairController
const result: IGetOffersResult = { const result: IGetOffersResult = {
offers: [], offers: [],
offersCount: searchRequest.limit, offersCount: searchRequest.limit,
selectedCategory: searchRequest.handbookId selectedCategory: searchRequest.handbookId,
}; };
const pmcProfile = this.profileHelper.getPmcProfile(sessionID); const pmcProfile = this.profileHelper.getPmcProfile(sessionID);
@ -106,7 +106,11 @@ export class RagfairController
this.addIndexValueToOffers(result.offers); this.addIndexValueToOffers(result.offers);
// Sort offers // Sort offers
result.offers = this.ragfairSortHelper.sortOffers(result.offers, searchRequest.sortType, searchRequest.sortDirection); result.offers = this.ragfairSortHelper.sortOffers(
result.offers,
searchRequest.sortType,
searchRequest.sortDirection,
);
// Match offers with quests and lock unfinished quests // Match offers with quests and lock unfinished quests
const profile = this.profileHelper.getFullProfile(sessionID); const profile = this.profileHelper.getFullProfile(sessionID);
@ -114,8 +118,8 @@ export class RagfairController
{ {
if (offer.user.memberType === MemberCategory.TRADER) if (offer.user.memberType === MemberCategory.TRADER)
{ {
// for the items, check the barter schemes. The method getDisplayableAssorts sets a flag sptQuestLocked to true if the quest // for the items, check the barter schemes. The method getDisplayableAssorts sets a flag sptQuestLocked
// is not completed yet // to true if the quest is not completed yet
if (this.ragfairOfferHelper.traderOfferItemQuestLocked(offer, traderAssorts)) if (this.ragfairOfferHelper.traderOfferItemQuestLocked(offer, traderAssorts))
{ {
offer.locked = true; offer.locked = true;
@ -135,7 +139,7 @@ export class RagfairController
if (searchRequest.buildCount === 0) if (searchRequest.buildCount === 0)
{ {
const start = searchRequest.page * searchRequest.limit; const start = searchRequest.page * searchRequest.limit;
const end = Math.min(((searchRequest.page + 1) * searchRequest.limit), result.offers.length); const end = Math.min((searchRequest.page + 1) * searchRequest.limit, result.offers.length);
result.offers = result.offers.slice(start, end); result.offers = result.offers.slice(start, end);
} }
return result; return result;
@ -149,7 +153,12 @@ export class RagfairController
* @param pmcProfile Player profile * @param pmcProfile Player profile
* @returns array of offers * @returns array of offers
*/ */
protected getOffersForSearchType(searchRequest: ISearchRequestData, itemsToAdd: string[], traderAssorts: Record<string, ITraderAssort>, pmcProfile: IPmcData): IRagfairOffer[] protected getOffersForSearchType(
searchRequest: ISearchRequestData,
itemsToAdd: string[],
traderAssorts: Record<string, ITraderAssort>,
pmcProfile: IPmcData,
): IRagfairOffer[]
{ {
// Searching for items in preset menu // Searching for items in preset menu
if (searchRequest.buildCount) if (searchRequest.buildCount)
@ -165,7 +174,7 @@ export class RagfairController
* Get categories for the type of search being performed, linked/required/all * Get categories for the type of search being performed, linked/required/all
* @param searchRequest Client search request data * @param searchRequest Client search request data
* @param offers ragfair offers to get categories for * @param offers ragfair offers to get categories for
* @returns record with tpls + counts * @returns record with templates + counts
*/ */
protected getSpecificCategories(searchRequest: ISearchRequestData, offers: IRagfairOffer[]): Record<string, number> protected getSpecificCategories(searchRequest: ISearchRequestData, offers: IRagfairOffer[]): Record<string, number>
{ {
@ -191,7 +200,12 @@ export class RagfairController
* @param pmcProfile Player profile * @param pmcProfile Player profile
* @param result Result object being sent back to client * @param result Result object being sent back to client
*/ */
protected addRequiredOffersToResult(searchRequest: ISearchRequestData, assorts: Record<string, ITraderAssort>, pmcProfile: IPmcData, result: IGetOffersResult): void protected addRequiredOffersToResult(
searchRequest: ISearchRequestData,
assorts: Record<string, ITraderAssort>,
pmcProfile: IPmcData,
result: IGetOffersResult,
): void
{ {
const requiredOffers = this.ragfairRequiredItemsService.getRequiredItemsById(searchRequest.neededSearchId); const requiredOffers = this.ragfairRequiredItemsService.getRequiredItemsById(searchRequest.neededSearchId);
for (const requiredOffer of requiredOffers) for (const requiredOffer of requiredOffers)
@ -214,7 +228,7 @@ export class RagfairController
for (const offer of offers) for (const offer of offers)
{ {
offer.intId = ++counter; offer.intId = ++counter;
offer.items[0].parentId = ""; //without this it causes error: "Item deserialization error: No parent with id hideout found for item x" offer.items[0].parentId = ""; // Without this it causes error: "Item deserialization error: No parent with id hideout found for item x"
} }
} }
@ -239,12 +253,12 @@ 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 = profile.traderPurchases[offer.user.id][assortId] offer.buyRestrictionCurrent = profile.traderPurchases[offer.user.id][assortId] ?
? profile.traderPurchases[offer.user.id][assortId].count profile.traderPurchases[offer.user.id][assortId].count :
: assortData.upd.BuyRestrictionCurrent; assortData.upd.BuyRestrictionCurrent;
offer.buyRestrictionMax = assortData.upd.BuyRestrictionMax; offer.buyRestrictionMax = assortData.upd.BuyRestrictionMax;
} }
@ -258,10 +272,15 @@ 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.localisationService.getText("ragfair-unable_to_adjust_stack_count_assort_not_found", {offerId: offer.items[0]._id, traderId: offer.user.id})); this.logger.warning(
this.localisationService.getText("ragfair-unable_to_adjust_stack_count_assort_not_found", {
offerId: offer.items[0]._id,
traderId: offer.user.id,
}),
);
return; return;
} }
@ -301,7 +320,7 @@ export class RagfairController
let offers = this.ragfairOfferService.getOffersOfType(getPriceRequest.templateId); let offers = this.ragfairOfferService.getOffersOfType(getPriceRequest.templateId);
// Offers exist for item, get averages of what's listed // Offers exist for item, get averages of what's listed
if (typeof(offers) === "object" && offers.length > 0) if (typeof offers === "object" && offers.length > 0)
{ {
offers = this.ragfairSortHelper.sortOffers(offers, RagfairSort.PRICE); offers = this.ragfairSortHelper.sortOffers(offers, RagfairSort.PRICE);
const min = offers[0].requirementsCost; // Get first item from array as its pre-sorted const min = offers[0].requirementsCost; // Get first item from array as its pre-sorted
@ -310,10 +329,11 @@ export class RagfairController
return { return {
avg: (min + max) / 2, avg: (min + max) / 2,
min: min, min: min,
max: max max: max,
}; };
} }
else // No offers listed, get price from live ragfair price list prices.json // No offers listed, get price from live ragfair price list prices.json
else
{ {
const templatesDb = this.databaseServer.getTables().templates; const templatesDb = this.databaseServer.getTables().templates;
@ -327,7 +347,7 @@ export class RagfairController
return { return {
avg: tplPrice, avg: tplPrice,
min: tplPrice, min: tplPrice,
max: tplPrice max: tplPrice,
}; };
} }
} }
@ -339,7 +359,11 @@ export class RagfairController
* @param sessionID Session id * @param sessionID Session id
* @returns IItemEventRouterResponse * @returns IItemEventRouterResponse
*/ */
public addPlayerOffer(pmcData: IPmcData, offerRequest: IAddOfferRequestData, sessionID: string): IItemEventRouterResponse public addPlayerOffer(
pmcData: IPmcData,
offerRequest: IAddOfferRequestData,
sessionID: string,
): IItemEventRouterResponse
{ {
const output = this.eventOutputHolder.getOutput(sessionID); const output = this.eventOutputHolder.getOutput(sessionID);
@ -351,7 +375,11 @@ 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 getItemsFromInventoryErrorMessage = ""; const getItemsFromInventoryErrorMessage = "";
const itemsInInventoryToList = this.getItemsToListOnFleaFromInventory(pmcData, offerRequest.items, getItemsFromInventoryErrorMessage); const itemsInInventoryToList = this.getItemsToListOnFleaFromInventory(
pmcData,
offerRequest.items,
getItemsFromInventoryErrorMessage,
);
if (!itemsInInventoryToList) if (!itemsInInventoryToList)
{ {
this.httpResponse.appendErrorToOutput(output, getItemsFromInventoryErrorMessage); this.httpResponse.appendErrorToOutput(output, getItemsFromInventoryErrorMessage);
@ -360,32 +388,52 @@ export class RagfairController
// Checks are done, create the offer // Checks are done, create the offer
const playerListedPriceInRub = this.calculateRequirementsPriceInRub(offerRequest.requirements); const playerListedPriceInRub = this.calculateRequirementsPriceInRub(offerRequest.requirements);
const fullProfile = this.saveServer.getProfile(sessionID); const fullProfile = this.saveServer.getProfile(sessionID);
const offer = this.createPlayerOffer(fullProfile, offerRequest.requirements, this.ragfairHelper.mergeStackable(itemsInInventoryToList), offerRequest.sellInOnePiece, playerListedPriceInRub); const offer = this.createPlayerOffer(
fullProfile,
offerRequest.requirements,
this.ragfairHelper.mergeStackable(itemsInInventoryToList),
offerRequest.sellInOnePiece,
playerListedPriceInRub,
);
const rootItem = offer.items[0]; const rootItem = offer.items[0];
const qualityMultiplier = this.itemHelper.getItemQualityModifier(rootItem); const qualityMultiplier = this.itemHelper.getItemQualityModifier(rootItem);
const averageOfferPrice = this.ragfairPriceService.getFleaPriceForItem(rootItem._tpl) * rootItem.upd.StackObjectsCount * qualityMultiplier; const averageOfferPrice = this.ragfairPriceService.getFleaPriceForItem(rootItem._tpl) *
const itemStackCount = (offerRequest.sellInOnePiece) rootItem.upd.StackObjectsCount * qualityMultiplier;
? 1 const itemStackCount = (offerRequest.sellInOnePiece) ?
: rootItem.upd.StackObjectsCount; 1 :
rootItem.upd.StackObjectsCount;
// Get averaged price of a single item being listed // Get averaged price of a single item being listed
const averageSingleItemPrice = (offerRequest.sellInOnePiece) const averageSingleItemPrice = (offerRequest.sellInOnePiece) ?
? averageOfferPrice / rootItem.upd.StackObjectsCount // Packs are a single offer made of many items averageOfferPrice / rootItem.upd.StackObjectsCount // Packs are a single offer made of many items
: averageOfferPrice / itemStackCount; :
averageOfferPrice / itemStackCount;
// Get averaged price of listing // Get averaged price of listing
const averagePlayerListedPriceInRub = (offerRequest.sellInOnePiece) const averagePlayerListedPriceInRub = (offerRequest.sellInOnePiece) ?
? playerListedPriceInRub / rootItem.upd.StackObjectsCount playerListedPriceInRub / rootItem.upd.StackObjectsCount :
: playerListedPriceInRub; playerListedPriceInRub;
// Packs are reduced to the average price of a single item in the pack vs the averaged single price of an item // Packs are reduced to the average price of a single item in the pack vs the averaged single price of an item
const sellChancePercent = this.ragfairSellHelper.calculateSellChance(averageSingleItemPrice, averagePlayerListedPriceInRub, qualityMultiplier); const sellChancePercent = this.ragfairSellHelper.calculateSellChance(
averageSingleItemPrice,
averagePlayerListedPriceInRub,
qualityMultiplier,
);
offer.sellResult = this.ragfairSellHelper.rollForSale(sellChancePercent, itemStackCount); offer.sellResult = this.ragfairSellHelper.rollForSale(sellChancePercent, itemStackCount);
// Subtract flea market fee from stash // Subtract flea market fee from stash
if (this.ragfairConfig.sell.fees) if (this.ragfairConfig.sell.fees)
{ {
const taxFeeChargeFailed = this.chargePlayerTaxFee(sessionID, rootItem, pmcData, playerListedPriceInRub, itemStackCount, offerRequest, output); const taxFeeChargeFailed = this.chargePlayerTaxFee(
sessionID,
rootItem,
pmcData,
playerListedPriceInRub,
itemStackCount,
offerRequest,
output,
);
if (taxFeeChargeFailed) if (taxFeeChargeFailed)
{ {
return output; return output;
@ -415,24 +463,41 @@ export class RagfairController
* @param output IItemEventRouterResponse * @param output IItemEventRouterResponse
* @returns True if charging tax to player failed * @returns True if charging tax to player failed
*/ */
protected chargePlayerTaxFee(sessionID: string, rootItem: Item, pmcData: IPmcData, requirementsPriceInRub: number, itemStackCount: number, offerRequest: IAddOfferRequestData, output: IItemEventRouterResponse): boolean protected chargePlayerTaxFee(
sessionID: string,
rootItem: Item,
pmcData: IPmcData,
requirementsPriceInRub: number,
itemStackCount: number,
offerRequest: IAddOfferRequestData,
output: IItemEventRouterResponse,
): boolean
{ {
// Get tax from cache hydrated earlier by client, if that's missing fall back to server calculation (inaccurate) // Get tax from cache hydrated earlier by client, if that's missing fall back to server calculation (inaccurate)
const storedClientTaxValue = this.ragfairTaxService.getStoredClientOfferTaxValueById(offerRequest.items[0]); const storedClientTaxValue = this.ragfairTaxService.getStoredClientOfferTaxValueById(offerRequest.items[0]);
const tax = storedClientTaxValue const tax = storedClientTaxValue ?
? storedClientTaxValue.fee storedClientTaxValue.fee :
: this.ragfairTaxService.calculateTax(rootItem, pmcData, requirementsPriceInRub, itemStackCount, offerRequest.sellInOnePiece); this.ragfairTaxService.calculateTax(
rootItem,
pmcData,
requirementsPriceInRub,
itemStackCount,
offerRequest.sellInOnePiece,
);
this.logger.debug(`Offer tax to charge: ${tax}, pulled from client: ${(!!storedClientTaxValue)}`); this.logger.debug(`Offer tax to charge: ${tax}, pulled from client: ${(!!storedClientTaxValue)}`);
//cleanup of cache now we've used the tax value from it // cleanup of cache now we've used the tax value from it
this.ragfairTaxService.clearStoredOfferTaxById(offerRequest.items[0]); this.ragfairTaxService.clearStoredOfferTaxById(offerRequest.items[0]);
const buyTradeRequest = this.createBuyTradeRequestObject("RUB", tax); const buyTradeRequest = this.createBuyTradeRequestObject("RUB", tax);
output = this.paymentService.payMoney(pmcData, buyTradeRequest, sessionID, output); output = this.paymentService.payMoney(pmcData, buyTradeRequest, sessionID, output);
if (output.warnings.length > 0) if (output.warnings.length > 0)
{ {
output = this.httpResponse.appendErrorToOutput(output, this.localisationService.getText("ragfair-unable_to_pay_commission_fee", tax)); output = this.httpResponse.appendErrorToOutput(
output,
this.localisationService.getText("ragfair-unable_to_pay_commission_fee", tax),
);
return true; return true;
} }
@ -484,7 +549,8 @@ export class RagfairController
} }
else else
{ {
requirementsPriceInRub += this.ragfairPriceService.getDynamicPriceForItem(requestedItemTpl) * item.count; requirementsPriceInRub += this.ragfairPriceService.getDynamicPriceForItem(requestedItemTpl) *
item.count;
} }
} }
@ -492,22 +558,28 @@ export class RagfairController
} }
/** /**
* Using item ids from flea offer request, find corrispnding items from player inventory and return as array * Using item ids from flea offer request, find corresponding items from player inventory and return as array
* @param pmcData Player profile * @param pmcData Player profile
* @param itemIdsFromFleaOfferRequest Ids from request * @param itemIdsFromFleaOfferRequest Ids from request
* @param errorMessage if item is not found, add error message to this parameter * @param errorMessage if item is not found, add error message to this parameter
* @returns Array of items from player inventory * @returns Array of items from player inventory
*/ */
protected getItemsToListOnFleaFromInventory(pmcData: IPmcData, itemIdsFromFleaOfferRequest: string[], errorMessage: string): Item[] protected getItemsToListOnFleaFromInventory(
pmcData: IPmcData,
itemIdsFromFleaOfferRequest: string[],
errorMessage: string,
): Item[]
{ {
const itemsToReturn = []; const itemsToReturn = [];
// 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", {id: itemId}); errorMessage = this.localisationService.getText("ragfair-unable_to_find_item_in_inventory", {
id: itemId,
});
this.logger.error(errorMessage); this.logger.error(errorMessage);
return null; return null;
@ -528,28 +600,34 @@ export class RagfairController
return itemsToReturn; return itemsToReturn;
} }
public createPlayerOffer(profile: IAkiProfile, requirements: Requirement[], items: Item[], sellInOnePiece: boolean, amountToSend: number): IRagfairOffer public createPlayerOffer(
profile: IAkiProfile,
requirements: Requirement[],
items: Item[],
sellInOnePiece: boolean,
amountToSend: number,
): IRagfairOffer
{ {
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,
_tpl: item._tpl, _tpl: item._tpl,
parentId: (isChild) ? item.parentId : "hideout", parentId: isChild ? item.parentId : "hideout",
slotId: (isChild) ? item.slotId : "hideout", slotId: isChild ? item.slotId : "hideout",
upd: item.upd upd: item.upd,
}; };
}); });
const formattedRequirements: IBarterScheme[] = requirements.map(item => const formattedRequirements: IBarterScheme[] = requirements.map((item) =>
{ {
return { return {
_tpl: item._tpl, _tpl: item._tpl,
count: item.count, count: item.count,
onlyFunctional: item.onlyFunctional onlyFunctional: item.onlyFunctional,
}; };
}); });
@ -559,7 +637,7 @@ export class RagfairController
formattedItems, formattedItems,
formattedRequirements, formattedRequirements,
loyalLevel, loyalLevel,
sellInOnePiece sellInOnePiece,
); );
} }
@ -586,21 +664,32 @@ export class RagfairController
const offers = pmcData.RagfairInfo.offers; const offers = pmcData.RagfairInfo.offers;
if (!offers) if (!offers)
{ {
this.logger.warning(this.localisationService.getText("ragfair-unable_to_remove_offer_not_found_in_profile", {profileId: sessionID, offerId: offerId})); this.logger.warning(
this.localisationService.getText("ragfair-unable_to_remove_offer_not_found_in_profile", {
profileId: sessionID,
offerId: offerId,
}),
);
pmcData.RagfairInfo.offers = []; pmcData.RagfairInfo.offers = [];
} }
const index = offers.findIndex(offer => offer._id === offerId); const index = offers.findIndex((offer) => offer._id === offerId);
if (index === -1) if (index === -1)
{ {
this.logger.error(this.localisationService.getText("ragfair-offer_not_found_in_profile", {offerId: offerId})); this.logger.error(
return this.httpResponse.appendErrorToOutput(this.eventOutputHolder.getOutput(sessionID), this.localisationService.getText("ragfair-offer_not_found_in_profile_short")); this.localisationService.getText("ragfair-offer_not_found_in_profile", {offerId: offerId}),
);
return this.httpResponse.appendErrorToOutput(
this.eventOutputHolder.getOutput(sessionID),
this.localisationService.getText("ragfair-offer_not_found_in_profile_short"),
);
} }
const differenceInSeconds = (offers[index].endTime - this.timeUtil.getTimestamp()); const differenceInSeconds = offers[index].endTime - this.timeUtil.getTimestamp();
if (differenceInSeconds > this.ragfairConfig.sell.expireSeconds)// expireSeconds Default is 71 seconds if (differenceInSeconds > this.ragfairConfig.sell.expireSeconds)
{ {
// expireSeconds Default is 71 seconds
const newEndTime = this.ragfairConfig.sell.expireSeconds + this.timeUtil.getTimestamp(); const newEndTime = this.ragfairConfig.sell.expireSeconds + this.timeUtil.getTimestamp();
offers[index].endTime = Math.round(newEndTime); offers[index].endTime = Math.round(newEndTime);
} }
@ -613,26 +702,42 @@ export class RagfairController
let output = this.eventOutputHolder.getOutput(sessionID); let output = this.eventOutputHolder.getOutput(sessionID);
const pmcData = this.saveServer.getProfile(sessionID).characters.pmc; const pmcData = this.saveServer.getProfile(sessionID).characters.pmc;
const offers = pmcData.RagfairInfo.offers; const offers = pmcData.RagfairInfo.offers;
const index = offers.findIndex(offer => offer._id === info.offerId); const index = offers.findIndex((offer) => offer._id === info.offerId);
const secondsToAdd = info.renewalTime * TimeUtil.oneHourAsSeconds; const secondsToAdd = info.renewalTime * TimeUtil.oneHourAsSeconds;
if (index === -1) if (index === -1)
{ {
this.logger.warning(this.localisationService.getText("ragfair-offer_not_found_in_profile", {offerId: info.offerId})); this.logger.warning(
return this.httpResponse.appendErrorToOutput(this.eventOutputHolder.getOutput(sessionID), this.localisationService.getText("ragfair-offer_not_found_in_profile_short")); this.localisationService.getText("ragfair-offer_not_found_in_profile", {offerId: info.offerId}),
);
return this.httpResponse.appendErrorToOutput(
this.eventOutputHolder.getOutput(sessionID),
this.localisationService.getText("ragfair-offer_not_found_in_profile_short"),
);
} }
// MOD: Pay flea market fee // MOD: Pay flea market fee
if (this.ragfairConfig.sell.fees) if (this.ragfairConfig.sell.fees)
{ {
const count = offers[index].sellInOnePiece ? 1 : offers[index].items.reduce((sum, item) => sum += item.upd.StackObjectsCount, 0); const count = offers[index].sellInOnePiece ?
const tax = this.ragfairTaxService.calculateTax(offers[index].items[0], this.profileHelper.getPmcProfile(sessionID), offers[index].requirementsCost, count, offers[index].sellInOnePiece); 1 :
offers[index].items.reduce((sum, item) => sum += item.upd.StackObjectsCount, 0);
const tax = this.ragfairTaxService.calculateTax(
offers[index].items[0],
this.profileHelper.getPmcProfile(sessionID),
offers[index].requirementsCost,
count,
offers[index].sellInOnePiece,
);
const request = this.createBuyTradeRequestObject("RUB", tax); const request = this.createBuyTradeRequestObject("RUB", tax);
output = this.paymentService.payMoney(pmcData, request, sessionID, output); output = this.paymentService.payMoney(pmcData, request, sessionID, output);
if (output.warnings.length > 0) if (output.warnings.length > 0)
{ {
return this.httpResponse.appendErrorToOutput(output, this.localisationService.getText("ragfair-unable_to_pay_commission_fee")); return this.httpResponse.appendErrorToOutput(
output,
this.localisationService.getText("ragfair-unable_to_pay_commission_fee"),
);
} }
} }
@ -652,19 +757,16 @@ export class RagfairController
return { return {
tid: "ragfair", tid: "ragfair",
Action: "TradingConfirm", Action: "TradingConfirm",
// eslint-disable-next-line @typescript-eslint/naming-convention
scheme_items: [ scheme_items: [
{ {
id: this.paymentHelper.getCurrency(currency), id: this.paymentHelper.getCurrency(currency),
count: Math.round(value) count: Math.round(value),
} },
], ],
type: "", type: "",
// eslint-disable-next-line @typescript-eslint/naming-convention
item_id: "", item_id: "",
count: 0, count: 0,
// eslint-disable-next-line @typescript-eslint/naming-convention scheme_id: 0,
scheme_id: 0
}; };
} }
} }

View File

@ -27,9 +27,9 @@ export class RepairController
@inject("TraderHelper") protected traderHelper: TraderHelper, @inject("TraderHelper") protected traderHelper: TraderHelper,
@inject("PaymentService") protected paymentService: PaymentService, @inject("PaymentService") protected paymentService: PaymentService,
@inject("RepairHelper") protected repairHelper: RepairHelper, @inject("RepairHelper") protected repairHelper: RepairHelper,
@inject("RepairService") protected repairService: RepairService @inject("RepairService") protected repairService: RepairService,
) )
{ } {}
/** /**
* Handle TraderRepair event * Handle TraderRepair event
@ -39,7 +39,11 @@ export class RepairController
* @param pmcData player profile * @param pmcData player profile
* @returns item event router action * @returns item event router action
*/ */
public traderRepair(sessionID: string, body: ITraderRepairActionDataRequest, pmcData: IPmcData): IItemEventRouterResponse public traderRepair(
sessionID: string,
body: ITraderRepairActionDataRequest,
pmcData: IPmcData,
): IItemEventRouterResponse
{ {
const output = this.eventOutputHolder.getOutput(sessionID); const output = this.eventOutputHolder.getOutput(sessionID);
@ -48,7 +52,14 @@ export class RepairController
{ {
const repairDetails = this.repairService.repairItemByTrader(sessionID, pmcData, repairItem, body.tid); const repairDetails = this.repairService.repairItemByTrader(sessionID, pmcData, repairItem, body.tid);
this.repairService.payForRepair(sessionID, pmcData, repairItem._id, repairDetails.repairCost, body.tid, output); this.repairService.payForRepair(
sessionID,
pmcData,
repairItem._id,
repairDetails.repairCost,
body.tid,
output,
);
if (output.warnings.length > 0) if (output.warnings.length > 0)
{ {
@ -78,7 +89,13 @@ export class RepairController
const output = this.eventOutputHolder.getOutput(sessionID); const output = this.eventOutputHolder.getOutput(sessionID);
// repair item // repair item
const repairDetails = this.repairService.repairItemByKit(sessionID, pmcData, body.repairKitsInfo, body.target, output); const repairDetails = this.repairService.repairItemByKit(
sessionID,
pmcData,
body.repairKitsInfo,
body.target,
output,
);
this.repairService.addBuffToItem(repairDetails, pmcData); this.repairService.addBuffToItem(repairDetails, pmcData);

View File

@ -7,7 +7,11 @@ import { RagfairServerHelper } from "@spt-aki/helpers/RagfairServerHelper";
import { RepeatableQuestHelper } from "@spt-aki/helpers/RepeatableQuestHelper"; import { RepeatableQuestHelper } from "@spt-aki/helpers/RepeatableQuestHelper";
import { IEmptyRequestData } from "@spt-aki/models/eft/common/IEmptyRequestData"; import { IEmptyRequestData } from "@spt-aki/models/eft/common/IEmptyRequestData";
import { IPmcData } from "@spt-aki/models/eft/common/IPmcData"; import { IPmcData } from "@spt-aki/models/eft/common/IPmcData";
import { IChangeRequirement, IPmcDataRepeatableQuest, IRepeatableQuest } from "@spt-aki/models/eft/common/tables/IRepeatableQuests"; import {
IChangeRequirement,
IPmcDataRepeatableQuest,
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 { IRepeatableQuestChangeRequest } from "@spt-aki/models/eft/quests/IRepeatableQuestChangeRequest"; import { IRepeatableQuestChangeRequest } from "@spt-aki/models/eft/quests/IRepeatableQuestChangeRequest";
import { ConfigTypes } from "@spt-aki/models/enums/ConfigTypes"; import { ConfigTypes } from "@spt-aki/models/enums/ConfigTypes";
@ -50,13 +54,12 @@ export class RepeatableQuestController
@inject("RepeatableQuestGenerator") protected repeatableQuestGenerator: RepeatableQuestGenerator, @inject("RepeatableQuestGenerator") protected repeatableQuestGenerator: RepeatableQuestGenerator,
@inject("RepeatableQuestHelper") protected repeatableQuestHelper: RepeatableQuestHelper, @inject("RepeatableQuestHelper") protected repeatableQuestHelper: RepeatableQuestHelper,
@inject("QuestHelper") protected questHelper: QuestHelper, @inject("QuestHelper") protected questHelper: QuestHelper,
@inject("ConfigServer") protected configServer: ConfigServer @inject("ConfigServer") protected configServer: ConfigServer,
) )
{ {
this.questConfig = this.configServer.getConfig(ConfigTypes.QUEST); this.questConfig = this.configServer.getConfig(ConfigTypes.QUEST);
} }
/** /**
* Handle client/repeatalbeQuests/activityPeriods * Handle client/repeatalbeQuests/activityPeriods
* Returns an array of objects in the format of repeatable quests to the client. * Returns an array of objects in the format of repeatable quests to the client.
@ -69,8 +72,8 @@ export class RepeatableQuestController
* } * }
* *
* The method checks if the player level requirement for repeatable quests (e.g. daily lvl5, weekly lvl15) is met and if the previously active quests * The method checks if the player level requirement for repeatable quests (e.g. daily lvl5, weekly lvl15) is met and if the previously active quests
* are still valid. This ischecked by endTime persisted in profile accordning to the resetTime configured for each repeatable kind (daily, weekly) * are still valid. This is checked by endTime persisted in profile according to the resetTime configured for each repeatable kind (daily, weekly)
* in QuestCondig.js * in QuestConfig.js
* *
* If the condition is met, new repeatableQuests are created, old quests (which are persisted in the profile.RepeatableQuests[i].activeQuests) are * If the condition is met, new repeatableQuests are created, old quests (which are persisted in the profile.RepeatableQuests[i].activeQuests) are
* moved to profile.RepeatableQuests[i].inactiveQuests. This memory is required to get rid of old repeatable quest data in the profile, otherwise * moved to profile.RepeatableQuests[i].inactiveQuests. This memory is required to get rid of old repeatable quest data in the profile, otherwise
@ -78,16 +81,16 @@ export class RepeatableQuestController
* (if the are on "Succeed" but not "Completed" we keep them, to allow the player to complete them and get the rewards) * (if the are on "Succeed" but not "Completed" we keep them, to allow the player to complete them and get the rewards)
* The new quests generated are again persisted in profile.RepeatableQuests * The new quests generated are again persisted in profile.RepeatableQuests
* *
*
* @param {string} sessionId Player's session id * @param {string} sessionId Player's session id
* @returns {array} array of "repeatableQuestObjects" as descibed above * @returns {array} array of "repeatableQuestObjects" as described above
*/ */
public getClientRepeatableQuests(_info: IEmptyRequestData, sessionID: string): IPmcDataRepeatableQuest[] public getClientRepeatableQuests(_info: IEmptyRequestData, sessionID: string): IPmcDataRepeatableQuest[]
{ {
const returnData: Array<IPmcDataRepeatableQuest> = []; const returnData: Array<IPmcDataRepeatableQuest> = [];
const pmcData = this.profileHelper.getPmcProfile(sessionID); const pmcData = this.profileHelper.getPmcProfile(sessionID);
const time = this.timeUtil.getTimestamp(); const time = this.timeUtil.getTimestamp();
const scavQuestUnlocked = pmcData?.Hideout?.Areas?.find(hideoutArea => hideoutArea.type === HideoutAreas.INTEL_CENTER)?.level >= 1; const scavQuestUnlocked =
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)
@ -95,9 +98,11 @@ export class RepeatableQuestController
// get daily/weekly data from profile, add empty object if missing // get daily/weekly data from profile, add empty object if missing
const currentRepeatableQuestType = this.getRepeatableQuestSubTypeFromProfile(repeatableConfig, pmcData); const currentRepeatableQuestType = this.getRepeatableQuestSubTypeFromProfile(repeatableConfig, pmcData);
if (repeatableConfig.side === "Pmc" if (
&& pmcData.Info.Level >= repeatableConfig.minPlayerLevel repeatableConfig.side === "Pmc" &&
|| repeatableConfig.side === "Scav" && scavQuestUnlocked) pmcData.Info.Level >= repeatableConfig.minPlayerLevel ||
repeatableConfig.side === "Scav" && scavQuestUnlocked
)
{ {
if (time > currentRepeatableQuestType.endTime - 1) if (time > currentRepeatableQuestType.endTime - 1)
{ {
@ -110,22 +115,24 @@ export class RepeatableQuestController
// after a raid (the client seems to keep quests internally and we want to get rid of old repeatable quests) // after a raid (the client seems to keep quests internally and we want to get rid of old repeatable quests)
// and remove them from the PMC's Quests and RepeatableQuests[i].activeQuests // and remove them from the PMC's Quests and RepeatableQuests[i].activeQuests
const questsToKeep = []; const questsToKeep = [];
//for (let i = 0; i < currentRepeatable.activeQuests.length; i++) // for (let i = 0; i < currentRepeatable.activeQuests.length; i++)
for (const activeQuest of currentRepeatableQuestType.activeQuests) for (const activeQuest of currentRepeatableQuestType.activeQuests)
{ {
// check if the quest is ready to be completed, if so, don't remove it // check if the quest is ready to be completed, if so, don't remove it
const quest = pmcData.Quests.filter(q => q.qid === activeQuest._id); const quest = pmcData.Quests.filter((q) => q.qid === activeQuest._id);
if (quest.length > 0) if (quest.length > 0)
{ {
if (quest[0].status === QuestStatus.AvailableForFinish) if (quest[0].status === QuestStatus.AvailableForFinish)
{ {
questsToKeep.push(activeQuest); questsToKeep.push(activeQuest);
this.logger.debug(`Keeping repeatable quest ${activeQuest._id} in activeQuests since it is available to AvailableForFinish`); this.logger.debug(
`Keeping repeatable quest ${activeQuest._id} in activeQuests since it is available to AvailableForFinish`,
);
continue; continue;
} }
} }
this.profileFixerService.removeDanglingConditionCounters(pmcData); this.profileFixerService.removeDanglingConditionCounters(pmcData);
pmcData.Quests = pmcData.Quests.filter(q => q.qid !== activeQuest._id); pmcData.Quests = pmcData.Quests.filter((q) => q.qid !== activeQuest._id);
currentRepeatableQuestType.inactiveQuests.push(activeQuest); currentRepeatableQuestType.inactiveQuests.push(activeQuest);
} }
currentRepeatableQuestType.activeQuests = questsToKeep; currentRepeatableQuestType.activeQuests = questsToKeep;
@ -144,12 +151,14 @@ export class RepeatableQuestController
pmcData.Info.Level, pmcData.Info.Level,
pmcData.TradersInfo, pmcData.TradersInfo,
questTypePool, questTypePool,
repeatableConfig repeatableConfig,
); );
lifeline++; lifeline++;
if (lifeline > 10) if (lifeline > 10)
{ {
this.logger.debug("We were stuck in repeatable quest generation. This should never happen. Please report"); this.logger.debug(
"We were stuck in repeatable quest generation. This should never happen. Please report",
);
break; break;
} }
} }
@ -174,7 +183,7 @@ export class RepeatableQuestController
{ {
currentRepeatableQuestType.changeRequirement[quest._id] = { currentRepeatableQuestType.changeRequirement[quest._id] = {
changeCost: quest.changeCost, changeCost: quest.changeCost,
changeStandingCost: this.randomUtil.getArrayValue([0, 0.01]) changeStandingCost: this.randomUtil.getArrayValue([0, 0.01]),
}; };
} }
@ -184,7 +193,7 @@ export class RepeatableQuestController
endTime: currentRepeatableQuestType.endTime, endTime: currentRepeatableQuestType.endTime,
activeQuests: currentRepeatableQuestType.activeQuests, activeQuests: currentRepeatableQuestType.activeQuests,
inactiveQuests: currentRepeatableQuestType.inactiveQuests, inactiveQuests: currentRepeatableQuestType.inactiveQuests,
changeRequirement: currentRepeatableQuestType.changeRequirement changeRequirement: currentRepeatableQuestType.changeRequirement,
}); });
} }
@ -199,10 +208,15 @@ export class RepeatableQuestController
*/ */
protected getQuestCount(repeatableConfig: IRepeatableQuestConfig, pmcData: IPmcData): number protected getQuestCount(repeatableConfig: IRepeatableQuestConfig, pmcData: IPmcData): number
{ {
if (repeatableConfig.name.toLowerCase() === "daily" && this.profileHelper.hasEliteSkillLevel(SkillTypes.CHARISMA, pmcData)) if (
repeatableConfig.name.toLowerCase() === "daily" &&
this.profileHelper.hasEliteSkillLevel(SkillTypes.CHARISMA, pmcData)
)
{ {
// Elite charisma skill gives extra daily quest(s) // Elite charisma skill gives extra daily quest(s)
return repeatableConfig.numQuests + this.databaseServer.getTables().globals.config.SkillsSettings.Charisma.BonusSettings.EliteBonusSettings.RepeatableQuestExtraCount; return repeatableConfig.numQuests +
this.databaseServer.getTables().globals.config.SkillsSettings.Charisma.BonusSettings.EliteBonusSettings
.RepeatableQuestExtraCount;
} }
return repeatableConfig.numQuests; return repeatableConfig.numQuests;
@ -214,10 +228,13 @@ export class RepeatableQuestController
* @param pmcData Profile to search * @param pmcData Profile to search
* @returns IPmcDataRepeatableQuest * @returns IPmcDataRepeatableQuest
*/ */
protected getRepeatableQuestSubTypeFromProfile(repeatableConfig: IRepeatableQuestConfig, pmcData: IPmcData): IPmcDataRepeatableQuest protected getRepeatableQuestSubTypeFromProfile(
repeatableConfig: IRepeatableQuestConfig,
pmcData: IPmcData,
): 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 = {
@ -226,7 +243,7 @@ export class RepeatableQuestController
activeQuests: [], activeQuests: [],
inactiveQuests: [], inactiveQuests: [],
endTime: 0, endTime: 0,
changeRequirement: {} changeRequirement: {},
}; };
// Add base object that holds repeatable data to profile // Add base object that holds repeatable data to profile
@ -297,16 +314,16 @@ export class RepeatableQuestController
// Target is boss // Target is boss
if (probabilityObject.data.isBoss) if (probabilityObject.data.isBoss)
{ {
questPool.pool.Elimination.targets[probabilityObject.key] = { locations: ["any"] }; questPool.pool.Elimination.targets[probabilityObject.key] = {locations: ["any"]};
} }
else else
{ {
const possibleLocations = Object.keys(repeatableConfig.locations); const possibleLocations = Object.keys(repeatableConfig.locations);
// Set possible locations for elimination task, ift arget 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] = (probabilityObject.key === "Savage") ?
? { locations: possibleLocations.filter(x => x !== "laboratory")} {locations: possibleLocations.filter((x) => x !== "laboratory")} :
: { locations: possibleLocations }; {locations: possibleLocations};
} }
} }
@ -319,15 +336,15 @@ export class RepeatableQuestController
types: repeatableConfig.types.slice(), types: repeatableConfig.types.slice(),
pool: { pool: {
Exploration: { Exploration: {
locations: {} locations: {},
}, },
Elimination: { Elimination: {
targets: {} targets: {},
}, },
Pickup: { Pickup: {
locations: {} locations: {},
} },
} },
}; };
} }
@ -355,7 +372,11 @@ export class RepeatableQuestController
/** /**
* Handle RepeatableQuestChange event * Handle RepeatableQuestChange event
*/ */
public changeRepeatableQuest(pmcData: IPmcData, changeRequest: IRepeatableQuestChangeRequest, sessionID: string): IItemEventRouterResponse public changeRepeatableQuest(
pmcData: IPmcData,
changeRequest: IRepeatableQuestChangeRequest,
sessionID: string,
): IItemEventRouterResponse
{ {
let repeatableToChange: IPmcDataRepeatableQuest; let repeatableToChange: IPmcDataRepeatableQuest;
let changeRequirement: IChangeRequirement; let changeRequirement: IChangeRequirement;
@ -367,7 +388,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;
@ -377,14 +398,19 @@ 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 => x._id !== changeRequest.qid); currentRepeatablePool.activeQuests = currentRepeatablePool.activeQuests.filter((x) =>
x._id !== changeRequest.qid
);
// Get cost to replace existing quest // Get cost to replace existing quest
changeRequirement = this.jsonUtil.clone(currentRepeatablePool.changeRequirement[changeRequest.qid]); changeRequirement = this.jsonUtil.clone(currentRepeatablePool.changeRequirement[changeRequest.qid]);
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)
const repeatableConfig = this.questConfig.repeatableQuests.find(x => x.name === currentRepeatablePool.name); // TODO: somehow we need to reduce the questPool by the currently active quests (for all repeatable)
const repeatableConfig = this.questConfig.repeatableQuests.find((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);
if (newRepeatableQuest) if (newRepeatableQuest)
@ -394,7 +420,7 @@ export class RepeatableQuestController
currentRepeatablePool.activeQuests.push(newRepeatableQuest); currentRepeatablePool.activeQuests.push(newRepeatableQuest);
currentRepeatablePool.changeRequirement[newRepeatableQuest._id] = { currentRepeatablePool.changeRequirement[newRepeatableQuest._id] = {
changeCost: newRepeatableQuest.changeCost, changeCost: newRepeatableQuest.changeCost,
changeStandingCost: this.randomUtil.getArrayValue([0, 0.01]) changeStandingCost: this.randomUtil.getArrayValue([0, 0.01]),
}; };
const fullProfile = this.profileHelper.getFullProfile(sessionID); const fullProfile = this.profileHelper.getFullProfile(sessionID);
@ -403,7 +429,10 @@ export class RepeatableQuestController
this.questHelper.findAndRemoveQuestFromArrayIfExists(questToReplace._id, pmcData.Quests); this.questHelper.findAndRemoveQuestFromArrayIfExists(questToReplace._id, pmcData.Quests);
// Find quest we're replacing in scav profile quests array and remove it // Find quest we're replacing in scav profile quests array and remove it
this.questHelper.findAndRemoveQuestFromArrayIfExists(questToReplace._id, fullProfile.characters.scav?.Quests ?? []); this.questHelper.findAndRemoveQuestFromArrayIfExists(
questToReplace._id,
fullProfile.characters.scav?.Quests ?? [],
);
} }
// Found and replaced the quest in current repeatable // Found and replaced the quest in current repeatable
@ -442,7 +471,11 @@ export class RepeatableQuestController
return output; return output;
} }
protected attemptToGenerateRepeatableQuest(pmcData: IPmcData, questTypePool: IQuestTypePool, repeatableConfig: IRepeatableQuestConfig): IRepeatableQuest protected attemptToGenerateRepeatableQuest(
pmcData: IPmcData,
questTypePool: IQuestTypePool,
repeatableConfig: IRepeatableQuestConfig,
): IRepeatableQuest
{ {
let newRepeatableQuest: IRepeatableQuest = null; let newRepeatableQuest: IRepeatableQuest = null;
let attemptsToGenerateQuest = 0; let attemptsToGenerateQuest = 0;
@ -452,16 +485,17 @@ export class RepeatableQuestController
pmcData.Info.Level, pmcData.Info.Level,
pmcData.TradersInfo, pmcData.TradersInfo,
questTypePool, questTypePool,
repeatableConfig repeatableConfig,
); );
attemptsToGenerateQuest++; attemptsToGenerateQuest++;
if (attemptsToGenerateQuest > 10) if (attemptsToGenerateQuest > 10)
{ {
this.logger.debug("We were stuck in repeatable quest generation. This should never happen. Please report"); this.logger.debug(
"We were stuck in repeatable quest generation. This should never happen. Please report",
);
break; break;
} }
} }
return newRepeatableQuest; return newRepeatableQuest;
} }
} }

View File

@ -29,7 +29,7 @@ import { HttpResponseUtil } from "@spt-aki/utils/HttpResponseUtil";
import { JsonUtil } from "@spt-aki/utils/JsonUtil"; import { JsonUtil } from "@spt-aki/utils/JsonUtil";
@injectable() @injectable()
class TradeController export class TradeController
{ {
protected ragfairConfig: IRagfairConfig; protected ragfairConfig: IRagfairConfig;
protected traderConfig: ITraderConfig; protected traderConfig: ITraderConfig;
@ -46,7 +46,7 @@ class TradeController
@inject("HttpResponseUtil") protected httpResponse: HttpResponseUtil, @inject("HttpResponseUtil") protected httpResponse: HttpResponseUtil,
@inject("LocalisationService") protected localisationService: LocalisationService, @inject("LocalisationService") protected localisationService: LocalisationService,
@inject("RagfairPriceService") protected ragfairPriceService: RagfairPriceService, @inject("RagfairPriceService") protected ragfairPriceService: RagfairPriceService,
@inject("ConfigServer") protected configServer: ConfigServer @inject("ConfigServer") protected configServer: ConfigServer,
) )
{ {
this.ragfairConfig = this.configServer.getConfig(ConfigTypes.RAGFAIR); this.ragfairConfig = this.configServer.getConfig(ConfigTypes.RAGFAIR);
@ -54,13 +54,21 @@ class TradeController
} }
/** Handle TradingConfirm event */ /** Handle TradingConfirm event */
public confirmTrading(pmcData: IPmcData, request: IProcessBaseTradeRequestData, sessionID: string): IItemEventRouterResponse public confirmTrading(
pmcData: IPmcData,
request: IProcessBaseTradeRequestData,
sessionID: string,
): IItemEventRouterResponse
{ {
return this.confirmTradingInternal(pmcData, request, sessionID, this.traderConfig.purchasesAreFoundInRaid); return this.confirmTradingInternal(pmcData, request, sessionID, this.traderConfig.purchasesAreFoundInRaid);
} }
/** Handle RagFairBuyOffer event */ /** Handle RagFairBuyOffer event */
public confirmRagfairTrading(pmcData: IPmcData, body: IProcessRagfairTradeRequestData, sessionID: string): IItemEventRouterResponse public confirmRagfairTrading(
pmcData: IPmcData,
body: IProcessRagfairTradeRequestData,
sessionID: string,
): IItemEventRouterResponse
{ {
let output = this.eventOutputHolder.getOutput(sessionID); let output = this.eventOutputHolder.getOutput(sessionID);
@ -69,19 +77,33 @@ class TradeController
const fleaOffer = this.ragfairServer.getOffer(offer.id); const fleaOffer = this.ragfairServer.getOffer(offer.id);
if (!fleaOffer) if (!fleaOffer)
{ {
return this.httpResponse.appendErrorToOutput(output, `Offer with ID ${offer.id} not found`, BackendErrorCodes.OFFERNOTFOUND); return this.httpResponse.appendErrorToOutput(
output,
`Offer with ID ${offer.id} not found`,
BackendErrorCodes.OFFERNOTFOUND,
);
} }
if (offer.count === 0) if (offer.count === 0)
{ {
const errorMessage = this.localisationService.getText("ragfair-unable_to_purchase_0_count_item", this.itemHelper.getItem(fleaOffer.items[0]._tpl)[1]._name); const errorMessage = this.localisationService.getText(
"ragfair-unable_to_purchase_0_count_item",
this.itemHelper.getItem(fleaOffer.items[0]._tpl)[1]._name,
);
return this.httpResponse.appendErrorToOutput(output, errorMessage, BackendErrorCodes.OFFEROUTOFSTOCK); return this.httpResponse.appendErrorToOutput(output, errorMessage, BackendErrorCodes.OFFEROUTOFSTOCK);
} }
// Skip buying items when player doesn't have necessary loyalty // Skip buying items when player doesn't have necessary loyalty
if (fleaOffer.user.memberType === MemberCategory.TRADER && fleaOffer.loyaltyLevel > pmcData.TradersInfo[fleaOffer.user.id].loyaltyLevel) if (
fleaOffer.user.memberType === MemberCategory.TRADER &&
fleaOffer.loyaltyLevel > pmcData.TradersInfo[fleaOffer.user.id].loyaltyLevel
)
{ {
this.logger.debug(`Unable to buy item: ${fleaOffer.items[0]._tpl} from trader: ${fleaOffer.user.id} as loyalty level too low, skipping`); this.logger.debug(
`Unable to buy item: ${
fleaOffer.items[0]._tpl
} from trader: ${fleaOffer.user.id} as loyalty level too low, skipping`,
);
continue; continue;
} }
@ -91,17 +113,21 @@ class TradeController
Action: "TradingConfirm", Action: "TradingConfirm",
type: "buy_from_trader", type: "buy_from_trader",
tid: (fleaOffer.user.memberType !== MemberCategory.TRADER) ? "ragfair" : fleaOffer.user.id, tid: (fleaOffer.user.memberType !== MemberCategory.TRADER) ? "ragfair" : fleaOffer.user.id,
// eslint-disable-next-line @typescript-eslint/naming-convention
item_id: fleaOffer.root, item_id: fleaOffer.root,
count: offer.count, count: offer.count,
// eslint-disable-next-line @typescript-eslint/naming-convention
scheme_id: 0, scheme_id: 0,
// eslint-disable-next-line @typescript-eslint/naming-convention scheme_items: offer.items,
scheme_items: offer.items
}; };
// confirmTrading() must occur prior to removing the offer stack, otherwise item inside offer doesn't exist for confirmTrading() to use // confirmTrading() must occur prior to removing the offer stack, otherwise item inside offer doesn't exist
output = this.confirmTradingInternal(pmcData, buyData, sessionID, this.ragfairConfig.dynamic.purchasesAreFoundInRaid, fleaOffer.items[0].upd); // for confirmTrading() to use
output = this.confirmTradingInternal(
pmcData,
buyData,
sessionID,
this.ragfairConfig.dynamic.purchasesAreFoundInRaid,
fleaOffer.items[0].upd,
);
if (fleaOffer.user.memberType !== MemberCategory.TRADER) if (fleaOffer.user.memberType !== MemberCategory.TRADER)
{ {
// remove player item offer stack // remove player item offer stack
@ -113,19 +139,26 @@ class TradeController
} }
/** Handle SellAllFromSavage event */ /** Handle SellAllFromSavage event */
public sellScavItemsToFence(pmcData: IPmcData, body: ISellScavItemsToFenceRequestData, sessionId: string): IItemEventRouterResponse public sellScavItemsToFence(
pmcData: IPmcData,
body: ISellScavItemsToFenceRequestData,
sessionId: string,
): IItemEventRouterResponse
{ {
const scavProfile = this.profileHelper.getFullProfile(sessionId)?.characters?.scav; const scavProfile = this.profileHelper.getFullProfile(sessionId)?.characters?.scav;
if (!scavProfile) if (!scavProfile)
{ {
return this.httpResponse.appendErrorToOutput(this.eventOutputHolder.getOutput(sessionId), `Profile ${body.fromOwner.id} has no scav account`); return this.httpResponse.appendErrorToOutput(
this.eventOutputHolder.getOutput(sessionId),
`Profile ${body.fromOwner.id} has no scav account`,
);
} }
return this.sellInventoryToTrader(sessionId, scavProfile, pmcData, Traders.FENCE); return this.sellInventoryToTrader(sessionId, scavProfile, pmcData, Traders.FENCE);
} }
/** /**
* Sell all sellable items to a trader from inventory * Sell all items (that can be sold) to a trader from inventory
* WILL DELETE ITEMS FROM INVENTORY + CHILDREN OF ITEMS SOLD * WILL DELETE ITEMS FROM INVENTORY + CHILDREN OF ITEMS SOLD
* @param sessionId Session id * @param sessionId Session id
* @param profileWithItemsToSell Profile with items to be sold to trader * @param profileWithItemsToSell Profile with items to be sold to trader
@ -133,10 +166,15 @@ class TradeController
* @param trader Trader to sell items to * @param trader Trader to sell items to
* @returns IItemEventRouterResponse * @returns IItemEventRouterResponse
*/ */
protected sellInventoryToTrader(sessionId: string, profileWithItemsToSell: IPmcData, profileThatGetsMoney: IPmcData, trader: Traders): IItemEventRouterResponse protected sellInventoryToTrader(
sessionId: string,
profileWithItemsToSell: IPmcData,
profileThatGetsMoney: IPmcData,
trader: Traders,
): IItemEventRouterResponse
{ {
const handbookPrices = this.ragfairPriceService.getAllStaticPrices(); const handbookPrices = this.ragfairPriceService.getAllStaticPrices();
// TODO, apply trader sell bonuses? // TODO: apply trader sell bonuses?
const traderDetails = this.traderHelper.getTrader(trader, sessionId); const traderDetails = this.traderHelper.getTrader(trader, sessionId);
// Prep request object // Prep request object
@ -145,16 +183,23 @@ class TradeController
type: "sell_to_trader", type: "sell_to_trader",
tid: trader, tid: trader,
price: 0, price: 0,
items: [] items: [],
}; };
// Get all base items that scav has (primaryweapon/backpack/pockets etc) // Get all base items that scav has (primaryweapon/backpack/pockets etc)
// Add items that trader will buy (only sell items that have the container as parent) to request object // Add items that trader will buy (only sell items that have the container as parent) to request object
const containerAndEquipmentItems = profileWithItemsToSell.Inventory.items.filter(x => x.parentId === profileWithItemsToSell.Inventory.equipment); const containerAndEquipmentItems = profileWithItemsToSell.Inventory.items.filter((x) =>
x.parentId === profileWithItemsToSell.Inventory.equipment
);
for (const itemToSell of containerAndEquipmentItems) for (const itemToSell of containerAndEquipmentItems)
{ {
// Increment sell price in request // Increment sell price in request
sellRequest.price += this.getPriceOfItemAndChildren(itemToSell._id, profileWithItemsToSell.Inventory.items, handbookPrices, traderDetails); sellRequest.price += this.getPriceOfItemAndChildren(
itemToSell._id,
profileWithItemsToSell.Inventory.items,
handbookPrices,
traderDetails,
);
// Add item details to request // Add item details to request
// eslint-disable-next-line @typescript-eslint/naming-convention // eslint-disable-next-line @typescript-eslint/naming-convention
@ -172,7 +217,12 @@ class TradeController
* @param traderDetails Trader being sold to to perform buy category check against * @param traderDetails Trader being sold to to perform buy category check against
* @returns Rouble price * @returns Rouble price
*/ */
protected getPriceOfItemAndChildren(parentItemId: string, items: Item[], handbookPrices: Record<string, number>, traderDetails: ITraderBase): number protected getPriceOfItemAndChildren(
parentItemId: string,
items: Item[],
handbookPrices: Record<string, number>,
traderDetails: ITraderBase,
): number
{ {
const itemWithChildren = this.itemHelper.findAndReturnChildrenAsItems(items, parentItemId); const itemWithChildren = this.itemHelper.findAndReturnChildrenAsItems(items, parentItemId);
@ -180,9 +230,12 @@ 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(itemDetails[1]._id, traderDetails.items_buy.category))) if (
!(itemDetails[0] &&
this.itemHelper.isOfBaseclasses(itemDetails[1]._id, traderDetails.items_buy.category))
)
{ {
// Skip if tpl isnt item OR item doesn't fulfill match traders buy categories // Skip if tpl isn't item OR item doesn't fulfill match traders buy categories
continue; continue;
} }
@ -193,7 +246,13 @@ class TradeController
return totalPrice; return totalPrice;
} }
protected confirmTradingInternal(pmcData: IPmcData, body: IProcessBaseTradeRequestData, sessionID: string, foundInRaid = false, upd: Upd = null): IItemEventRouterResponse protected confirmTradingInternal(
pmcData: IPmcData,
body: IProcessBaseTradeRequestData,
sessionID: string,
foundInRaid = false,
upd: Upd = null,
): IItemEventRouterResponse
{ {
// buying // buying
if (body.type === "buy_from_trader") if (body.type === "buy_from_trader")
@ -212,6 +271,3 @@ class TradeController
return null; return null;
} }
} }
export { TradeController };

View File

@ -23,12 +23,13 @@ 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("JsonUtil") protected jsonUtil: JsonUtil @inject("JsonUtil") protected jsonUtil: JsonUtil,
) )
{ } {}
/** /**
* Runs when onLoad event is fired * Runs when onLoad event is fired
@ -132,7 +133,7 @@ export class TraderController
} }
} }
return traders.sort((a, b) => this.sortByTraderId(a,b)); return traders.sort((a, b) => this.sortByTraderId(a, b));
} }
/** /**

View File

@ -15,7 +15,7 @@ export class WeatherController
constructor( constructor(
@inject("WeatherGenerator") protected weatherGenerator: WeatherGenerator, @inject("WeatherGenerator") protected weatherGenerator: WeatherGenerator,
@inject("WinstonLogger") protected logger: ILogger, @inject("WinstonLogger") protected logger: ILogger,
@inject("ConfigServer") protected configServer: ConfigServer @inject("ConfigServer") protected configServer: ConfigServer,
) )
{ {
this.weatherConfig = this.configServer.getConfig(ConfigTypes.WEATHER); this.weatherConfig = this.configServer.getConfig(ConfigTypes.WEATHER);
@ -28,7 +28,7 @@ export class WeatherController
acceleration: 0, acceleration: 0,
time: "", time: "",
date: "", date: "",
weather: null weather: null,
}; };
result = this.weatherGenerator.calculateGameTime(result); result = this.weatherGenerator.calculateGameTime(result);

View File

@ -9,16 +9,16 @@ import { EventOutputHolder } from "@spt-aki/routers/EventOutputHolder";
export class WishlistController export class WishlistController
{ {
constructor( constructor(
@inject("EventOutputHolder") protected eventOutputHolder: EventOutputHolder @inject("EventOutputHolder") protected eventOutputHolder: EventOutputHolder,
) )
{ } {}
/** Handle AddToWishList */ /** Handle AddToWishList */
public addToWishList(pmcData: IPmcData, body: IWishlistActionData, sessionID: string): IItemEventRouterResponse public addToWishList(pmcData: IPmcData, body: IWishlistActionData, sessionID: string): IItemEventRouterResponse
{ {
for (const item in pmcData.WishList) for (const item in pmcData.WishList)
{ {
// don't add the item // Don't add the item
if (pmcData.WishList[item] === body.templateId) if (pmcData.WishList[item] === body.templateId)
{ {
return this.eventOutputHolder.getOutput(sessionID); return this.eventOutputHolder.getOutput(sessionID);