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.organizeImports.biome"
],
"cSpell.language": "en-GB",
"cSpell.words": [
"armor",
"asonline",
"behaviour",
"biomejs",
"botreload",
"currexp",
"currlvl",
"dbaeumer",
"deathmatch",
"dprint",
"edgeofdarkness",
"fulfill",
"gethideout",
"gifter",
"hpresource",
"inraid",
"isvalid",
"leftbehind",
"leveled",
"loadout",
"maxlvl",
"medkit",
"MEDSTATION",
"nextlvl",
"offraid",
"peacefullzryachiyevent",
"preparetoescape",
"prevexp",
"profileid",
"Protobuf",
"pscav",
"Ragfair",
"Regen",
"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("ConfigServer") protected configServer: ConfigServer,
@inject("ApplicationContext") protected applicationContext: ApplicationContext,
@inject("JsonUtil") protected jsonUtil: JsonUtil
@inject("JsonUtil") protected jsonUtil: JsonUtil,
)
{
this.botConfig = this.configServer.getConfig(ConfigTypes.BOT);
@ -51,29 +51,29 @@ export class BotController
}
/**
* Return the number of bot loadout varieties to be generated
* @param type bot Type we want the loadout gen count for
* Return the number of bot load-out varieties to be generated
* @param type bot Type we want the load-out gen count for
* @returns number of bots to generate
*/
public getBotPresetGenerationLimit(type: string): number
{
const value = this.botConfig.presetBatch[(type === "assaultGroup")
? "assault"
: type];
const value = this.botConfig.presetBatch[
(type === "assaultGroup") ?
"assault" :
type
];
if (!value)
{
this.logger.warning(`No value found for bot type ${type}, defaulting to 30`);
return value;
}
return value;
}
/**
* 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
*/
public getBotCoreDifficulty(): IBotCore
@ -90,10 +90,14 @@ export class BotController
*/
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)
{
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
@ -101,7 +105,9 @@ export class BotController
const botDifficultyDropDownValue = raidConfig.wavesSettings.botDifficulty.toLowerCase();
if (botDifficultyDropDownValue !== "asonline")
{
difficulty = this.botDifficultyHelper.convertBotDifficultyDropdownToBotDifficulty(botDifficultyDropDownValue);
difficulty = this.botDifficultyHelper.convertBotDifficultyDropdownToBotDifficulty(
botDifficultyDropDownValue,
);
}
let difficultySettings: Difficulty;
@ -109,19 +115,31 @@ export class BotController
switch (lowercasedBotType)
{
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;
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;
default:
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()))
{
this.botHelper.addBotToEnemyList(difficultySettings, [this.pmcConfig.bearType, this.pmcConfig.usecType], lowercasedBotType);
this.botHelper.addBotToEnemyList(difficultySettings, [
this.pmcConfig.bearType,
this.pmcConfig.usecType,
], lowercasedBotType);
}
break;
}
@ -149,7 +167,7 @@ export class BotController
botRelativeLevelDeltaMax: this.pmcConfig.botRelativeLevelDeltaMax,
botCountToGenerate: this.botConfig.presetBatch[condition.Role],
botDifficulty: condition.Difficulty,
isPlayerScav: false
isPlayerScav: false,
};
// 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
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
@ -171,10 +191,10 @@ export class BotController
// Loop over and make x bots for this condition
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 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
const botConvertRateMinMax = this.pmcConfig.convertIntoPmcChance[botRole.toLowerCase()];
@ -192,6 +212,7 @@ export class BotController
}
cacheKey = `${botRole}${details.botDifficulty}`;
// Check for bot in cache, add if not
if (!this.botGenerationCacheService.cacheHasBotOfRole(cacheKey))
{
@ -200,6 +221,7 @@ export class BotController
this.botGenerationCacheService.storeBots(cacheKey, botsToAddToCache);
}
}
// Get bot from cache, add to return array
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
* @param requestedDifficulty
* @returns
* Get the difficulty passed in, if its not "asonline", get selected difficulty from config
* @param requestedDifficulty
* @returns
*/
public getPMCDifficulty(requestedDifficulty: string): string
{
// maybe retrun a random difficulty...
// Maybe return a random difficulty...
if (this.pmcConfig.difficulty.toLowerCase() === "asonline")
{
return requestedDifficulty;
@ -245,20 +267,27 @@ export class BotController
public getBotCap(): number
{
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)
{
this.logger.warning(this.localisationService.getText("bot-missing_saved_match_info"));
}
const mapName = (raidConfig)
? raidConfig.location
: defaultMapCapId;
const mapName = raidConfig ?
raidConfig.location :
defaultMapCapId;
let botCap = this.botConfig.maxBotCap[mapName.toLowerCase()];
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];
}
@ -269,7 +298,7 @@ export class BotController
{
return {
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
{
constructor(
@inject("WinstonLogger") protected logger: ILogger
@inject("WinstonLogger") protected logger: ILogger,
)
{ }
{}
/**
* Handle /singleplayer/log
@ -54,4 +54,4 @@ export class ClientLogController
this.logger.info(message);
}
}
}
}

View File

@ -17,7 +17,7 @@ export class CustomizationController
{
protected readonly clothingIds = {
lowerParentId: "5cd944d01388ce000a659df9",
upperParentId: "5cd944ca1388ce03a44dc2a4"
upperParentId: "5cd944ca1388ce03a44dc2a4",
};
constructor(
@ -26,7 +26,7 @@ export class CustomizationController
@inject("DatabaseServer") protected databaseServer: DatabaseServer,
@inject("SaveServer") protected saveServer: SaveServer,
@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 suits = this.databaseServer.getTables().traders[traderID].suits;
// Get an inner join of clothing from templates.customization and ragmans suits array
const matchingSuits = suits.filter(x => x.suiteId in templates);
// Get an inner join of clothing from templates.customization and Ragman's suits array
const matchingSuits = suits.filter((x) => x.suiteId in templates);
// 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
* 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)
{
@ -85,7 +89,11 @@ export class CustomizationController
* @param sessionId Session id
* @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 output = this.eventOutputHolder.getOutput(sessionId);
@ -93,7 +101,9 @@ export class CustomizationController
const traderOffer = this.getTraderClothingOffer(sessionId, buyClothingRequest.offer);
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;
}
@ -102,7 +112,12 @@ export class CustomizationController
if (this.outfitAlreadyPurchased(suitId, sessionId))
{
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;
}
@ -118,7 +133,7 @@ export class CustomizationController
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 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)
{
@ -154,12 +174,22 @@ export class CustomizationController
* @param clothingItem Clothing item purchased
* @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)
{
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;
}
@ -179,7 +209,7 @@ export class CustomizationController
parentId: relatedItem.parentId,
slotId: relatedItem.slotId,
location: relatedItem.location,
upd: { StackObjectsCount: relatedItem.upd.StackObjectsCount }
upd: {StackObjectsCount: relatedItem.upd.StackObjectsCount},
});
}
}
@ -199,4 +229,4 @@ export class CustomizationController
return result;
}
}
}

View File

@ -37,7 +37,7 @@ export class DialogueController
@inject("MailSendService") protected mailSendService: MailSendService,
@inject("GiftService") protected giftService: GiftService,
@inject("HashUtil") protected hashUtil: HashUtil,
@inject("ConfigServer") protected configServer: ConfigServer
@inject("ConfigServer") protected configServer: ConfigServer,
)
{
this.coreConfig = this.configServer.getConfig(ConfigTypes.CORE);
@ -57,14 +57,13 @@ export class DialogueController
* Handle client/friend/list
* @returns IGetFriendListDataResponse
*/
// eslint-disable-next-line @typescript-eslint/no-unused-vars
public getFriendList(sessionID: string): IGetFriendListDataResponse
{
// Force a fake friend called SPT into friend list
return {
Friends: [this.getSptFriendData()],
Ignore: [],
InIgnoreList: []
InIgnoreList: [],
};
}
@ -104,7 +103,7 @@ export class DialogueController
new: dialogue.new,
attachmentsNew: dialogue.attachmentsNew,
pinned: dialogue.pinned,
Users: this.getDialogueUsers(dialogue, dialogue.type, sessionID)
Users: this.getDialogueUsers(dialogue, dialogue.type, sessionID),
};
return result;
@ -121,7 +120,9 @@ export class DialogueController
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
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)
{
@ -134,8 +135,8 @@ export class DialogueController
Level: profile.characters.pmc.Info.Level,
Nickname: profile.characters.pmc.Info.Nickname,
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 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
* for the specified dialogue.
* @param request Get dialog request
* @param sessionId Session id
* @returns IGetMailDialogViewResponseData object
*/
public generateDialogueView(request: IGetMailDialogViewRequestData, sessionId: string): IGetMailDialogViewResponseData
public generateDialogueView(
request: IGetMailDialogViewRequestData,
sessionId: string,
): IGetMailDialogViewResponseData
{
const dialogueId = request.dialogId;
const fullProfile = this.saveServer.getProfile(sessionId);
@ -163,17 +167,17 @@ export class DialogueController
// Set number of new attachments, but ignore those that have expired.
dialogue.attachmentsNew = this.getUnreadMessagesWithAttachmentsCount(sessionId, dialogueId);
return {
return {
messages: dialogue.messages,
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
* @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
*/
protected getDialogByIdFromProfile(profile: IAkiProfile, request: IGetMailDialogViewRequestData): Dialogue
@ -186,7 +190,7 @@ export class DialogueController
pinned: false,
messages: [],
new: 0,
type: request.type
type: request.type,
};
if (request.type === MessageType.USER_MESSAGE)
@ -211,8 +215,8 @@ export class DialogueController
{
result.push(...dialogUsers);
// Player doesnt exist, add them in before returning
if (!result.find(x => x._id === fullProfile.info.id))
// Player doesn't exist, add them in before returning
if (!result.find((x) => x._id === fullProfile.info.id))
{
const pmcProfile = fullProfile.characters.pmc;
result.push({
@ -221,8 +225,8 @@ export class DialogueController
Nickname: pmcProfile.Info.Nickname,
Side: pmcProfile.Info.Side,
Level: pmcProfile.Info.Level,
MemberCategory: pmcProfile.Info.MemberCategory
}
MemberCategory: pmcProfile.Info.MemberCategory,
},
});
}
}
@ -258,7 +262,7 @@ export class DialogueController
*/
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)
{
this.logger.error(`No dialog in profile: ${sessionId} found with id: ${dialogueId}`);
return;
}
@ -288,7 +291,6 @@ export class DialogueController
if (!dialog)
{
this.logger.error(`No dialog in profile: ${sessionId} found with id: ${dialogueId}`);
return;
}
@ -307,7 +309,6 @@ export class DialogueController
if (!dialogs)
{
this.logger.error(`No dialog object in profile: ${sessionId}`);
return;
}
@ -323,7 +324,7 @@ export class DialogueController
* Get all uncollected items attached to mail in a particular dialog
* @param dialogueId Dialog to get mail attachments from
* @param sessionId Session id
* @returns
* @returns
*/
public getAllAttachments(dialogueId: string, sessionId: string): IGetAllAttachmentsResponse
{
@ -332,25 +333,23 @@ export class DialogueController
if (!dialog)
{
this.logger.error(`No dialog in profile: ${sessionId} found with id: ${dialogueId}`);
return;
}
// Removes corner 'new messages' tag
dialog.attachmentsNew = 0;
const activeMessages = this.getActiveMessagesFromDialog(sessionId, dialogueId);
const messagesWithAttachments = this.getMessagesWithAttachments(activeMessages);
return {
return {
messages: messagesWithAttachments,
profiles: [],
hasMessagesWithRewards: this.messagesHaveUncollectedRewards(messagesWithAttachments)
hasMessagesWithRewards: this.messagesHaveUncollectedRewards(messagesWithAttachments),
};
}
/** client/mail/msg/send */
// eslint-disable-next-line @typescript-eslint/no-unused-vars
public sendMessage(sessionId: string, request: ISendMessageRequest): string
{
this.mailSendService.sendPlayerMessageToNpc(sessionId, request.dialogId, request.text);
@ -372,48 +371,97 @@ export class DialogueController
protected handleChatWithSPTFriend(sessionId: string, request: ISendMessageRequest): void
{
const sender = this.profileHelper.getPmcProfile(sessionId);
const sptFriendUser = this.getSptFriendData();
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;
}
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;
}
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,
MemberCategory: MemberCategory.DEVELOPER,
Nickname: this.coreConfig.sptFriendNickname,
Side: "Usec"
}
Side: "Usec",
},
};
}
@ -440,7 +488,7 @@ export class DialogueController
{
const timeNow = this.timeUtil.getTimestamp();
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[]
{
return messages.filter(x => x.items?.data?.length > 0);
return messages.filter((x) => x.items?.data?.length > 0);
}
/**
@ -497,4 +545,4 @@ export class DialogueController
{
return (this.timeUtil.getTimestamp()) > (message.dt + message.maxStorageTime);
}
}
}

View File

@ -69,7 +69,7 @@ export class GameController
@inject("ItemBaseClassService") protected itemBaseClassService: ItemBaseClassService,
@inject("GiftService") protected giftService: GiftService,
@inject("ApplicationContext") protected applicationContext: ApplicationContext,
@inject("ConfigServer") protected configServer: ConfigServer
@inject("ConfigServer") protected configServer: ConfigServer,
)
{
this.httpConfig = this.configServer.getConfig(ConfigTypes.HTTP);
@ -82,10 +82,10 @@ export class GameController
public load(): void
{
// Regenerate basecache 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
// 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
this.itemBaseClassService.hydrateItemBaseClassCache();
this.addCustomLooseLootPositions();
}
@ -121,9 +121,9 @@ export class GameController
this.checkTraderRepairValuesExist();
// repeatableQuests are stored by in profile.Quests due to the responses of the client (e.g. Quests in offraidData)
// Since we don't want to clutter the Quests list, we need to remove all completed (failed / successful) repeatable quests.
// We also have to remove the Counters from the repeatableQuests
// repeatableQuests are stored by in profile.Quests due to the responses of the client (e.g. Quests in
// offraidData). Since we don't want to clutter the Quests list, we need to remove all completed (failed or
// successful) repeatable quests. We also have to remove the Counters from the repeatableQuests
if (sessionID)
{
const fullProfile = this.profileHelper.getFullProfile(sessionID);
@ -166,7 +166,7 @@ export class GameController
this.profileFixerService.fixIncorrectAidValue(fullProfile);
this.profileFixerService.migrateStatsToNewStructure(fullProfile);
this.sendPraporGiftsToNewProfiles(pmcProfile);
this.profileFixerService.checkForOrphanedModdedItems(sessionID, fullProfile);
@ -184,7 +184,7 @@ export class GameController
this.hideoutHelper.unlockHideoutWallInProfile(pmcProfile);
this.profileFixerService.addMissingIdsToBonuses(pmcProfile);
}
this.logProfileDetails(fullProfile);
this.adjustLabsRaiderSpawnRate();
@ -240,7 +240,9 @@ export class GameController
const trader = this.databaseServer.getTables().traders[traderKey];
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);
return;
@ -248,8 +250,12 @@ export class GameController
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`);
trader.base.repair.quality = this.jsonUtil.clone(this.databaseServer.getTables().traders.ragfair.base.repair.quality);
this.logger.warning(
`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)
{
// 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)
{
existingLootPosition.template.Items.push(...positionToAdd.template.Items);
@ -283,7 +291,7 @@ export class GameController
continue;
}
// new postion, add entire object
// New position, add entire object
mapLooseLoot.spawnpoints.push(positionToAdd);
}
}
@ -303,7 +311,7 @@ export class GameController
const mapLootAdjustmentsDict = adjustments[mapId];
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)
{
this.logger.warning(`Unable to adjust loot position: ${lootKey} on map: ${mapId}`);
@ -314,24 +322,24 @@ export class GameController
}
}
}
protected setHideoutAreasAndCraftsTo40Secs(): void
{
for (const hideoutProd of this.databaseServer.getTables().hideout.production)
for (const hideoutProd of this.databaseServer.getTables().hideout.production)
{
if (hideoutProd.productionTime > 40)
if (hideoutProd.productionTime > 40)
{
hideoutProd.productionTime = 40;
}
}
this.logger.warning("DEVELOPER: SETTING ALL HIDEOUT PRODUCTIONS TO 40 SECONDS");
for (const hideoutArea of this.databaseServer.getTables().hideout.areas)
for (const hideoutArea of this.databaseServer.getTables().hideout.areas)
{
for (const stageKey in hideoutArea.stages)
for (const stageKey in hideoutArea.stages)
{
const stage = hideoutArea.stages[stageKey];
if (stage.constructionTime > 40)
if (stage.constructionTime > 40)
{
stage.constructionTime = 40;
}
@ -339,9 +347,9 @@ export class GameController
}
this.logger.warning("DEVELOPER: SETTING ALL HIDEOUT AREAS TO 40 SECOND UPGRADES");
for (const scavCaseCraft of this.databaseServer.getTables().hideout.scavcase)
for (const scavCaseCraft of this.databaseServer.getTables().hideout.scavcase)
{
if (scavCaseCraft.ProductionTime > 40)
if (scavCaseCraft.ProductionTime > 40)
{
scavCaseCraft.ProductionTime = 40;
}
@ -363,12 +371,14 @@ export class GameController
const map: ILocationData = mapsDb[mapId];
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])
{
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)
{
// Existing bot type found in MinMaxBots array, edit
@ -378,14 +388,12 @@ export class GameController
}
else
{
map.base.MinMaxBots.push(
{
// Bot type not found, add new object
WildSpawnType: botToLimit.type,
min: botToLimit.min,
max: botToLimit.max
}
);
// Bot type not found, add new object
map.base.MinMaxBots.push({
WildSpawnType: botToLimit.type,
min: botToLimit.min,
max: botToLimit.max,
});
}
}
}
@ -412,12 +420,11 @@ export class GameController
Trading: this.httpServerHelper.getBackendUrl(),
Messaging: this.httpServerHelper.getBackendUrl(),
Main: this.httpServerHelper.getBackendUrl(),
RagFair: this.httpServerHelper.getBackendUrl()
RagFair: this.httpServerHelper.getBackendUrl(),
},
useProtobuf: false,
// eslint-disable-next-line @typescript-eslint/naming-convention
utc_time: new Date().getTime() / 1000,
totalInGame: profile.Stats?.Eft?.TotalInGameTime ?? 0
totalInGame: profile.Stats?.Eft?.TotalInGameTime ?? 0,
};
return config;
@ -426,50 +433,43 @@ export class GameController
/**
* Handle client/server/list
*/
// eslint-disable-next-line @typescript-eslint/no-unused-vars
public getServer(sessionId: string): IServerDetails[]
{
return [
{
ip: this.httpConfig.ip,
port: this.httpConfig.port
}
];
return [{
ip: this.httpConfig.ip,
port: this.httpConfig.port,
}];
}
/**
* Handle client/match/group/current
*/
// eslint-disable-next-line @typescript-eslint/no-unused-vars
public getCurrentGroup(sessionId: string): ICurrentGroupResponse
{
return {
squad: []
squad: [],
};
}
/**
* Handle client/checkVersion
*/
// eslint-disable-next-line @typescript-eslint/no-unused-vars
public getValidGameVersion(sessionId: string): ICheckVersionResponse
{
return {
isvalid: true,
latestVersion: this.coreConfig.compatibleTarkovVersion
latestVersion: this.coreConfig.compatibleTarkovVersion,
};
}
/**
* Handle client/game/keepalive
*/
// eslint-disable-next-line @typescript-eslint/no-unused-vars
public getKeepAlive(sessionId: string): IGameKeepAliveResponse
{
return {
msg: "OK",
// eslint-disable-next-line @typescript-eslint/naming-convention
utc_time: new Date().getTime() / 1000
utc_time: new Date().getTime() / 1000,
};
}
@ -520,8 +520,8 @@ export class GameController
/**
* When player logs in, iterate over all active effects and reduce timer
* TODO - add body part HP regen
* @param pmcProfile
* // TODO: Add body part HP regeneration
* @param pmcProfile
*/
protected updateProfileHealthValues(pmcProfile: IPmcData): void
{
@ -538,14 +538,23 @@ export class GameController
let hpRegenPerHour = 456.6;
// Set new values, whatever is smallest
energyRegenPerHour += pmcProfile.Bonuses.filter(x => x.type === "EnergyRegeneration").reduce((sum, curr) => sum + curr.value, 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);
energyRegenPerHour += pmcProfile.Bonuses.filter((x) => x.type === "EnergyRegeneration").reduce(
(sum, curr) => sum + curr.value,
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)
{
// 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)
{
pmcProfile.Health.Energy.Current = pmcProfile.Health.Energy.Maximum;
@ -554,7 +563,7 @@ export class GameController
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)
{
pmcProfile.Health.Hydration.Current = pmcProfile.Health.Hydration.Maximum;
@ -565,17 +574,17 @@ export class GameController
for (const bodyPartKey in pmcProfile.Health.BodyParts)
{
const bodyPart = pmcProfile.Health.BodyParts[bodyPartKey] as BodyPartHealth;
// Check part hp
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)
{
bodyPart.Health.Current = bodyPart.Health.Maximum;
}
// Look for effects
if (Object.keys(bodyPart.Effects ?? {}).length > 0)
{
@ -618,7 +627,9 @@ export class GameController
const location: ILocationData = this.databaseServer.getTables().locations[locationKey];
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;
}
@ -626,7 +637,9 @@ export class GameController
{
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++;
}
}
@ -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
{
@ -689,18 +703,23 @@ export class GameController
for (const wave of location.base.waves)
{
// 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
const waveSize = wave.slots_max - wave.slots_min;
// Update wave to spawn single bot
wave.slots_min = 1;
wave.slots_max = 2;
// Get index of 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
let wavesAddedCount = 0;
@ -716,20 +735,23 @@ export class GameController
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);
wavesAddedCount++;
}
// Update subsequent wave number property to accomodate the new waves
for (let index = indexOfWaveToSplit + wavesAddedCount + 1; index < location.base.waves.length; index++)
// Update subsequent wave number property to accommodate the new waves
for (
let index = indexOfWaveToSplit + wavesAddedCount + 1;
index < location.base.waves.length;
index++
)
{
// Some waves have value of 0, leave them as-is
if (location.base.waves[index].number !== 0)
{
location.base.waves[index].number += wavesAddedCount;
}
}
}
}
@ -753,9 +775,13 @@ export class GameController
for (const modKey in activeMods)
{
const modDetails = activeMods[modKey];
if (fullProfile.aki.mods.some(x => x.author === modDetails.author
&& x.name === modDetails.name
&& x.version === modDetails.version))
if (
fullProfile.aki.mods.some((x) =>
x.author === modDetails.author &&
x.name === modDetails.name &&
x.version === modDetails.version
)
)
{
// Exists already, skip
continue;
@ -765,13 +791,13 @@ export class GameController
author: modDetails.author,
dateAdded: Date.now(),
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
{
@ -788,19 +814,26 @@ export class GameController
}
// 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))
{
// Does assort key exist in trader assort file
if (!traderAssorts.loyal_level_items[assortKey])
{
// reverse lookup of enum key by value
// Reverse lookup of enum key by value
const messageValues = {
traderName: Object.keys(Traders)[Object.values(Traders).indexOf(traderId)],
questName: quests[questKey]?.QuestName ?? "UNKNOWN"};
this.logger.debug(this.localisationService.getText("assort-missing_quest_assort_unlock", messageValues));
questName: quests[questKey]?.QuestName ?? "UNKNOWN",
};
this.logger.debug(
this.localisationService.getText("assort-missing_quest_assort_unlock", messageValues),
);
}
}
}
@ -821,11 +854,11 @@ export class GameController
{
bots["bear"].firstName.push(playerName);
}
if (bots["usec"])
{
bots["usec"].firstName.push(playerName);
}
}
}
}
@ -847,7 +880,7 @@ export class GameController
*/
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)
{
this.databaseServer.getTables().locales.global[localeKey]["61687e2c3e526901fa76baf9"] = "";
@ -860,7 +893,9 @@ export class GameController
protected adjustLabsRaiderSpawnRate(): void
{
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)
{
for (const boss of nonTriggerLabsBossSpawns)
@ -878,4 +913,4 @@ export class GameController
this.logger.debug(`Debug enabled: ${globalThis.G_DEBUG_CONFIGURATION}`);
this.logger.debug(`Mods enabled: ${globalThis.G_MODS_ENABLED}`);
}
}
}

View File

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

View File

@ -31,7 +31,7 @@ export class HealthController
@inject("InventoryHelper") protected inventoryHelper: InventoryHelper,
@inject("LocalisationService") protected localisationService: LocalisationService,
@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 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);
}
@ -60,10 +66,13 @@ export class HealthController
const output = this.eventOutputHolder.getOutput(sessionID);
// 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)
{
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);
return this.httpResponse.appendErrorToOutput(output, errorMessage);
@ -82,8 +91,8 @@ export class HealthController
else
{
// Get max healing from db
const maxhp = this.itemHelper.getItem(healingItemToUse._tpl)[1]._props.MaxHpResource;
healingItemToUse.upd.MedKit = { HpResource: maxhp - request.count }; // Subtract amout used from max
const maxHp = this.itemHelper.getItem(healingItemToUse._tpl)[1]._props.MaxHpResource;
healingItemToUse.upd.MedKit = {HpResource: maxHp - request.count}; // Subtract amount used from max
}
// Resource in medkit is spent, delete it
@ -108,11 +117,14 @@ export class HealthController
let output = this.eventOutputHolder.getOutput(sessionID);
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)
{
// 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;
@ -120,7 +132,7 @@ export class HealthController
{
if (itemToConsume.upd.FoodDrink === undefined)
{
itemToConsume.upd.FoodDrink = { HpPercent: consumedItemMaxResource - request.count };
itemToConsume.upd.FoodDrink = {HpPercent: consumedItemMaxResource - request.count};
}
else
{
@ -138,7 +150,7 @@ export class HealthController
return output;
}
/**
* Handle RestoreHealth event
* Occurs on post-raid healing page
@ -147,20 +159,21 @@ export class HealthController
* @param sessionID Session id
* @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);
const payMoneyRequest: IProcessBuyTradeRequestData = {
Action: healthTreatmentRequest.Action,
tid: Traders.THERAPIST,
// eslint-disable-next-line @typescript-eslint/naming-convention
scheme_items: healthTreatmentRequest.items,
type: "",
// eslint-disable-next-line @typescript-eslint/naming-convention
item_id: "",
count: 0,
// eslint-disable-next-line @typescript-eslint/naming-convention
scheme_id: 0
scheme_id: 0,
};
output = this.paymentService.payMoney(pmcData, payMoneyRequest, sessionID, output);
@ -175,7 +188,7 @@ export class HealthController
const partRequest: BodyPart = healthTreatmentRequest.difference.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;
// Check for effects to remove
@ -205,9 +218,8 @@ export class HealthController
* applies skills from hideout workout.
* @param pmcData Player profile
* @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
{
// https://dev.sp-tarkov.com/SPT-AKI/Server/issues/2674
@ -215,4 +227,4 @@ export class HealthController
// Health effects (fractures etc) are handled in /player/health/sync.
pmcData.Skills.Common = info.skills.Common;
}
}
}

View File

@ -69,7 +69,7 @@ export class HideoutController
@inject("LocalisationService") protected localisationService: LocalisationService,
@inject("ConfigServer") protected configServer: ConfigServer,
@inject("JsonUtil") protected jsonUtil: JsonUtil,
@inject("FenceService") protected fenceService: FenceService
@inject("FenceService") protected fenceService: FenceService,
)
{
this.hideoutConfig = this.configServer.getConfig(ConfigTypes.HIDEOUT);
@ -83,15 +83,19 @@ export class HideoutController
* @param sessionID Session id
* @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 items = request.items.map(reqItem =>
const items = request.items.map((reqItem) =>
{
const item = pmcData.Inventory.items.find(invItem => invItem._id === reqItem.id);
const item = pmcData.Inventory.items.find((invItem) => invItem._id === reqItem.id);
return {
inventoryItem: item,
requestedItem: reqItem
requestedItem: reqItem,
};
});
@ -100,14 +104,18 @@ export class HideoutController
{
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);
}
if (this.paymentHelper.isMoneyTpl(item.inventoryItem._tpl)
&& item.inventoryItem.upd
&& item.inventoryItem.upd.StackObjectsCount
&& item.inventoryItem.upd.StackObjectsCount > item.requestedItem.count)
if (
this.paymentHelper.isMoneyTpl(item.inventoryItem._tpl) &&
item.inventoryItem.upd &&
item.inventoryItem.upd.StackObjectsCount &&
item.inventoryItem.upd.StackObjectsCount > item.requestedItem.count
)
{
item.inventoryItem.upd.StackObjectsCount -= item.requestedItem.count;
}
@ -118,17 +126,21 @@ export class HideoutController
}
// 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)
{
this.logger.error(this.localisationService.getText("hideout-unable_to_find_area", request.areaType));
return this.httpResponse.appendErrorToOutput(output);
}
const hideoutData = this.databaseServer.getTables().hideout.areas.find(area => area.type === request.areaType);
const hideoutData = this.databaseServer.getTables().hideout.areas.find((area) =>
area.type === request.areaType
);
if (!hideoutData)
{
this.logger.error(this.localisationService.getText("hideout-unable_to_find_area_in_database", request.areaType));
this.logger.error(
this.localisationService.getText("hideout-unable_to_find_area_in_database", request.areaType),
);
return this.httpResponse.appendErrorToOutput(output);
}
@ -152,12 +164,16 @@ export class HideoutController
* @param sessionID Session id
* @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 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)
{
this.logger.error(this.localisationService.getText("hideout-unable_to_find_area", request.areaType));
@ -169,10 +185,12 @@ export class HideoutController
profileHideoutArea.completeTime = 0;
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)
{
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);
}
@ -190,18 +208,32 @@ export class HideoutController
// Upgrade includes a container improvement/addition
if (hideoutStage?.container)
{
this.addContainerImprovementToProfile(output, sessionID, pmcData, profileHideoutArea, hideoutData, hideoutStage);
this.addContainerImprovementToProfile(
output,
sessionID,
pmcData,
profileHideoutArea,
hideoutData,
hideoutStage,
);
}
// 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);
}
// 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;
}
@ -211,11 +243,11 @@ export class HideoutController
*/
protected checkAndUpgradeWall(pmcData: IPmcData): void
{
const medStation = pmcData.Hideout.Areas.find(area => area.type === HideoutAreas.MEDSTATION);
const waterCollector = pmcData.Hideout.Areas.find(area => area.type === HideoutAreas.WATER_COLLECTOR);
const medStation = pmcData.Hideout.Areas.find((area) => area.type === HideoutAreas.MEDSTATION);
const waterCollector = pmcData.Hideout.Areas.find((area) => area.type === HideoutAreas.WATER_COLLECTOR);
if (medStation?.level >= 1 && waterCollector?.level >= 1)
{
const wall = pmcData.Hideout.Areas.find(area => area.type === HideoutAreas.EMERGENCY_WALL);
const wall = pmcData.Hideout.Areas.find((area) => area.type === HideoutAreas.EMERGENCY_WALL);
if (wall?.level === 0)
{
wall.level = 3;
@ -224,7 +256,6 @@ export class HideoutController
}
/**
*
* @param pmcData Profile to edit
* @param output Object to send back to client
* @param sessionID Session/player id
@ -232,7 +263,14 @@ export class HideoutController
* @param dbHideoutArea Hideout area being upgraded
* @param hideoutStage Stage hideout area is being upgraded to
*/
protected addContainerImprovementToProfile(output: IItemEventRouterResponse, sessionID: string, pmcData: IPmcData, profileParentHideoutArea: HideoutArea, dbHideoutArea: IHideoutArea, hideoutStage: Stage): void
protected addContainerImprovementToProfile(
output: IItemEventRouterResponse,
sessionID: string,
pmcData: IPmcData,
profileParentHideoutArea: HideoutArea,
dbHideoutArea: IHideoutArea,
hideoutStage: Stage,
): void
{
// Add key/value to `hideoutAreaStashes` dictionary - used to link hideout area to inventory stash by its id
if (!pmcData.Inventory.hideoutAreaStashes[dbHideoutArea.type])
@ -247,7 +285,9 @@ export class HideoutController
this.addContainerUpgradeToClientOutput(output, sessionID, dbHideoutArea.type, dbHideoutArea, hideoutStage);
// Some areas like gun stand have a child area linked to it, it needs to do the same as above
const childDbArea = this.databaseServer.getTables().hideout.areas.find(x => x.parentArea === dbHideoutArea._id);
const childDbArea = this.databaseServer.getTables().hideout.areas.find((x) =>
x.parentArea === dbHideoutArea._id
);
if (childDbArea)
{
// Add key/value to `hideoutAreaStashes` dictionary - used to link hideout area to inventory stash by its id
@ -257,7 +297,9 @@ export class HideoutController
}
// 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
const childDbAreaStage = childDbArea.stages[profileParentHideoutArea.level];
@ -276,38 +318,41 @@ export class HideoutController
*/
protected addUpdateInventoryItemToProfile(pmcData: IPmcData, dbHideoutData: IHideoutArea, hideoutStage: Stage): void
{
const existingInventoryItem = pmcData.Inventory.items.find(x => x._id === dbHideoutData._id);
if (existingInventoryItem)
const existingInventoryItem = pmcData.Inventory.items.find((x) => x._id === dbHideoutData._id);
if (existingInventoryItem)
{
// Update existing items container tpl to point to new id (tpl)
existingInventoryItem._tpl = hideoutStage.container;
return;
}
// Add new item as none exists
pmcData.Inventory.items.push({ _id: dbHideoutData._id, _tpl: hideoutStage.container });
pmcData.Inventory.items.push({_id: dbHideoutData._id, _tpl: hideoutStage.container});
}
/**
*
* @param output Objet to send to client
* @param sessionID Session/player id
* @param areaType Hideout area that had stash added
* @param hideoutDbData Hideout area that caused addition of stash
* @param hideoutStage Hideout area upgraded to this
*/
protected addContainerUpgradeToClientOutput(output: IItemEventRouterResponse, sessionID: string, areaType: HideoutAreas, hideoutDbData: IHideoutArea, hideoutStage: Stage): void
protected addContainerUpgradeToClientOutput(
output: IItemEventRouterResponse,
sessionID: string,
areaType: HideoutAreas,
hideoutDbData: IHideoutArea,
hideoutStage: Stage,
): void
{
if (!output.profileChanges[sessionID].changedHideoutStashes)
{
output.profileChanges[sessionID].changedHideoutStashes = {};
}
output.profileChanges[sessionID].changedHideoutStashes[areaType] =
{
output.profileChanges[sessionID].changedHideoutStashes[areaType] = {
Id: hideoutDbData._id,
Tpl: hideoutStage.container
Tpl: hideoutStage.container,
};
}
@ -315,28 +360,37 @@ export class HideoutController
* Handle HideoutPutItemsInAreaSlots
* Create item in hideout slot item array, remove item from player inventory
* @param pmcData Profile data
* @param addItemToHideoutRequest reqeust from client to place item in area slot
* @param addItemToHideoutRequest request from client to place item in area slot
* @param sessionID Session id
* @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);
const itemsToAdd = Object.entries(addItemToHideoutRequest.items).map(kvp =>
const itemsToAdd = Object.entries(addItemToHideoutRequest.items).map((kvp) =>
{
const item = pmcData.Inventory.items.find(invItem => invItem._id === kvp[1]["id"]);
const item = pmcData.Inventory.items.find((invItem) => invItem._id === kvp[1]["id"]);
return {
inventoryItem: item,
requestedItem: kvp[1],
slot: kvp[0]
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)
{
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);
}
@ -344,21 +398,26 @@ export class HideoutController
{
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);
}
// Add item to area.slots
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 = [{
_id: item.inventoryItem._id,
_tpl: item.inventoryItem._tpl,
upd: item.inventoryItem.upd
upd: item.inventoryItem.upd,
}];
output = this.inventoryHelper.removeItem(pmcData, item.inventoryItem._id, sessionID, output);
}
}
// Trigger a forced update
this.hideoutHelper.updatePlayerHideout(sessionID);
@ -374,11 +433,15 @@ export class HideoutController
* @param sessionID Session id
* @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 hideoutArea = pmcData.Hideout.Areas.find(area => area.type === request.areaType);
const hideoutArea = pmcData.Hideout.Areas.find((area) => area.type === request.areaType);
if (!hideoutArea)
{
this.logger.error(this.localisationService.getText("hideout-unable_to_find_area", request.areaType));
@ -387,19 +450,30 @@ export class HideoutController
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);
}
// 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);
this.update();
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
* @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 itemToReturn = hideoutArea.slots.find(x => x.locationIndex === slotIndexToRemove).item[0];
const itemToReturn = hideoutArea.slots.find((x) => x.locationIndex === slotIndexToRemove).item[0];
const newReq = {
items: [{
// eslint-disable-next-line @typescript-eslint/naming-convention
item_id: itemToReturn._tpl,
count: 1
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 (output.warnings && output.warnings.length > 0)
@ -435,7 +523,7 @@ export class HideoutController
}
// 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;
return output;
@ -449,14 +537,18 @@ export class HideoutController
* @param sessionID Session id
* @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);
// Force a production update (occur before area is toggled as it could be generator and doing it after generator enabled would cause incorrect calculaton of production progress)
this.hideoutHelper.updatePlayerHideout(sessionID);
const hideoutArea = pmcData.Hideout.Areas.find(area => area.type === request.areaType);
const hideoutArea = pmcData.Hideout.Areas.find((area) => area.type === request.areaType);
if (!hideoutArea)
{
this.logger.error(this.localisationService.getText("hideout-unable_to_find_area", request.areaType));
@ -472,27 +564,31 @@ export class HideoutController
* Handle HideoutSingleProductionStart event
* Start production for an item from hideout area
* @param pmcData Player profile
* @param body Start prodution of single item request
* @param body Start production of single item request
* @param sessionID Session id
* @returns IItemEventRouterResponse
*/
public singleProductionStart(pmcData: IPmcData, body: IHideoutSingleProductionStartRequestData, sessionID: string): IItemEventRouterResponse
public singleProductionStart(
pmcData: IPmcData,
body: IHideoutSingleProductionStartRequestData,
sessionID: string,
): IItemEventRouterResponse
{
// Start production
this.registerProduction(pmcData, body, sessionID);
// Find the recipe of the production
const recipe = this.databaseServer.getTables().hideout.production.find(p => p._id === body.recipeId);
const recipe = this.databaseServer.getTables().hideout.production.find((p) => p._id === body.recipeId);
// Find the actual amount of items we need to remove because body can send weird data
const requirements = this.jsonUtil.clone(recipe.requirements.filter(i => i.type === "Item"));
const requirements = this.jsonUtil.clone(recipe.requirements.filter((i) => i.type === "Item"));
const output = this.eventOutputHolder.getOutput(sessionID);
for (const itemToDelete of body.items)
{
const itemToCheck = pmcData.Inventory.items.find(i => i._id === itemToDelete.id);
const requirement = requirements.find(requirement => requirement.templateId === itemToCheck._tpl);
const itemToCheck = pmcData.Inventory.items.find((i) => i._id === itemToDelete.id);
const requirement = requirements.find((requirement) => requirement.templateId === itemToCheck._tpl);
if (requirement.count <= 0)
{
continue;
@ -513,21 +609,32 @@ export class HideoutController
* @param sessionID session id
* @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);
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)
{
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);
}
if (inventoryItem.upd?.StackObjectsCount
&& inventoryItem.upd.StackObjectsCount > requestedItem.count)
if (
inventoryItem.upd?.StackObjectsCount &&
inventoryItem.upd.StackObjectsCount > requestedItem.count
)
{
inventoryItem.upd.StackObjectsCount -= requestedItem.count;
}
@ -537,19 +644,25 @@ 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)
{
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);
}
// @Important: Here we need to be very exact:
// - normal recipe: Production time value is stored in attribute "productionType" with small "p"
// - scav case recipe: Production time value is stored in attribute "ProductionType" with capital "P"
const modifiedScavCaseTime = this.getScavCaseTime(pmcData, recipe.ProductionTime);
pmcData.Hideout.Production[body.recipeId] = this.hideoutHelper.initProduction(body.recipeId, modifiedScavCaseTime, false);
pmcData.Hideout.Production[body.recipeId] = this.hideoutHelper.initProduction(
body.recipeId,
modifiedScavCaseTime,
false,
);
pmcData.Hideout.Production[body.recipeId].sptIsScavCase = true;
return output;
@ -557,7 +670,7 @@ export class HideoutController
/**
* Adjust scav case time based on fence standing
*
*
* @param pmcData Player profile
* @param productionTime Time to complete scav case in seconds
* @returns Adjusted scav case time in seconds
@ -569,7 +682,6 @@ export class HideoutController
{
return productionTime;
}
return productionTime * fenceLevel.ScavCaseTimeModifier;
}
@ -582,21 +694,24 @@ export class HideoutController
protected addScavCaseRewardsToProfile(pmcData: IPmcData, rewards: Product[], recipeId: string): void
{
pmcData.Hideout.Production[`ScavCase${recipeId}`] = {
Products: rewards
Products: rewards,
};
}
/**
* Start production of continuously created item
* @param pmcData Player profile
* @param request Continious production request
* @param request Continuous production request
* @param sessionID Session id
* @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);
return this.eventOutputHolder.getOutput(sessionID);
}
@ -608,7 +723,11 @@ export class HideoutController
* @param sessionID Session id
* @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);
@ -617,19 +736,24 @@ export class HideoutController
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)
{
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)
{
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);
}
@ -643,9 +767,15 @@ export class HideoutController
* @param output Output object to update
* @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;
// ? move the logic of BackendCounters in new method?
@ -654,12 +784,11 @@ export class HideoutController
{
pmcData.BackendCounters[HideoutController.nameBackendCountersCrafting] = {
id: HideoutController.nameBackendCountersCrafting,
value: 0
value: 0,
};
counterHoursCrafting = pmcData.BackendCounters[HideoutController.nameBackendCountersCrafting];
}
let hoursCrafting = counterHoursCrafting.value;
// create item and throw it into profile
let id = recipe.endProduct;
@ -672,18 +801,18 @@ export class HideoutController
const newReq = {
items: [{
// eslint-disable-next-line @typescript-eslint/naming-convention
item_id: id,
count: recipe.count
count: recipe.count,
}],
tid: "ragfair"
tid: "ragfair",
};
const entries = Object.entries(pmcData.Hideout.Production);
let prodId: string;
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)
{
@ -695,7 +824,12 @@ export class HideoutController
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);
}
@ -712,9 +846,9 @@ export class HideoutController
hoursCrafting += recipe.productionTime;
if ((hoursCrafting / this.hideoutConfig.hoursForSkillCrafting) >= 1)
{
const multiplierCrafting = Math.floor((hoursCrafting / this.hideoutConfig.hoursForSkillCrafting));
craftingExpAmount += (1 * multiplierCrafting);
hoursCrafting -= (this.hideoutConfig.hoursForSkillCrafting * multiplierCrafting);
const multiplierCrafting = Math.floor(hoursCrafting / this.hideoutConfig.hoursForSkillCrafting);
craftingExpAmount += 1 * multiplierCrafting;
hoursCrafting -= this.hideoutConfig.hoursForSkillCrafting * multiplierCrafting;
}
// increment
@ -726,12 +860,21 @@ export class HideoutController
// manager Hideout skill
// ? use a configuration variable for the value?
const globals = this.databaseServer.getTables().globals;
this.profileHelper.addSkillPointsToPlayer(pmcData, SkillTypes.HIDEOUT_MANAGEMENT, globals.config.SkillsSettings.HideoutManagement.SkillPointsPerCraft, true);
//manager Crafting skill
this.profileHelper.addSkillPointsToPlayer(
pmcData,
SkillTypes.HIDEOUT_MANAGEMENT,
globals.config.SkillsSettings.HideoutManagement.SkillPointsPerCraft,
true,
);
// manager Crafting skill
if (craftingExpAmount > 0)
{
this.profileHelper.addSkillPointsToPlayer(pmcData, SkillTypes.CRAFTING, craftingExpAmount);
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;
counterHoursCrafting.value = hoursCrafting;
@ -747,7 +890,7 @@ export class HideoutController
if (recipe.isEncoded)
{
const upd: Upd = {
RecodableComponent: { IsEncoded: true}
RecodableComponent: {IsEncoded: true},
};
return this.inventoryHelper.addItem(pmcData, newReq, output, sessionID, callback, true, upd);
@ -764,13 +907,19 @@ export class HideoutController
* @param output Output object to update
* @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);
let prodId: string;
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)
{
@ -782,7 +931,12 @@ export class HideoutController
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);
}
@ -795,28 +949,29 @@ export class HideoutController
// Remove the old production from output object before its sent to client
delete output.profileChanges[sessionID].production[request.recipeId];
const itemsToAdd = pmcData.Hideout.Production[prodId].Products.map((x: { _tpl: string; upd?: { StackObjectsCount?: number; }; }) =>
{
let id = x._tpl;
if (this.presetHelper.hasPreset(id))
const itemsToAdd = pmcData.Hideout.Production[prodId].Products.map(
(x: {_tpl: string; upd?: {StackObjectsCount?: number;};}) =>
{
id = this.presetHelper.getDefaultPreset(id)._id;
}
const numOfItems = !x.upd?.StackObjectsCount
? 1
: x.upd.StackObjectsCount;
// eslint-disable-next-line @typescript-eslint/naming-convention
return { item_id: id, count: numOfItems };
});
let id = x._tpl;
if (this.presetHelper.hasPreset(id))
{
id = this.presetHelper.getDefaultPreset(id)._id;
}
const numOfItems = !x.upd?.StackObjectsCount ?
1 :
x.upd.StackObjectsCount;
return {item_id: id, count: numOfItems};
},
);
const newReq = {
items: itemsToAdd,
tid: "ragfair"
tid: "ragfair",
};
const callback = () =>
{
// Null production data now it's complete - will be cleaned up later by update() process
pmcData.Hideout.Production[prodId] = null;
};
@ -831,18 +986,21 @@ export class HideoutController
* @param sessionID Session id
* @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);
}
/**
* Get quick time event list for hideout
* // TODO - implement this
* // TODO: Implement this
* @param sessionId Session id
* @returns IQteData array
*/
// eslint-disable-next-line @typescript-eslint/no-unused-vars
*/
public getQteList(sessionId: string): IQteData[]
{
return this.databaseServer.getTables().hideout.qte;
@ -855,8 +1013,11 @@ export class HideoutController
* @param pmcData Profile to adjust
* @param request QTE result object
*/
// eslint-disable-next-line @typescript-eslint/no-unused-vars
public handleQTEEventOutcome(sessionId: string, pmcData: IPmcData, request: IHandleQTEEventRequestData): IItemEventRouterResponse
public handleQTEEventOutcome(
sessionId: string,
pmcData: IPmcData,
request: IHandleQTEEventRequestData,
): IItemEventRouterResponse
{
// {
// Action: "HideoutQuickTimeEvent",
@ -865,7 +1026,7 @@ export class HideoutController
// timestamp: 1672585349
// }
// Skill changes are done in
// Skill changes are done in
// /client/hideout/workout (applyWorkoutChanges).
pmcData.Health.Energy.Current -= 50;
@ -890,22 +1051,28 @@ export class HideoutController
* @param request shooting range score request
* @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
if (!pmcData.Stats.Eft.OverallCounters.Items.find(x => x.Key.includes("ShootingRangePoints")))
// Check if counter exists, add placeholder if it doesn't
if (!pmcData.Stats.Eft.OverallCounters.Items.find((x) => x.Key.includes("ShootingRangePoints")))
{
pmcData.Stats.Eft.OverallCounters.Items.push({
Key: ["ShootingRangePoints"],
Value: 0
Value: 0,
});
}
// 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;
// Check against live, maybe a response isnt necessary
// Check against live, maybe a response isn't necessary
return this.eventOutputHolder.getOutput(sessionId);
}
@ -915,17 +1082,21 @@ export class HideoutController
* @param pmcData Profile to improve area in
* @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);
// Create mapping of required item with corrisponding item from player inventory
const items = request.items.map(reqItem =>
// Create mapping of required item with corresponding item from player inventory
const items = request.items.map((reqItem) =>
{
const item = pmcData.Inventory.items.find(invItem => invItem._id === reqItem.id);
const item = pmcData.Inventory.items.find((invItem) => invItem._id === reqItem.id);
return {
inventoryItem: item,
requestedItem: reqItem
requestedItem: reqItem,
};
});
@ -934,14 +1105,18 @@ export class HideoutController
{
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);
}
if (this.paymentHelper.isMoneyTpl(item.inventoryItem._tpl)
&& item.inventoryItem.upd
&& item.inventoryItem.upd.StackObjectsCount
&& item.inventoryItem.upd.StackObjectsCount > item.requestedItem.count)
if (
this.paymentHelper.isMoneyTpl(item.inventoryItem._tpl) &&
item.inventoryItem.upd &&
item.inventoryItem.upd.StackObjectsCount &&
item.inventoryItem.upd.StackObjectsCount > item.requestedItem.count
)
{
item.inventoryItem.upd.StackObjectsCount -= item.requestedItem.count;
}
@ -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)
{
this.logger.error(this.localisationService.getText("hideout-unable_to_find_area", request.areaType));
return this.httpResponse.appendErrorToOutput(output);
}
const hideoutDbData = this.databaseServer.getTables().hideout.areas.find(x => x.type === request.areaType);
const hideoutDbData = this.databaseServer.getTables().hideout.areas.find((x) => x.type === request.areaType);
if (!hideoutDbData)
{
this.logger.error(this.localisationService.getText("hideout-unable_to_find_area_in_database", request.areaType));
this.logger.error(
this.localisationService.getText("hideout-unable_to_find_area_in_database", request.areaType),
);
return this.httpResponse.appendErrorToOutput(output);
}
// Add all improvemets to output object
// Add all improvements to output object
const improvements = hideoutDbData.stages[profileHideoutArea.level].improvements;
const timestamp = this.timeUtil.getTimestamp();
for (const improvement of improvements)
@ -975,10 +1152,13 @@ export class HideoutController
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;
pmcData.Hideout.Improvement[improvement.id] = improvementDetails;
}
}
return output;
}
@ -990,7 +1170,11 @@ export class HideoutController
* @param request Cancel production request data
* @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);
@ -1006,7 +1190,7 @@ export class HideoutController
// Null out production data so client gets informed when response send back
pmcData.Hideout.Production[request.recipeId] = null;
// TODO - handle timestamp somehow?
// TODO: handle timestamp somehow?
return output;
}

View File

@ -56,7 +56,7 @@ export class InraidController
@inject("InsuranceService") protected insuranceService: InsuranceService,
@inject("InRaidHelper") protected inRaidHelper: InRaidHelper,
@inject("ApplicationContext") protected applicationContext: ApplicationContext,
@inject("ConfigServer") protected configServer: ConfigServer
@inject("ConfigServer") protected configServer: ConfigServer,
)
{
this.airdropConfig = this.configServer.getConfig(ConfigTypes.AIRDROP);
@ -124,7 +124,12 @@ export class InraidController
// Check for exit status
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);
// Purge profile of equipment/container items
@ -146,22 +151,30 @@ export class InraidController
if (locationName === "lighthouse" && postRaidRequest.profile.Info.Side.toLowerCase() === "usec")
{
// 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)
{
remainingCounter.Value --;
remainingCounter.Value--;
}
}
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();
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)
{
this.pmcChatResponseService.sendVictimResponse(sessionID, victims, serverPmcData);
@ -175,26 +188,37 @@ export class InraidController
/**
* Make changes to pmc profile after they've died in raid,
* Alter bodypart hp, handle insurance, delete inventory items, remove carried quest items
* @param postRaidSaveRequest Post-raid save request
* Alter body part hp, handle insurance, delete inventory items, remove carried quest items
* @param postRaidSaveRequest Post-raid save request
* @param pmcData Pmc profile
* @param sessionID Session id
* @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.inRaidHelper.deleteInventory(pmcData, sessionID);
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 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)
{
// 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)
{
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 pmcData player profile
*/
@ -234,13 +258,13 @@ export class InraidController
/**
* Reduce body part hp to % of max
* @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))
{
(<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
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.handlePostRaidPlayerScavProcess(scavData, sessionID, postRaidRequest, pmcData, isDead);
@ -278,7 +307,7 @@ export class InraidController
/**
* Does provided profile contain any condition counters
* @param profile Profile to check for condition counters
* @returns
* @returns
*/
protected profileHasConditionCounters(profile: IPmcData): boolean
{
@ -286,7 +315,6 @@ export class InraidController
{
return false;
}
return profile.ConditionCounters.Counters.length > 0;
}
@ -294,7 +322,7 @@ export class InraidController
{
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)
{
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
// 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];
// 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
for (const scavCounter of scavProfile.ConditionCounters.Counters)
{
this.logger.warning(`Processing counter: ${scavCounter.id} value:${scavCounter.value} quest:${scavCounter.qid}`);
const counterInPmcProfile = pmcProfile.ConditionCounters.Counters.find(x => x.id === scavCounter.id);
this.logger.warning(
`Processing counter: ${scavCounter.id} value:${scavCounter.value} quest:${scavCounter.qid}`,
);
const counterInPmcProfile = pmcProfile.ConditionCounters.Counters.find((x) => x.id === scavCounter.id);
if (!counterInPmcProfile)
{
// Doesn't exist yet, push it straight in
pmcProfile.ConditionCounters.Counters.push(scavCounter);
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
if (counterInPmcProfile.value !== scavCounter.value)
@ -363,7 +399,7 @@ export class InraidController
{
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);
}
}
@ -376,7 +412,13 @@ export class InraidController
* @param pmcData Pmc profile
* @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
scavData = this.inRaidHelper.setInventory(sessionID, scavData, offraidData.profile);
@ -411,14 +453,17 @@ export class InraidController
let fenceStanding = Number(pmcData.TradersInfo[fenceId].standing);
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
if (offraidData.exit === PlayerRaidEndState.SURVIVED)
{
fenceStanding += this.inraidConfig.scavExtractGain;
}
// Make standing changes to pmc profile
pmcData.TradersInfo[fenceId].standing = Math.min(Math.max(fenceStanding, -7), 15); // Ensure it stays between -7 and 15
this.logger.debug(`New fence standing: ${pmcData.TradersInfo[fenceId].standing}`);
@ -443,4 +488,4 @@ export class InraidController
{
return this.airdropConfig;
}
}
}

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 { IInsureRequestData } from "@spt-aki/models/eft/insurance/IInsureRequestData";
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 { ConfigTypes } from "@spt-aki/models/enums/ConfigTypes";
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 { MailSendService } from "@spt-aki/services/MailSendService";
import { PaymentService } from "@spt-aki/services/PaymentService";
import { MathUtil } from "@spt-aki/utils/MathUtil";
import { RandomUtil } from "@spt-aki/utils/RandomUtil";
import { TimeUtil } from "@spt-aki/utils/TimeUtil";
import { MathUtil } from "@spt-aki/utils/MathUtil";
@injectable()
export class InsuranceController
@ -48,7 +48,7 @@ export class InsuranceController
@inject("PaymentService") protected paymentService: PaymentService,
@inject("InsuranceService") protected insuranceService: InsuranceService,
@inject("MailSendService") protected mailSendService: MailSendService,
@inject("ConfigServer") protected configServer: ConfigServer
@inject("ConfigServer") protected configServer: ConfigServer,
)
{
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.
*
* @returns void
*/
*/
public processReturn(): void
{
// 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.
*
* @returns void
*/
*/
public processReturnByProfile(sessionID: string): void
{
// 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
{
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.
for (const insured of insuranceDetails)
@ -146,7 +150,7 @@ export class InsuranceController
*/
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
{
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.time !== packageInfo.time ||
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);
// 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.
if (hasRegularItems)
@ -238,12 +246,14 @@ export class InsuranceController
for (const insuredItem of insured.items)
{
// 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.
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;
}
@ -261,7 +271,9 @@ export class InsuranceController
if (!mainParent)
{
// 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;
}
@ -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
// children are currently attached attachments.
const directChildren = insured.items.filter(item => item.parentId === insuredItem._id);
const allChildrenAreAttachments = directChildren.every(child => this.itemHelper.isAttachmentAttached(child));
const directChildren = insured.items.filter((item) => item.parentId === insuredItem._id);
const allChildrenAreAttachments = directChildren.every((child) =>
this.itemHelper.isAttachmentAttached(child)
);
if (allChildrenAreAttachments)
{
for (const item of itemAndChildren)
@ -331,9 +345,14 @@ export class InsuranceController
* @param traderId The trader ID from the Insurance object.
* @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.
const parentItem = itemsMap.get(parentId);
@ -375,10 +394,10 @@ export class InsuranceController
*/
protected sortAttachmentsByPrice(attachments: Item[]): EnrichedItem[]
{
return attachments.map(item => ({
return attachments.map((item) => ({
...item,
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);
}
@ -389,7 +408,7 @@ export class InsuranceController
*/
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}`);
}
@ -404,7 +423,7 @@ export class InsuranceController
*/
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;
}
@ -415,16 +434,20 @@ export class InsuranceController
* @param successfulRolls The number of successful rolls.
* @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)
{
const valuableChild = attachments.find(({ _id }) => _id === attachmentsId);
const valuableChild = attachments.find(({_id}) => _id === attachmentsId);
if (valuableChild)
{
const { name, maxPrice } = valuableChild;
const {name, maxPrice} = valuableChild;
this.logger.debug(`Marked for removal - Child Item: ${name}, Max Price: ${maxPrice}`);
toDelete.add(attachmentsId);
}
@ -440,7 +463,7 @@ export class InsuranceController
*/
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)
{
// 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 (!parentExists && item.parentId !== hideoutParentId && item.slotId !== "hideout")
@ -478,7 +501,7 @@ export class InsuranceController
*/
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 : "";
if (hideoutParentId === "")
@ -502,7 +525,8 @@ export class InsuranceController
// successfully "failed" to return anything and an appropriate message should be sent to the player.
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);
}
@ -514,7 +538,7 @@ export class InsuranceController
insurance.messageContent.templateId,
insurance.items,
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.
const itemName = insuredItem ? ` for "${this.itemHelper.getItemName(insuredItem._tpl)}"` : "";
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;
}
@ -575,21 +601,18 @@ export class InsuranceController
{
itemsToPay.push({
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 = {
// eslint-disable-next-line @typescript-eslint/naming-convention
scheme_items: itemsToPay,
tid: body.tid,
Action: "",
type: "",
// eslint-disable-next-line @typescript-eslint/naming-convention
item_id: "",
count: 0,
// eslint-disable-next-line @typescript-eslint/naming-convention
scheme_id: 0
scheme_id: 0,
};
// pay for the item insurance
@ -604,7 +627,7 @@ export class InsuranceController
{
pmcData.InsuredItems.push({
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`);
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;

View File

@ -62,21 +62,25 @@ export class InventoryController
@inject("LocalisationService") protected localisationService: LocalisationService,
@inject("LootGenerator") protected lootGenerator: LootGenerator,
@inject("EventOutputHolder") protected eventOutputHolder: EventOutputHolder,
@inject("HttpResponseUtil") protected httpResponseUtil: HttpResponseUtil
@inject("HttpResponseUtil") protected httpResponseUtil: HttpResponseUtil,
)
{}
/**
* Move Item
* change location of item with parentId and slotId
* transfers items from one profile to another if fromOwner/toOwner is set in the body.
* otherwise, move is contained within the same profile_f.
* Move Item
* change location of item with parentId and slotId
* transfers items from one profile to another if fromOwner/toOwner is set in the body.
* otherwise, move is contained within the same profile_f.
* @param pmcData Profile
* @param moveRequest Move request data
* @param sessionID Session id
* @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);
@ -89,14 +93,14 @@ export class InventoryController
const ownerInventoryItems = this.inventoryHelper.getOwnerInventoryItems(moveRequest, sessionID);
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)
{
return this.getTraderExploitErrorResponse(output);
}
// 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)
{
// Internal item move but item never existed, possible dupe glitch
@ -123,14 +127,23 @@ export class InventoryController
*/
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
* Deep tree item deletion, also removes items from insurance list
*/
public removeItem(pmcData: IPmcData, itemId: string, sessionID: string, output: IItemEventRouterResponse = undefined): IItemEventRouterResponse
* Remove Item from Profile
* Deep tree item deletion, also removes items from insurance list
*/
public removeItem(
pmcData: IPmcData,
itemId: string,
sessionID: string,
output: IItemEventRouterResponse = undefined,
): IItemEventRouterResponse
{
return this.inventoryHelper.removeItem(pmcData, itemId, sessionID, output);
}
@ -140,29 +153,46 @@ export class InventoryController
* Implements functionality "Discard" from Main menu (Stash etc.)
* 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")
{
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)
? pmcData
: this.profileHelper.getFullProfile(sessionID).characters.scav;
return this.inventoryHelper.removeItem(profileToRemoveItemFrom, body.item, sessionID, this.eventOutputHolder.getOutput(sessionID));
const profileToRemoveItemFrom = (!body.fromOwner || body.fromOwner.id === pmcData._id) ?
pmcData :
this.profileHelper.getFullProfile(sessionID).characters.scav;
return this.inventoryHelper.removeItem(
profileToRemoveItemFrom,
body.item,
sessionID,
this.eventOutputHolder.getOutput(sessionID),
);
}
/**
* Split Item
* spliting 1 stack into 2
* splitting 1 stack into 2
* @param pmcData Player profile (unused, getOwnerInventoryItems() gets profile)
* @param request Split request
* @param sessionID Session/player id
* @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);
@ -172,15 +202,16 @@ export class InventoryController
// Handle cartridge edge-case
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
}
// The item being merged has three possible sources: pmc, scav or mail, getOwnerInventoryItems() handles getting correct one
const itemToSplit = inventoryItems.from.find(x => x._id === request.splitItem);
// The item being merged has three possible sources: pmc, scav or mail, getOwnerInventoryItems() handles getting
// correct one.
const itemToSplit = inventoryItems.from.find((x) => x._id === request.splitItem);
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);
return this.httpResponseUtil.appendErrorToOutput(output, errorMessage);
@ -197,7 +228,7 @@ export class InventoryController
output.profileChanges[sessionID].items.new.push({
_id: request.newItem,
_tpl: itemToSplit._tpl,
upd: updatedUpd
upd: updatedUpd,
});
// Update player inventory
@ -207,7 +238,7 @@ export class InventoryController
parentId: request.container.id,
slotId: request.container.container,
location: request.container.location,
upd: updatedUpd
upd: updatedUpd,
});
return output;
@ -229,20 +260,20 @@ export class InventoryController
const inventoryItems = this.inventoryHelper.getOwnerInventoryItems(body, sessionID);
// 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)
{
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);
return this.httpResponseUtil.appendErrorToOutput(output, errorMessage);
}
// 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)
{
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);
return this.httpResponseUtil.appendErrorToOutput(output, errorMessage);
@ -250,29 +281,29 @@ export class InventoryController
if (!(destinationItem.upd?.StackObjectsCount))
{
// No stackcount on destination, add one
destinationItem.upd = { StackObjectsCount: 1 };
// No stack count on destination, add one
destinationItem.upd = {StackObjectsCount: 1};
}
if (!sourceItem.upd)
{
sourceItem.upd = {
StackObjectsCount: 1
StackObjectsCount: 1,
};
}
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;
}
destinationItem.upd.StackObjectsCount += sourceItem.upd.StackObjectsCount; // Add source stackcount to destination
output.profileChanges[sessionID].items.del.push({ _id: sourceItem._id }); // Inform client source item being deleted
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
const indexOfItemToRemove = inventoryItems.from.findIndex(x => x._id === sourceItem._id);
const indexOfItemToRemove = inventoryItems.from.findIndex((x) => x._id === sourceItem._id);
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);
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: should make use of getOwnerInventoryItems(), stack being transferred may not always be on pmc
* // 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
* 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"
* @param pmcData Player profile
@ -292,7 +323,11 @@ export class InventoryController
* @param sessionID Session id
* @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);
@ -314,7 +349,7 @@ export class InventoryController
if (sourceItem !== null && destinationItem !== null)
{
// Both items found, exit loop
break;
break;
}
}
@ -359,7 +394,7 @@ export class InventoryController
}
else
{
Object.assign(destinationItem, { upd: { StackObjectsCount: 1 } });
Object.assign(destinationItem, {upd: {StackObjectsCount: 1}});
}
destinationItem.upd.StackObjectsCount = destinationStackCount + body.count;
@ -368,28 +403,28 @@ export class InventoryController
}
/**
* Swap Item
* 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
*/
* Swap Item
* 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
*/
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)
{
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)
{
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;
// to.container is the slotid
// to.container is the slotId
itemOne.slotId = request.to.container;
// 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
{
// Fix for folding weapons while on they're in the Scav inventory
if (body.fromOwner
&& body.fromOwner.type === "Profile"
&& body.fromOwner.id !== pmcData._id)
if (
body.fromOwner &&
body.fromOwner.type === "Profile" &&
body.fromOwner.id !== pmcData._id
)
{
pmcData = this.profileHelper.getScavProfile(sessionID);
}
@ -434,19 +471,19 @@ export class InventoryController
{
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 {
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 body Toggle request
* @param sessionID Session id
@ -460,27 +497,31 @@ export class InventoryController
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.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.Togglable = { On: body.value };
itemToToggle.upd.Togglable = {On: body.value};
return this.eventOutputHolder.getOutput(sessionID);
}
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 {
warnings: [],
profileChanges: {}
profileChanges: {},
};
}
@ -499,11 +540,11 @@ export class InventoryController
{
if ("upd" in item)
{
item.upd.Tag = { Color: body.TagColor, Name: body.TagName };
item.upd.Tag = {Color: body.TagColor, Name: body.TagName};
}
else
{
item.upd = { Tag: { Color: body.TagColor, Name: body.TagName } };
item.upd = {Tag: {Color: body.TagColor, Name: body.TagName}};
}
return this.eventOutputHolder.getOutput(sessionID);
@ -512,18 +553,22 @@ export class InventoryController
return {
warnings: [],
profileChanges: {}
profileChanges: {},
};
}
/**
* Bind an inventory item to the quick access menu at bottom of player screen
* @param pmcData Player profile
* @param bindRequest Reqeust object
* @param bindRequest Request object
* @param sessionID Session id
* @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)
{
@ -538,7 +583,6 @@ export class InventoryController
return this.eventOutputHolder.getOutput(sessionID);
}
/**
* Handles examining an item
* @param pmcData player profile
@ -546,7 +590,11 @@ export class InventoryController
* @param sessionID session id
* @returns response
*/
public examineItem(pmcData: IPmcData, body: IInventoryExamineRequestData, sessionID: string): IItemEventRouterResponse
public examineItem(
pmcData: IPmcData,
body: IInventoryExamineRequestData,
sessionID: string,
): IItemEventRouterResponse
{
let itemId = "";
if ("fromOwner" in body)
@ -559,7 +607,7 @@ export class InventoryController
{
this.logger.error(this.localisationService.getText("inventory-examine_item_does_not_exist", body.item));
}
// get hideout item
if (body.fromOwner.type === "HideoutProduction")
{
@ -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
* @returns tplid
* @returns string
*/
protected getExaminedItemTpl(body: IInventoryExamineRequestData): string
{
@ -618,49 +666,55 @@ export class InventoryController
}
else if (body.fromOwner.id === Traders.FENCE)
{
// get tpl from fence assorts
return this.fenceService.getRawFenceAssorts().items.find(x => x._id === body.item)._tpl;
// Get tpl from fence assorts
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
return this.databaseServer.getTables().traders[body.fromOwner.id].assort.items.find(item => item._id === body.item)._tpl;
// Not fence
// 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")
{
// 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];
if (item)
{
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);
if (!offer)
{
offer = this.ragfairOfferService.getOfferByOfferId(body.fromOwner.id);
}
// try find examine item inside offer items array
const matchingItem = offer.items.find(x => x._id === body.item);
// Try find examine item inside offer items array
const matchingItem = offer.items.find((x) => x._id === body.item);
if (matchingItem)
{
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));
}
}
public readEncyclopedia(pmcData: IPmcData, body: IInventoryReadEncyclopediaRequestData, sessionID: string): IItemEventRouterResponse
public readEncyclopedia(
pmcData: IPmcData,
body: IInventoryReadEncyclopediaRequestData,
sessionID: string,
): IItemEventRouterResponse
{
for (const id of body.ids)
{
pmcData.Encyclopedia[id] = true;
}
return this.eventOutputHolder.getOutput(sessionID);
}
@ -672,15 +726,20 @@ export class InventoryController
* @param sessionID Session id
* @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)
{
const inventoryItem = pmcData.Inventory.items.find(x => x._id === change._id);
const inventoryItem = pmcData.Inventory.items.find((x) => x._id === change._id);
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;
}
@ -707,13 +766,17 @@ export class InventoryController
* @param sessionID Session id
* @returns IItemEventRouterResponse
*/
public createMapMarker(pmcData: IPmcData, request: IInventoryCreateMarkerRequestData, sessionID: string): IItemEventRouterResponse
public createMapMarker(
pmcData: IPmcData,
request: IInventoryCreateMarkerRequestData,
sessionID: string,
): IItemEventRouterResponse
{
// 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
mapItem.upd.Map = mapItem.upd.Map || { Markers: [] };
mapItem.upd.Map = mapItem.upd.Map || {Markers: []};
request.mapMarker.Note = this.sanitiseMapMarkerText(request.mapMarker.Note);
mapItem.upd.Map.Markers.push(request.mapMarker);
@ -731,10 +794,14 @@ export class InventoryController
* @param sessionID Session id
* @returns IItemEventRouterResponse
*/
public deleteMapMarker(pmcData: IPmcData, request: IInventoryDeleteMarkerRequestData, sessionID: string): IItemEventRouterResponse
public deleteMapMarker(
pmcData: IPmcData,
request: IInventoryDeleteMarkerRequestData,
sessionID: string,
): IItemEventRouterResponse
{
// 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
const markers = mapItem.upd.Map.Markers.filter((marker) =>
@ -756,13 +823,17 @@ export class InventoryController
* @param sessionID Session id
* @returns IItemEventRouterResponse
*/
public editMapMarker(pmcData: IPmcData, request: IInventoryEditMarkerRequestData, sessionID: string): IItemEventRouterResponse
public editMapMarker(
pmcData: IPmcData,
request: IInventoryEditMarkerRequestData,
sessionID: string,
): IItemEventRouterResponse
{
// 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
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);
mapItem.upd.Map.Markers[indexOfExistingNote] = request.mapMarker;
@ -791,15 +862,19 @@ export class InventoryController
* @param sessionID Session id
* @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 isSealedWeaponBox = containerDetails[1]._name.includes("event_container_airdrop");
const newItemRequest: IAddItemRequestData = {
tid: "RandomLootContainer",
items: []
items: [],
};
let foundInRaid = false;
@ -829,4 +904,4 @@ export class InventoryController
return output;
}
}
}

View File

@ -30,7 +30,7 @@ export class LauncherController
@inject("DatabaseServer") protected databaseServer: DatabaseServer,
@inject("LocalisationService") protected localisationService: LocalisationService,
@inject("PreAkiModLoader") protected preAkiModLoader: PreAkiModLoader,
@inject("ConfigServer") protected configServer: ConfigServer
@inject("ConfigServer") protected configServer: ConfigServer,
)
{
this.coreConfig = this.configServer.getConfig(ConfigTypes.CORE);
@ -42,30 +42,26 @@ export class LauncherController
backendUrl: this.httpServerHelper.getBackendUrl(),
name: this.coreConfig.serverName,
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
* @returns
* Get descriptive text for each of the profile editions a player can choose
* @returns
*/
protected getProfileDescriptions(): Record<string, string>
{
return {
/* eslint-disable @typescript-eslint/naming-convention */
Standard: this.localisationService.getText("launcher-profile_standard"),
// eslint-disable-next-line @typescript-eslint/naming-convention
"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"),
// eslint-disable-next-line @typescript-eslint/naming-convention
"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"),
// eslint-disable-next-line @typescript-eslint/naming-convention
"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,
password: info.password,
wipe: true,
edition: info.edition
edition: info.edition,
};
this.saveServer.createProfile(newProfileDetails);
this.saveServer.loadProfile(sessionID);
this.saveServer.saveProfile(sessionID);
return sessionID;
}

View File

@ -41,7 +41,7 @@ export class LocationController
@inject("LootGenerator") protected lootGenerator: LootGenerator,
@inject("DatabaseServer") protected databaseServer: DatabaseServer,
@inject("TimeUtil") protected timeUtil: TimeUtil,
@inject("ConfigServer") protected configServer: ConfigServer
@inject("ConfigServer") protected configServer: ConfigServer,
)
{
this.airdropConfig = this.configServer.getConfig(ConfigTypes.AIRDROP);
@ -88,17 +88,23 @@ export class LocationController
// Create containers and add loot to them
const staticLoot = this.locationGenerator.generateStaticContainers(location.base, staticAmmoDist);
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 dynamicSpawnPoints: SpawnpointTemplate[] = this.locationGenerator.generateDynamicLoot(dynamicLootDist, staticAmmoDist, name);
const dynamicSpawnPoints: SpawnpointTemplate[] = this.locationGenerator.generateDynamicLoot(
dynamicLootDist,
staticAmmoDist,
name,
);
for (const spawnPoint of dynamicSpawnPoints)
{
output.Loot.push(spawnPoint);
}
// 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));
return output;
@ -110,7 +116,6 @@ export class LocationController
* @param sessionId Players Id
* @returns ILocationsGenerateAllResponse
*/
// eslint-disable-next-line @typescript-eslint/no-unused-vars
public generateAll(sessionId: string): ILocationsGenerateAllResponse
{
const locationsFromDb = this.databaseServer.getTables().locations;
@ -130,15 +135,15 @@ export class LocationController
locations[mapBase._Id] = mapBase;
}
return {
return {
locations: locations,
paths: locationsFromDb.base.paths
paths: locationsFromDb.base.paths,
};
}
/**
* 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
* @returns Array of LootItem objects
*/
@ -174,7 +179,9 @@ export class LocationController
let lootSettingsByType = this.airdropConfig.loot[airdropType];
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];
}
@ -187,7 +194,7 @@ export class LocationController
itemLimits: lootSettingsByType.itemLimits,
itemStackLimits: lootSettingsByType.itemStackLimits,
armorLevelWhitelist: lootSettingsByType.armorLevelWhitelist,
allowBossItems: lootSettingsByType.allowBossItems
allowBossItems: lootSettingsByType.allowBossItems,
};
}
}
}

View File

@ -56,7 +56,7 @@ export class MatchController
@inject("BotGenerationCacheService") protected botGenerationCacheService: BotGenerationCacheService,
@inject("MailSendService") protected mailSendService: MailSendService,
@inject("LootGenerator") protected lootGenerator: LootGenerator,
@inject("ApplicationContext") protected applicationContext: ApplicationContext
@inject("ApplicationContext") protected applicationContext: ApplicationContext,
)
{
this.matchConfig = this.configServer.getConfig(ConfigTypes.MATCH);
@ -99,12 +99,11 @@ export class MatchController
}
/** Handle match/group/start_game */
// eslint-disable-next-line @typescript-eslint/no-unused-vars
public joinMatch(info: IJoinMatchRequestData, sessionId: string): IJoinMatchResult
{
const output: IJoinMatchResult = {
maxPveCountExceeded: false,
profiles: []
profiles: [],
};
// get list of players joining into the match
@ -120,20 +119,18 @@ export class MatchController
raidMode: "Online",
mode: "deathmatch",
shortid: null,
// eslint-disable-next-line @typescript-eslint/naming-convention
additional_info: null
additional_info: null,
});
return output;
}
/** Handle client/match/group/status */
// eslint-disable-next-line @typescript-eslint/no-unused-vars
public getGroupStatus(info: IGetGroupStatusRequestData): any
{
return {
players: [],
maxPveCountExceeded: false
maxPveCountExceeded: false,
};
}
@ -147,15 +144,17 @@ export class MatchController
// Store request data for access during bot generation
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)
{
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
const currentProfile = this.saveServer.getProfile(sessionID);
this.profileSnapshotService.storeProfileSnapshot(sessionID, currentProfile);
}
@ -178,7 +177,7 @@ export class MatchController
/** Handle client/match/offline/end */
public endOfflineRaid(info: IEndOfflineRaidRequestData, sessionId: string): void
{
{
const pmcData: IPmcData = this.profileHelper.getPmcProfile(sessionId);
const extractName = info.exitName;
@ -223,7 +222,7 @@ export class MatchController
// Generate reward for taking coop extract
const loot = this.lootGenerator.createRandomLoot(this.traderConfig.fence.coopExtractGift);
const mailableLoot: Item[] = [];
const parentId = this.hashUtil.generate();
for (const item of loot)
{
@ -235,9 +234,9 @@ export class MatchController
parentId: parentId,
upd: {
StackObjectsCount: item.stackCount,
SpawnedInSession: true
}
}
SpawnedInSession: true,
},
},
);
}
@ -248,7 +247,7 @@ export class MatchController
MessageType.MESSAGE_WITH_ITEMS,
this.randomUtil.getArrayValue(this.traderConfig.fence.coopExtractGift.messageLocaleIds),
mailableLoot,
this.timeUtil.getHoursAsSeconds(this.traderConfig.fence.coopExtractGift.giftExpiryHours)
this.timeUtil.getHoursAsSeconds(this.traderConfig.fence.coopExtractGift.giftExpiryHours),
);
}
@ -292,25 +291,27 @@ export class MatchController
const fenceId: string = Traders.FENCE;
this.updateFenceStandingInProfile(pmcData, fenceId, extractName);
this.traderHelper.lvlUp(fenceId, pmcData);
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]}`,
);
}
/**
* Update players fence trader standing value in profile
* @param pmcData Player profile
* @param fenceId Id of fence trader
* @param extractName Name of extract used
* @param extractName Name of extract used
*/
protected updateFenceStandingInProfile(pmcData: IPmcData, fenceId: string, extractName: string): void
{
let fenceStanding = Number(pmcData.TradersInfo[fenceId].standing);
// Not exact replica of Live behaviour
// Simplified for now, no real reason to do the whole (unconfirmed) extra 0.01 standing per day regeneration mechanic
// Not exact replica of Live behaviour... Simplified for now. No real reason to do the whole (unconfirmed)
// extra 0.01 standing per day regeneration mechanic.
const baseGain: number = this.inraidConfig.carExtractBaseStandingGain;
const extractCount: number = pmcData.CarExtractCounts[extractName];
@ -321,4 +322,4 @@ export class MatchController
this.logger.debug(`Old vs new fence standing: ${pmcData.TradersInfo[fenceId].standing}, ${newFenceStanding}`);
pmcData.TradersInfo[fenceId].standing = newFenceStanding;
}
}
}

View File

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

View File

@ -14,7 +14,7 @@ export class NotifierController
constructor(
@inject("NotifierHelper") protected notifierHelper: NotifierHelper,
@inject("HttpServerHelper") protected httpServerHelper: HttpServerHelper,
@inject("NotificationService") protected notificationService: NotificationService
@inject("NotificationService") protected notificationService: NotificationService,
)
{}
@ -82,11 +82,10 @@ export class NotifierController
{
return {
server: this.httpServerHelper.buildUrl(),
// eslint-disable-next-line @typescript-eslint/naming-convention
channel_id: sessionID,
url: "",
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("DatabaseServer") protected databaseServer: DatabaseServer,
@inject("ItemHelper") protected itemHelper: ItemHelper,
@inject("SaveServer") protected saveServer: SaveServer
@inject("SaveServer") protected saveServer: SaveServer,
)
{ }
{}
/** Handle client/handbook/builds/my/list */
public getUserBuilds(sessionID: string): IUserBuilds
@ -35,21 +35,27 @@ export class PresetBuildController
{
profile.userbuilds = {
equipmentBuilds: [],
weaponBuilds: []
weaponBuilds: [],
};
}
// 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 playerSecureContainer = profile.characters.pmc.Inventory.items?.find(x => x.slotId === "SecuredContainer");
const firstDefaultItemsSecureContainer = defaultEquipmentPresets[0]?.items?.find(x => x.slotId === "SecuredContainer");
const defaultEquipmentPresets = this.jsonUtil.clone(
this.databaseServer.getTables().templates.defaultEquipmentPresets,
);
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)
{
// 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)
{
// Find presets secure container
const secureContainer = defaultPreset.items.find(x => x.slotId === "SecuredContainer");
const secureContainer = defaultPreset.items.find((x) => x.slotId === "SecuredContainer");
if (secureContainer)
{
secureContainer._tpl = playerSecureContainer._tpl;
@ -65,9 +71,13 @@ export class PresetBuildController
}
/** 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);
// Replace duplicate Id's. The first item is the base item.
@ -82,15 +92,19 @@ export class PresetBuildController
name: body.name,
root: body.root,
items: body.items,
type: "weapon"
type: "weapon",
};
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)
{
// 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
{
@ -105,12 +119,21 @@ export class PresetBuildController
}
/** 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");
}
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 existingSavedBuilds: any[] = this.saveServer.getProfile(sessionID).userbuilds[buildType];
@ -125,14 +148,18 @@ export class PresetBuildController
buildType: "Custom",
root: body.root,
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)
{
// 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
{
@ -152,26 +179,34 @@ export class PresetBuildController
}
/** 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);
}
/** 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);
}
protected removePlayerBuild(pmcData: IPmcData, id: string, sessionID: string): IItemEventRouterResponse
{
const weaponBuilds = this.saveServer.getProfile(sessionID).userbuilds.weaponBuilds;
const equipmentBuilds = this.saveServer.getProfile(sessionID).userbuilds.equipmentBuilds;
// 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)
{
weaponBuilds.splice(weaponBuilds.indexOf(matchingWeaponBuild), 1);
@ -180,7 +215,7 @@ export class PresetBuildController
}
// 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)
{
equipmentBuilds.splice(equipmentBuilds.indexOf(matchingEquipmentBuild), 1);
@ -191,8 +226,7 @@ export class PresetBuildController
{
this.logger.error(`Unable to delete preset, cannot find ${id} in weapon or equipment presets`);
}
return this.eventOutputHolder.getOutput(sessionID);
}
}
}

View File

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

View File

@ -47,9 +47,9 @@ export class ProfileController
@inject("TraderHelper") protected traderHelper: TraderHelper,
@inject("DialogueHelper") protected dialogueHelper: DialogueHelper,
@inject("QuestHelper") protected questHelper: QuestHelper,
@inject("ProfileHelper") protected profileHelper: ProfileHelper
@inject("ProfileHelper") protected profileHelper: ProfileHelper,
)
{ }
{}
/**
* Handle /launcher/profiles
@ -76,7 +76,7 @@ export class ProfileController
const pmc = profile.characters.pmc;
// make sure character completed creation
if (!((pmc?.Info?.Level)))
if (!(pmc?.Info?.Level))
{
return {
username: profile.info.username,
@ -87,7 +87,7 @@ export class ProfileController
prevexp: 0,
nextlvl: 0,
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),
nextlvl: nextlvl,
maxlvl: maxlvl,
akiData: profile.aki
akiData: profile.aki,
};
return result;
@ -122,7 +122,8 @@ export class ProfileController
public createProfile(info: IProfileCreateRequestData, sessionID: string): void
{
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;
// Delete existing profile
@ -148,11 +149,16 @@ export class ProfileController
if (!pmcData.UnlockedInfo)
{
pmcData.UnlockedInfo = { unlockedProductionRecipe: [] };
pmcData.UnlockedInfo = {unlockedProductionRecipe: []};
}
// 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 = {};
// Create profile
@ -160,7 +166,7 @@ export class ProfileController
info: account,
characters: {
pmc: pmcData,
scav: {} as IPmcData
scav: {} as IPmcData,
},
suits: profile.suits,
userbuilds: profile.userbuilds,
@ -169,7 +175,7 @@ export class ProfileController
vitality: {} as Vitality,
inraid: {} as Inraid,
insurance: [],
traderPurchases: {}
traderPurchases: {},
};
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
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
const response = this.eventOutputHolder.getOutput(sessionID);
@ -219,7 +229,9 @@ export class ProfileController
}
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,16 +242,29 @@ export class ProfileController
* @param sessionID Session id
* @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)
{
const questFromDb = this.questHelper.getQuestFromDb(quest.qid, profileDetails.characters.pmc);
// Get messageId of text to send to player as text message in game
// Copy of code from QuestController.acceptQuest()
const messageId = this.questHelper.getMessageIdForQuestStart(questFromDb.startedMessageText, questFromDb.description);
const itemRewards = this.questHelper.applyQuestReward(profileDetails.characters.pmc, quest.qid, QuestStatus.Started, sessionID, response);
const messageId = this.questHelper.getMessageIdForQuestStart(
questFromDb.startedMessageText,
questFromDb.description,
);
const itemRewards = this.questHelper.applyQuestReward(
profileDetails.characters.pmc,
quest.qid,
QuestStatus.Started,
sessionID,
response,
);
this.mailSendService.sendLocalisedNpcMessageToPlayer(
sessionID,
@ -247,7 +272,8 @@ export class ProfileController
MessageType.QUEST_START,
messageId,
itemRewards,
this.timeUtil.getHoursAsSeconds(100));
this.timeUtil.getHoursAsSeconds(100),
);
}
}
@ -266,7 +292,7 @@ export class ProfileController
/**
* Generate a player scav object
* PMC profile MUST exist first before pscav can be generated
* @param sessionID
* @param sessionID
* @returns IPmcData object
*/
public generatePlayerScav(sessionID: string): IPmcData
@ -323,18 +349,15 @@ export class ProfileController
/**
* Handle client/game/profile/search
*/
// eslint-disable-next-line @typescript-eslint/no-unused-vars
public getFriends(info: ISearchFriendRequestData, sessionID: string): ISearchFriendResponse[]
{
return [
{
_id: this.hashUtil.generate(),
Info: {
Level: 1,
Side: "Bear",
Nickname: info.nickname
}
}
];
return [{
_id: this.hashUtil.generate(),
Info: {
Level: 1,
Side: "Bear",
Nickname: info.nickname,
},
}];
}
}
}

View File

@ -57,7 +57,7 @@ export class QuestController
@inject("LocaleService") protected localeService: LocaleService,
@inject("SeasonalEventService") protected seasonalEventService: SeasonalEventService,
@inject("LocalisationService") protected localisationService: LocalisationService,
@inject("ConfigServer") protected configServer: ConfigServer
@inject("ConfigServer") protected configServer: ConfigServer,
)
{
this.questConfig = this.configServer.getConfig(ConfigTypes.QUEST);
@ -79,7 +79,7 @@ export class QuestController
for (const quest of allQuests)
{
// 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)
{
quest.sptStatus = questInProfile.status;
@ -105,8 +105,12 @@ export class QuestController
}
const questRequirements = this.questConditionHelper.getQuestConditions(quest.conditions.AvailableForStart);
const loyaltyRequirements = this.questConditionHelper.getLoyaltyConditions(quest.conditions.AvailableForStart);
const standingRequirements = this.questConditionHelper.getStandingConditions(quest.conditions.AvailableForStart);
const loyaltyRequirements = this.questConditionHelper.getLoyaltyConditions(
quest.conditions.AvailableForStart,
);
const standingRequirements = this.questConditionHelper.getStandingConditions(
quest.conditions.AvailableForStart,
);
// Quest has no conditions, standing or loyalty conditions, add to visible quest list
if (questRequirements.length === 0 && loyaltyRequirements.length === 0 && standingRequirements.length === 0)
@ -122,14 +126,14 @@ export class QuestController
for (const conditionToFulfil of questRequirements)
{
// 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)
{
haveCompletedPreviousQuest = false;
break;
}
// Prereq does not have its status requirement fulfilled
// Prerequisite does not have its status requirement fulfilled
if (!conditionToFulfil._props.status.includes(prerequisiteQuest.status))
{
haveCompletedPreviousQuest = false;
@ -144,7 +148,11 @@ export class QuestController
const unlockTime = previousQuestCompleteTime + conditionToFulfil._props.availableAfter;
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`,
);
}
}
}
@ -207,7 +215,7 @@ export class QuestController
}
// All conditions passed / has no level requirement, valid
return true;
return true;
}
/**
@ -221,19 +229,28 @@ export class QuestController
const isHalloweenEventActive = this.seasonalEventService.halloweenEventEnabled();
// Not christmas + quest is for christmas
if (!isChristmasEventActive && this.seasonalEventService.isQuestRelatedToEvent(questId, SeasonalEventType.CHRISTMAS))
if (
!isChristmasEventActive &&
this.seasonalEventService.isQuestRelatedToEvent(questId, SeasonalEventType.CHRISTMAS)
)
{
return false;
}
// Not halloween + quest is for halloween
if (!isHalloweenEventActive && this.seasonalEventService.isQuestRelatedToEvent(questId, SeasonalEventType.HALLOWEEN))
if (
!isHalloweenEventActive &&
this.seasonalEventService.isQuestRelatedToEvent(questId, SeasonalEventType.HALLOWEEN)
)
{
return false;
}
// 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;
}
@ -274,7 +291,11 @@ export class QuestController
* @param sessionID Session id
* @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);
@ -282,7 +303,7 @@ export class QuestController
const newQuest = this.questHelper.getQuestReadyForProfile(pmcData, startedState, acceptedQuest);
// 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
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".
const questFromDb = this.questHelper.getQuestFromDb(acceptedQuest.qid, pmcData);
// Get messageId of text to send to player as text message in game
const messageId = this.questHelper.getMessageIdForQuestStart(questFromDb.startedMessageText, questFromDb.description);
const startedQuestRewards = this.questHelper.applyQuestReward(pmcData, acceptedQuest.qid, QuestStatus.Started, sessionID, acceptQuestResponse);
const messageId = this.questHelper.getMessageIdForQuestStart(
questFromDb.startedMessageText,
questFromDb.description,
);
const startedQuestRewards = this.questHelper.applyQuestReward(
pmcData,
acceptedQuest.qid,
QuestStatus.Started,
sessionID,
acceptQuestResponse,
);
this.mailSendService.sendLocalisedNpcMessageToPlayer(
sessionID,
@ -306,9 +336,11 @@ export class QuestController
MessageType.QUEST_START,
messageId,
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;
}
@ -322,24 +354,36 @@ export class QuestController
* @param sessionID Session id
* @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 desiredQuestState = QuestStatus.Started;
const newQuest = this.questHelper.getQuestReadyForProfile(pmcData, desiredQuestState, acceptedQuest);
pmcData.Quests.push(newQuest);
const repeatableQuestProfile = this.getRepeatableQuestFromProfile(pmcData, acceptedQuest);
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"));
}
// 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);
if (!fullProfile.characters.scav.Quests)
@ -351,46 +395,67 @@ export class QuestController
}
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
let questStartedMessageText = locale[questStartedMessageKey];
// TODO: remove this whole if statement, possibly not required?
// TODO: Remove this whole if statement, possibly not required?
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"];
questStartedMessageText = enLocale[repeatableQuestProfile.startedMessageText];
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(
sessionID,
this.traderHelper.getTraderById(repeatableQuestProfile.traderId),
MessageType.QUEST_START,
questStartedMessageKey,
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 = {};
change[repeatableQuestProfile._id] = repeatableSettings.changeRequirement[repeatableQuestProfile._id];
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,
endTime: repeatableSettings.endTime,
changeRequirement: change,
activeQuests: [repeatableQuestProfile],
inactiveQuests: []
inactiveQuests: [],
};
acceptQuestResponse.profileChanges[sessionID].repeatableQuests = [responseData];
@ -407,12 +472,12 @@ export class QuestController
{
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)
{
this.logger.debug(`Accepted repeatable quest ${acceptedQuest.qid} from ${repeatableQuest.name}`);
matchingQuest.sptRepatableGroupName = repeatableQuest.name;
return matchingQuest;
}
}
@ -430,7 +495,11 @@ export class QuestController
* @param sessionID Session id
* @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);
@ -442,7 +511,13 @@ export class QuestController
const newQuestState = QuestStatus.Success;
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
const questsToFail = this.getQuestsFailedByCompletingQuest(completedQuestId);
@ -456,8 +531,8 @@ export class QuestController
// Add diff of quests before completion vs after for client response
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);
// 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
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)
{
currentRepeatable.activeQuests = currentRepeatable.activeQuests.filter(x => x._id !== completedQuestId);
currentRepeatable.activeQuests = currentRepeatable.activeQuests.filter((x) =>
x._id !== completedQuestId
);
currentRepeatable.inactiveQuests.push(repeatableQuest);
// 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
{
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)
{
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;
}
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
* @param preQuestStatusus Quests before
* @param preQuestStatuses Quests before
* @param postQuestStatuses Quests after
* @returns QuestStatusChange array
*/
protected getQuestsWithDifferentStatuses(preQuestStatusus: IQuestStatus[], postQuestStatuses: IQuestStatus[]): IQuestStatus[]
protected getQuestsWithDifferentStatuses(
preQuestStatuses: IQuestStatus[],
postQuestStatuses: IQuestStatus[],
): IQuestStatus[]
{
const result: IQuestStatus[] = [];
for (const quest of postQuestStatuses)
{
// 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)
{
result.push(quest);
@ -544,7 +629,12 @@ export class QuestController
* @param completedQuestId Completed quest id
* @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);
@ -554,7 +644,8 @@ export class QuestController
MessageType.QUEST_SUCCESS,
quest.successMessageText,
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
for (const quest of quests)
{
// If quest has prereq 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);
// 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
);
if (nextQuestWaitCondition)
{
// 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
const existingQuestInProfile = pmcData.Quests.find(x => x.qid === quest._id);
const existingQuestInProfile = pmcData.Quests.find((x) => x.qid === quest._id);
if (existingQuestInProfile)
{
existingQuestInProfile.availableAfter = availableAfterTimestamp;
@ -592,10 +686,9 @@ export class QuestController
startTime: 0,
status: QuestStatus.AvailableAfter,
statusTimers: {
// eslint-disable-next-line @typescript-eslint/naming-convention
"9": this.timeUtil.getTimestamp()
"9": this.timeUtil.getTimestamp(), // eslint-disable-line @typescript-eslint/naming-convention
},
availableAfter: availableAfterTimestamp
availableAfter: availableAfterTimestamp,
});
}
}
@ -617,7 +710,7 @@ export class QuestController
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 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)
{
// 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;
}
const isActiveQuestInPlayerProfile = pmcData.Quests.find(y => y.qid === questToFail._id);
const isActiveQuestInPlayerProfile = pmcData.Quests.find((y) => y.qid === questToFail._id);
if (isActiveQuestInPlayerProfile)
{
const failBody: IFailQuestRequestData = {
Action: "QuestComplete",
qid: questToFail._id,
removeExcessItems: true
removeExcessItems: true,
};
this.questHelper.failQuest(pmcData, failBody, sessionID, output);
}
@ -657,7 +755,7 @@ export class QuestController
qid: questToFail._id,
startTime: this.timeUtil.getTimestamp(),
statusTimers: statusTimers,
status: QuestStatus.Fail
status: QuestStatus.Fail,
};
pmcData.Quests.push(questData);
}
@ -671,7 +769,11 @@ export class QuestController
* @param sessionID Session id
* @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 handoverQuestTypes = ["HandoverItem", "WeaponAssembly"];
@ -684,20 +786,33 @@ export class QuestController
let handoverRequirements: AvailableForConditions;
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);
isItemHandoverQuest = condition._parent === handoverQuestTypes[0];
handoverRequirements = condition;
const profileCounter = (handoverQuestRequest.conditionId in pmcData.BackendCounters)
? pmcData.BackendCounters[handoverQuestRequest.conditionId].value
: 0;
const profileCounter = (handoverQuestRequest.conditionId in pmcData.BackendCounters) ?
pmcData.BackendCounters[handoverQuestRequest.conditionId].value :
0;
handedInCount -= profileCounter;
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;
}
@ -710,15 +825,20 @@ export class QuestController
{
return this.showRepeatableQuestInvalidConditionError(handoverQuestRequest, output);
}
let totalItemCountToRemove = 0;
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))
{
// Item handed in by player doesnt match what was requested
return this.showQuestItemHandoverMatchError(handoverQuestRequest, matchingItemInProfile, handoverRequirements, output);
// Item handed in by player doesn't match what was requested
return this.showQuestItemHandoverMatchError(
handoverQuestRequest,
matchingItemInProfile,
handoverRequirements,
output,
);
}
// Remove the right quantity of given items
@ -727,7 +847,13 @@ export class QuestController
if (itemHandover.count - itemCountToRemove > 0)
{
// 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)
{
break;
@ -740,7 +866,7 @@ export class QuestController
let index = pmcData.Inventory.items.length;
// 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
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;
}
@ -764,9 +895,15 @@ export class QuestController
* @param output Response to send to user
* @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);
return this.httpResponseUtil.appendErrorToOutput(output, errorMessage);
@ -780,9 +917,18 @@ export class QuestController
* @param output Response to send to user
* @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);
return this.httpResponseUtil.appendErrorToOutput(output, errorMessage);
@ -796,7 +942,12 @@ export class QuestController
* @param questId quest id counter is associated 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)
{
@ -804,10 +955,10 @@ export class QuestController
return;
}
pmcData.BackendCounters[conditionId] = {
pmcData.BackendCounters[conditionId] = {
id: conditionId,
qid: questId,
value: counterValue
value: counterValue,
};
}
}
}

View File

@ -76,7 +76,7 @@ export class RagfairController
@inject("RagfairRequiredItemsService") protected ragfairRequiredItemsService: RagfairRequiredItemsService,
@inject("RagfairOfferGenerator") protected ragfairOfferGenerator: RagfairOfferGenerator,
@inject("LocalisationService") protected localisationService: LocalisationService,
@inject("ConfigServer") protected configServer: ConfigServer
@inject("ConfigServer") protected configServer: ConfigServer,
)
{
this.ragfairConfig = this.configServer.getConfig(ConfigTypes.RAGFAIR);
@ -89,7 +89,7 @@ export class RagfairController
const result: IGetOffersResult = {
offers: [],
offersCount: searchRequest.limit,
selectedCategory: searchRequest.handbookId
selectedCategory: searchRequest.handbookId,
};
const pmcProfile = this.profileHelper.getPmcProfile(sessionID);
@ -106,7 +106,11 @@ export class RagfairController
this.addIndexValueToOffers(result.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
const profile = this.profileHelper.getFullProfile(sessionID);
@ -114,13 +118,13 @@ export class RagfairController
{
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
// is not completed yet
// for the items, check the barter schemes. The method getDisplayableAssorts sets a flag sptQuestLocked
// to true if the quest is not completed yet
if (this.ragfairOfferHelper.traderOfferItemQuestLocked(offer, traderAssorts))
{
offer.locked = true;
}
// Update offers BuyRestrictionCurrent/BuyRestrictionMax values
this.setTraderOfferPurchaseLimits(offer, profile);
this.setTraderOfferStackSize(offer);
@ -132,10 +136,10 @@ export class RagfairController
result.offersCount = result.offers.length;
// Handle paging before returning results only if searching for general items, not preset items
if (searchRequest.buildCount === 0)
if (searchRequest.buildCount === 0)
{
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);
}
return result;
@ -149,7 +153,12 @@ export class RagfairController
* @param pmcProfile Player profile
* @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
if (searchRequest.buildCount)
@ -165,14 +174,14 @@ export class RagfairController
* Get categories for the type of search being performed, linked/required/all
* @param searchRequest Client search request data
* @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>
{
// Linked/required search categories
if (this.isLinkedSearch(searchRequest) || this.isRequiredSearch(searchRequest))
{
return this.ragfairServer.getBespokeCategories(offers);
return this.ragfairServer.getBespokeCategories(offers);
}
// Get all categories
@ -187,11 +196,16 @@ export class RagfairController
/**
* Add Required offers to offers result
* @param searchRequest Client search request data
* @param assorts
* @param assorts
* @param pmcProfile Player profile
* @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);
for (const requiredOffer of requiredOffers)
@ -214,7 +228,7 @@ export class RagfairController
for (const offer of offers)
{
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 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
offer.buyRestrictionCurrent = profile.traderPurchases[offer.user.id][assortId]
? profile.traderPurchases[offer.user.id][assortId].count
: assortData.upd.BuyRestrictionCurrent;
offer.buyRestrictionCurrent = profile.traderPurchases[offer.user.id][assortId] ?
profile.traderPurchases[offer.user.id][assortId].count :
assortData.upd.BuyRestrictionCurrent;
offer.buyRestrictionMax = assortData.upd.BuyRestrictionMax;
}
@ -258,10 +272,15 @@ export class RagfairController
const firstItem = offer.items[0];
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)
{
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;
}
@ -292,7 +311,7 @@ export class RagfairController
/**
* Called when creating an offer on flea, fills values in top right corner
* @param getPriceRequest
* @param getPriceRequest
* @returns min/avg/max values for an item based on flea offers available
*/
public getItemMinAvgMaxFleaPriceValues(getPriceRequest: IGetMarketPriceRequestData): IGetItemPriceResult
@ -301,7 +320,7 @@ export class RagfairController
let offers = this.ragfairOfferService.getOffersOfType(getPriceRequest.templateId);
// 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);
const min = offers[0].requirementsCost; // Get first item from array as its pre-sorted
@ -310,10 +329,11 @@ export class RagfairController
return {
avg: (min + max) / 2,
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;
@ -327,7 +347,7 @@ export class RagfairController
return {
avg: tplPrice,
min: tplPrice,
max: tplPrice
max: tplPrice,
};
}
}
@ -339,7 +359,11 @@ export class RagfairController
* @param sessionID Session id
* @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);
@ -351,7 +375,11 @@ export class RagfairController
// Get an array of items from player inventory to list on flea
const getItemsFromInventoryErrorMessage = "";
const itemsInInventoryToList = this.getItemsToListOnFleaFromInventory(pmcData, offerRequest.items, getItemsFromInventoryErrorMessage);
const itemsInInventoryToList = this.getItemsToListOnFleaFromInventory(
pmcData,
offerRequest.items,
getItemsFromInventoryErrorMessage,
);
if (!itemsInInventoryToList)
{
this.httpResponse.appendErrorToOutput(output, getItemsFromInventoryErrorMessage);
@ -360,32 +388,52 @@ export class RagfairController
// Checks are done, create the offer
const playerListedPriceInRub = this.calculateRequirementsPriceInRub(offerRequest.requirements);
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 qualityMultiplier = this.itemHelper.getItemQualityModifier(rootItem);
const averageOfferPrice = this.ragfairPriceService.getFleaPriceForItem(rootItem._tpl) * rootItem.upd.StackObjectsCount * qualityMultiplier;
const itemStackCount = (offerRequest.sellInOnePiece)
? 1
: rootItem.upd.StackObjectsCount;
const averageOfferPrice = this.ragfairPriceService.getFleaPriceForItem(rootItem._tpl) *
rootItem.upd.StackObjectsCount * qualityMultiplier;
const itemStackCount = (offerRequest.sellInOnePiece) ?
1 :
rootItem.upd.StackObjectsCount;
// Get averaged price of a single item being listed
const averageSingleItemPrice = (offerRequest.sellInOnePiece)
? averageOfferPrice / rootItem.upd.StackObjectsCount // Packs are a single offer made of many items
: averageOfferPrice / itemStackCount;
const averageSingleItemPrice = (offerRequest.sellInOnePiece) ?
averageOfferPrice / rootItem.upd.StackObjectsCount // Packs are a single offer made of many items
:
averageOfferPrice / itemStackCount;
// Get averaged price of listing
const averagePlayerListedPriceInRub = (offerRequest.sellInOnePiece)
? playerListedPriceInRub / rootItem.upd.StackObjectsCount
: playerListedPriceInRub;
const averagePlayerListedPriceInRub = (offerRequest.sellInOnePiece) ?
playerListedPriceInRub / rootItem.upd.StackObjectsCount :
playerListedPriceInRub;
// 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);
// Subtract flea market fee from stash
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)
{
return output;
@ -415,24 +463,41 @@ export class RagfairController
* @param output IItemEventRouterResponse
* @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)
const storedClientTaxValue = this.ragfairTaxService.getStoredClientOfferTaxValueById(offerRequest.items[0]);
const tax = storedClientTaxValue
? storedClientTaxValue.fee
: this.ragfairTaxService.calculateTax(rootItem, pmcData, requirementsPriceInRub, itemStackCount, offerRequest.sellInOnePiece);
const tax = storedClientTaxValue ?
storedClientTaxValue.fee :
this.ragfairTaxService.calculateTax(
rootItem,
pmcData,
requirementsPriceInRub,
itemStackCount,
offerRequest.sellInOnePiece,
);
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]);
const buyTradeRequest = this.createBuyTradeRequestObject("RUB", tax);
output = this.paymentService.payMoney(pmcData, buyTradeRequest, sessionID, output);
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;
}
@ -454,7 +519,7 @@ export class RagfairController
return false;
}
if (!offerRequest.requirements)
{
errorMessage = this.localisationService.getText("ragfair-unable_to_place_offer_with_no_requirements");
@ -462,13 +527,13 @@ export class RagfairController
return false;
}
return true;
}
/**
* Get the handbook price in roubles for the items being listed
* @param requirements
* @param requirements
* @returns Rouble price
*/
protected calculateRequirementsPriceInRub(requirements: Requirement[]): number
@ -477,37 +542,44 @@ export class RagfairController
for (const item of requirements)
{
const requestedItemTpl = item._tpl;
if (this.paymentHelper.isMoneyTpl(requestedItemTpl))
{
requirementsPriceInRub += this.handbookHelper.inRUB(item.count, requestedItemTpl);
}
else
{
requirementsPriceInRub += this.ragfairPriceService.getDynamicPriceForItem(requestedItemTpl) * item.count;
requirementsPriceInRub += this.ragfairPriceService.getDynamicPriceForItem(requestedItemTpl) *
item.count;
}
}
return requirementsPriceInRub;
}
/**
* 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 itemIdsFromFleaOfferRequest Ids from request
* @param errorMessage if item is not found, add error message to this parameter
* @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 = [];
// Count how many items are being sold and multiply the requested amount accordingly
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)
{
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);
return null;
@ -528,28 +600,34 @@ export class RagfairController
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 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 {
_id: item._id,
_tpl: item._tpl,
parentId: (isChild) ? item.parentId : "hideout",
slotId: (isChild) ? item.slotId : "hideout",
upd: item.upd
parentId: isChild ? item.parentId : "hideout",
slotId: isChild ? item.slotId : "hideout",
upd: item.upd,
};
});
const formattedRequirements: IBarterScheme[] = requirements.map(item =>
const formattedRequirements: IBarterScheme[] = requirements.map((item) =>
{
return {
_tpl: item._tpl,
count: item.count,
onlyFunctional: item.onlyFunctional
onlyFunctional: item.onlyFunctional,
};
});
@ -559,7 +637,7 @@ export class RagfairController
formattedItems,
formattedRequirements,
loyalLevel,
sellInOnePiece
sellInOnePiece,
);
}
@ -586,21 +664,32 @@ export class RagfairController
const offers = pmcData.RagfairInfo.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 = [];
}
const index = offers.findIndex(offer => offer._id === offerId);
const index = offers.findIndex((offer) => offer._id === offerId);
if (index === -1)
{
this.logger.error(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"));
this.logger.error(
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());
if (differenceInSeconds > this.ragfairConfig.sell.expireSeconds)// expireSeconds Default is 71 seconds
const differenceInSeconds = offers[index].endTime - this.timeUtil.getTimestamp();
if (differenceInSeconds > this.ragfairConfig.sell.expireSeconds)
{
// expireSeconds Default is 71 seconds
const newEndTime = this.ragfairConfig.sell.expireSeconds + this.timeUtil.getTimestamp();
offers[index].endTime = Math.round(newEndTime);
}
@ -613,26 +702,42 @@ export class RagfairController
let output = this.eventOutputHolder.getOutput(sessionID);
const pmcData = this.saveServer.getProfile(sessionID).characters.pmc;
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;
if (index === -1)
{
this.logger.warning(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"));
this.logger.warning(
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
if (this.ragfairConfig.sell.fees)
{
const 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 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);
output = this.paymentService.payMoney(pmcData, request, sessionID, output);
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 {
tid: "ragfair",
Action: "TradingConfirm",
// eslint-disable-next-line @typescript-eslint/naming-convention
scheme_items: [
{
id: this.paymentHelper.getCurrency(currency),
count: Math.round(value)
}
count: Math.round(value),
},
],
type: "",
// eslint-disable-next-line @typescript-eslint/naming-convention
item_id: "",
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("PaymentService") protected paymentService: PaymentService,
@inject("RepairHelper") protected repairHelper: RepairHelper,
@inject("RepairService") protected repairService: RepairService
@inject("RepairService") protected repairService: RepairService,
)
{ }
{}
/**
* Handle TraderRepair event
@ -39,16 +39,27 @@ export class RepairController
* @param pmcData player profile
* @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);
// find the item to repair
for (const repairItem of body.repairItems)
{
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)
{
@ -78,7 +89,13 @@ export class RepairController
const output = this.eventOutputHolder.getOutput(sessionID);
// 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);
@ -90,4 +107,4 @@ export class RepairController
return output;
}
}
}

View File

@ -7,7 +7,11 @@ import { RagfairServerHelper } from "@spt-aki/helpers/RagfairServerHelper";
import { RepeatableQuestHelper } from "@spt-aki/helpers/RepeatableQuestHelper";
import { IEmptyRequestData } from "@spt-aki/models/eft/common/IEmptyRequestData";
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 { IRepeatableQuestChangeRequest } from "@spt-aki/models/eft/quests/IRepeatableQuestChangeRequest";
import { ConfigTypes } from "@spt-aki/models/enums/ConfigTypes";
@ -50,13 +54,12 @@ export class RepeatableQuestController
@inject("RepeatableQuestGenerator") protected repeatableQuestGenerator: RepeatableQuestGenerator,
@inject("RepeatableQuestHelper") protected repeatableQuestHelper: RepeatableQuestHelper,
@inject("QuestHelper") protected questHelper: QuestHelper,
@inject("ConfigServer") protected configServer: ConfigServer
@inject("ConfigServer") protected configServer: ConfigServer,
)
{
this.questConfig = this.configServer.getConfig(ConfigTypes.QUEST);
}
/**
* Handle client/repeatalbeQuests/activityPeriods
* 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
* are still valid. This ischecked by endTime persisted in profile accordning to the resetTime configured for each repeatable kind (daily, weekly)
* in QuestCondig.js
* are still valid. This is checked by endTime persisted in profile according to the resetTime configured for each repeatable kind (daily, weekly)
* in QuestConfig.js
*
* 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
@ -78,26 +81,28 @@ 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)
* The new quests generated are again persisted in profile.RepeatableQuests
*
*
* @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[]
{
const returnData: Array<IPmcDataRepeatableQuest> = [];
const pmcData = this.profileHelper.getPmcProfile(sessionID);
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
for (const repeatableConfig of this.questConfig.repeatableQuests)
{
// get daily/weekly data from profile, add empty object if missing
const currentRepeatableQuestType = this.getRepeatableQuestSubTypeFromProfile(repeatableConfig, pmcData);
if (repeatableConfig.side === "Pmc"
&& pmcData.Info.Level >= repeatableConfig.minPlayerLevel
|| repeatableConfig.side === "Scav" && scavQuestUnlocked)
if (
repeatableConfig.side === "Pmc" &&
pmcData.Info.Level >= repeatableConfig.minPlayerLevel ||
repeatableConfig.side === "Scav" && scavQuestUnlocked
)
{
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)
// and remove them from the PMC's Quests and RepeatableQuests[i].activeQuests
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)
{
// 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[0].status === QuestStatus.AvailableForFinish)
{
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;
}
}
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.activeQuests = questsToKeep;
@ -144,12 +151,14 @@ export class RepeatableQuestController
pmcData.Info.Level,
pmcData.TradersInfo,
questTypePool,
repeatableConfig
repeatableConfig,
);
lifeline++;
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;
}
}
@ -174,7 +183,7 @@ export class RepeatableQuestController
{
currentRepeatableQuestType.changeRequirement[quest._id] = {
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,
activeQuests: currentRepeatableQuestType.activeQuests,
inactiveQuests: currentRepeatableQuestType.inactiveQuests,
changeRequirement: currentRepeatableQuestType.changeRequirement
changeRequirement: currentRepeatableQuestType.changeRequirement,
});
}
@ -199,10 +208,15 @@ export class RepeatableQuestController
*/
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)
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;
@ -214,10 +228,13 @@ export class RepeatableQuestController
* @param pmcData Profile to search
* @returns IPmcDataRepeatableQuest
*/
protected getRepeatableQuestSubTypeFromProfile(repeatableConfig: IRepeatableQuestConfig, pmcData: IPmcData): IPmcDataRepeatableQuest
protected getRepeatableQuestSubTypeFromProfile(
repeatableConfig: IRepeatableQuestConfig,
pmcData: IPmcData,
): IPmcDataRepeatableQuest
{
// 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)
{
repeatableQuestDetails = {
@ -226,7 +243,7 @@ export class RepeatableQuestController
activeQuests: [],
inactiveQuests: [],
endTime: 0,
changeRequirement: {}
changeRequirement: {},
};
// Add base object that holds repeatable data to profile
@ -297,16 +314,16 @@ export class RepeatableQuestController
// Target is boss
if (probabilityObject.data.isBoss)
{
questPool.pool.Elimination.targets[probabilityObject.key] = { locations: ["any"] };
questPool.pool.Elimination.targets[probabilityObject.key] = {locations: ["any"]};
}
else
{
const possibleLocations = Object.keys(repeatableConfig.locations);
// Set possible locations for elimination task, ift arget is savage, exclude labs from locations
questPool.pool.Elimination.targets[probabilityObject.key] = (probabilityObject.key === "Savage")
? { locations: possibleLocations.filter(x => x !== "laboratory")}
: { locations: possibleLocations };
// Set possible locations for elimination task, if target is savage, exclude labs from locations
questPool.pool.Elimination.targets[probabilityObject.key] = (probabilityObject.key === "Savage") ?
{locations: possibleLocations.filter((x) => x !== "laboratory")} :
{locations: possibleLocations};
}
}
@ -319,15 +336,15 @@ export class RepeatableQuestController
types: repeatableConfig.types.slice(),
pool: {
Exploration: {
locations: {}
locations: {},
},
Elimination: {
targets: {}
targets: {},
},
Pickup: {
locations: {}
}
}
locations: {},
},
},
};
}
@ -355,7 +372,11 @@ export class RepeatableQuestController
/**
* 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 changeRequirement: IChangeRequirement;
@ -367,24 +388,29 @@ export class RepeatableQuestController
for (const currentRepeatablePool of pmcData.RepeatableQuests)
{
// 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)
{
continue;
}
// Save for later standing loss calculation
replacedQuestTraderId = questToReplace.traderId;
// 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
changeRequirement = this.jsonUtil.clone(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 newRepeatableQuest = this.attemptToGenerateRepeatableQuest(pmcData, questTypePool, repeatableConfig);
if (newRepeatableQuest)
@ -394,16 +420,19 @@ export class RepeatableQuestController
currentRepeatablePool.activeQuests.push(newRepeatableQuest);
currentRepeatablePool.changeRequirement[newRepeatableQuest._id] = {
changeCost: newRepeatableQuest.changeCost,
changeStandingCost: this.randomUtil.getArrayValue([0, 0.01])
changeStandingCost: this.randomUtil.getArrayValue([0, 0.01]),
};
const fullProfile = this.profileHelper.getFullProfile(sessionID);
// Find quest we're replacing in pmc profile quests array and remove it
this.questHelper.findAndRemoveQuestFromArrayIfExists(questToReplace._id, pmcData.Quests);
// 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
@ -442,7 +471,11 @@ export class RepeatableQuestController
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 attemptsToGenerateQuest = 0;
@ -452,16 +485,17 @@ export class RepeatableQuestController
pmcData.Info.Level,
pmcData.TradersInfo,
questTypePool,
repeatableConfig
repeatableConfig,
);
attemptsToGenerateQuest++;
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;
}
}
return newRepeatableQuest;
}
}

View File

@ -29,7 +29,7 @@ import { HttpResponseUtil } from "@spt-aki/utils/HttpResponseUtil";
import { JsonUtil } from "@spt-aki/utils/JsonUtil";
@injectable()
class TradeController
export class TradeController
{
protected ragfairConfig: IRagfairConfig;
protected traderConfig: ITraderConfig;
@ -46,7 +46,7 @@ class TradeController
@inject("HttpResponseUtil") protected httpResponse: HttpResponseUtil,
@inject("LocalisationService") protected localisationService: LocalisationService,
@inject("RagfairPriceService") protected ragfairPriceService: RagfairPriceService,
@inject("ConfigServer") protected configServer: ConfigServer
@inject("ConfigServer") protected configServer: ConfigServer,
)
{
this.ragfairConfig = this.configServer.getConfig(ConfigTypes.RAGFAIR);
@ -54,34 +54,56 @@ class TradeController
}
/** 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);
}
/** 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);
for (const offer of body.offers)
{
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)
{
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);
}
// 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;
}
@ -91,17 +113,21 @@ class TradeController
Action: "TradingConfirm",
type: "buy_from_trader",
tid: (fleaOffer.user.memberType !== MemberCategory.TRADER) ? "ragfair" : fleaOffer.user.id,
// eslint-disable-next-line @typescript-eslint/naming-convention
item_id: fleaOffer.root,
count: offer.count,
// eslint-disable-next-line @typescript-eslint/naming-convention
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
output = this.confirmTradingInternal(pmcData, buyData, sessionID, this.ragfairConfig.dynamic.purchasesAreFoundInRaid, fleaOffer.items[0].upd);
// confirmTrading() must occur prior to removing the offer stack, otherwise item inside offer doesn't exist
// for confirmTrading() to use
output = this.confirmTradingInternal(
pmcData,
buyData,
sessionID,
this.ragfairConfig.dynamic.purchasesAreFoundInRaid,
fleaOffer.items[0].upd,
);
if (fleaOffer.user.memberType !== MemberCategory.TRADER)
{
// remove player item offer stack
@ -113,30 +139,42 @@ class TradeController
}
/** 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;
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);
}
/**
* 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
* @param sessionId Session id
* @param profileWithItemsToSell Profile with items to be sold to trader
* @param profileWithItemsToSell Profile with items to be sold to trader
* @param profileThatGetsMoney Profile that gets the money after selling items
* @param trader Trader to sell items to
* @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();
// TODO, apply trader sell bonuses?
// TODO: apply trader sell bonuses?
const traderDetails = this.traderHelper.getTrader(trader, sessionId);
// Prep request object
@ -145,16 +183,23 @@ class TradeController
type: "sell_to_trader",
tid: trader,
price: 0,
items: []
items: [],
};
// 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
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)
{
// 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
// 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
* @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);
@ -180,9 +230,12 @@ class TradeController
for (const itemToSell of itemWithChildren)
{
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;
}
@ -193,7 +246,13 @@ class TradeController
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
if (body.type === "buy_from_trader")
@ -212,6 +271,3 @@ class TradeController
return null;
}
}
export { TradeController };

View File

@ -23,12 +23,13 @@ export class TraderController
@inject("ProfileHelper") protected profileHelper: ProfileHelper,
@inject("TraderHelper") protected traderHelper: TraderHelper,
@inject("TraderAssortService") protected traderAssortService: TraderAssortService,
@inject("TraderPurchasePersisterService") protected traderPurchasePersisterService: TraderPurchasePersisterService,
@inject("TraderPurchasePersisterService") protected traderPurchasePersisterService:
TraderPurchasePersisterService,
@inject("FenceService") protected fenceService: FenceService,
@inject("FenceBaseAssortGenerator") protected fenceBaseAssortGenerator: FenceBaseAssortGenerator,
@inject("JsonUtil") protected jsonUtil: JsonUtil
@inject("JsonUtil") protected jsonUtil: JsonUtil,
)
{ }
{}
/**
* Runs when onLoad event is fired
@ -93,7 +94,7 @@ export class TraderController
}
const trader = this.databaseServer.getTables().traders[traderId];
// trader needs to be refreshed
if (this.traderAssortHelper.traderAssortsHaveExpired(traderId))
{
@ -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));
}
/**
@ -167,4 +168,4 @@ export class TraderController
{
return this.traderAssortHelper.getAssort(sessionId, traderId);
}
}
}

View File

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

View File

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