Formatting for controller classes.
This commit is contained in:
parent
5fa8803f8c
commit
87bb07cfd9
@ -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"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
* 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,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
@ -166,14 +170,14 @@ export class DialogueController
|
||||
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;
|
||||
}
|
||||
|
||||
@ -332,7 +333,6 @@ export class DialogueController
|
||||
if (!dialog)
|
||||
{
|
||||
this.logger.error(`No dialog in profile: ${sessionId} found with id: ${dialogueId}`);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@ -345,12 +345,11 @@ export class DialogueController
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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"))
|
||||
{
|
||||
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")
|
||||
{
|
||||
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()))
|
||||
{
|
||||
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")
|
||||
{
|
||||
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")
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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);
|
||||
@ -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}`);
|
||||
@ -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,7 +520,7 @@ export class GameController
|
||||
|
||||
/**
|
||||
* When player logs in, iterate over all active effects and reduce timer
|
||||
* TODO - add body part HP regen
|
||||
* // TODO: Add body part HP regeneration
|
||||
* @param pmcProfile
|
||||
*/
|
||||
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;
|
||||
@ -569,7 +578,7 @@ export class GameController
|
||||
// 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)
|
||||
{
|
||||
@ -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,7 +703,10 @@ 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;
|
||||
@ -700,7 +717,9 @@ export class GameController
|
||||
|
||||
// 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),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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)
|
||||
|
@ -8,9 +8,9 @@ export class HandbookController
|
||||
{
|
||||
constructor(
|
||||
@inject("DatabaseServer") protected databaseServer: DatabaseServer,
|
||||
@inject("HandbookHelper") protected handbookHelper: HandbookHelper
|
||||
@inject("HandbookHelper") protected handbookHelper: HandbookHelper,
|
||||
)
|
||||
{ }
|
||||
{}
|
||||
|
||||
public load(): void
|
||||
{
|
||||
|
@ -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
|
||||
{
|
||||
@ -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
|
||||
@ -207,7 +220,6 @@ export class HealthController
|
||||
* @param info Request data
|
||||
* @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
|
||||
|
@ -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,17 +208,31 @@ 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);
|
||||
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,17 +398,22 @@ 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);
|
||||
@ -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,10 +644,12 @@ export class HideoutController
|
||||
}
|
||||
}
|
||||
|
||||
const recipe = this.databaseServer.getTables().hideout.scavcase.find(r => r._id === body.recipeId);
|
||||
const recipe = this.databaseServer.getTables().hideout.scavcase.find((r) => r._id === body.recipeId);
|
||||
if (!recipe)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
@ -549,7 +658,11 @@ export class HideoutController
|
||||
// - 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;
|
||||
@ -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,13 +784,12 @@ 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",
|
||||
@ -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,7 +1152,10 @@ 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;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
* 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);
|
||||
@ -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,7 +453,10 @@ 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)
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
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));
|
||||
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);
|
||||
|
||||
@ -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)
|
||||
@ -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;
|
||||
|
@ -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
|
||||
* 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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
@ -89,16 +89,22 @@ export class LocationController
|
||||
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,
|
||||
};
|
||||
}
|
||||
}
|
@ -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,12 +144,14 @@ 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
|
||||
@ -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),
|
||||
);
|
||||
}
|
||||
|
||||
@ -296,7 +295,9 @@ export class MatchController
|
||||
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]}`,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -309,8 +310,8 @@ export class MatchController
|
||||
{
|
||||
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];
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -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,16 +179,24 @@ 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);
|
||||
}
|
||||
|
||||
@ -171,7 +206,7 @@ export class PresetBuildController
|
||||
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);
|
||||
@ -192,7 +227,6 @@ export class PresetBuildController
|
||||
this.logger.error(`Unable to delete preset, cannot find ${id} in weapon or equipment presets`);
|
||||
}
|
||||
|
||||
|
||||
return this.eventOutputHolder.getOutput(sessionID);
|
||||
}
|
||||
}
|
@ -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
|
||||
{
|
||||
|
@ -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,7 +242,11 @@ 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)
|
||||
{
|
||||
@ -238,8 +254,17 @@ export class ProfileController
|
||||
|
||||
// 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),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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,
|
||||
},
|
||||
}];
|
||||
}
|
||||
}
|
@ -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`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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,7 +354,11 @@ 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);
|
||||
|
||||
@ -333,13 +369,21 @@ export class QuestController
|
||||
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,7 +472,7 @@ 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}`);
|
||||
@ -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);
|
||||
@ -457,7 +532,7 @@ 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;
|
||||
}
|
||||
@ -714,11 +829,16 @@ export class QuestController
|
||||
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)
|
||||
{
|
||||
@ -807,7 +958,7 @@ export class QuestController
|
||||
pmcData.BackendCounters[conditionId] = {
|
||||
id: conditionId,
|
||||
qid: questId,
|
||||
value: counterValue
|
||||
value: counterValue,
|
||||
};
|
||||
}
|
||||
}
|
@ -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,8 +118,8 @@ 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;
|
||||
@ -135,7 +139,7 @@ export class RagfairController
|
||||
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,7 +174,7 @@ 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>
|
||||
{
|
||||
@ -191,7 +200,12 @@ export class RagfairController
|
||||
* @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;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -484,7 +549,8 @@ export class RagfairController
|
||||
}
|
||||
else
|
||||
{
|
||||
requirementsPriceInRub += this.ragfairPriceService.getDynamicPriceForItem(requestedItemTpl) * item.count;
|
||||
requirementsPriceInRub += this.ragfairPriceService.getDynamicPriceForItem(requestedItemTpl) *
|
||||
item.count;
|
||||
}
|
||||
}
|
||||
|
||||
@ -492,22 +558,28 @@ export class RagfairController
|
||||
}
|
||||
|
||||
/**
|
||||
* Using item ids from flea offer request, find corrispnding items from player inventory and return as array
|
||||
* Using item ids from flea offer request, find corresponding items from player inventory and return as array
|
||||
* @param pmcData Player profile
|
||||
* @param 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,
|
||||
};
|
||||
}
|
||||
}
|
@ -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,7 +39,11 @@ 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);
|
||||
|
||||
@ -48,7 +52,14 @@ export class RepairController
|
||||
{
|
||||
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);
|
||||
|
||||
|
@ -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,16 +81,16 @@ export class RepeatableQuestController
|
||||
* (if the are on "Succeed" but not "Completed" we keep them, to allow the player to complete them and get the rewards)
|
||||
* 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)
|
||||
@ -95,9 +98,11 @@ export class RepeatableQuestController
|
||||
// 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,7 +388,7 @@ 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;
|
||||
@ -377,14 +398,19 @@ export class RepeatableQuestController
|
||||
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,7 +420,7 @@ 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);
|
||||
@ -403,7 +429,10 @@ export class RepeatableQuestController
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -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,13 +54,21 @@ 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);
|
||||
|
||||
@ -69,19 +77,33 @@ class TradeController
|
||||
const fleaOffer = this.ragfairServer.getOffer(offer.id);
|
||||
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,19 +139,26 @@ 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
|
||||
@ -133,10 +166,15 @@ class TradeController
|
||||
* @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 };
|
||||
|
||||
|
@ -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
|
||||
@ -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));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user