Formatting for controller classes.
This commit is contained in:
parent
5fa8803f8c
commit
87bb07cfd9
@ -21,12 +21,63 @@
|
|||||||
"source.fixAll.eslint",
|
"source.fixAll.eslint",
|
||||||
"source.organizeImports.biome"
|
"source.organizeImports.biome"
|
||||||
],
|
],
|
||||||
|
"cSpell.language": "en-GB",
|
||||||
"cSpell.words": [
|
"cSpell.words": [
|
||||||
|
"armor",
|
||||||
|
"asonline",
|
||||||
|
"behaviour",
|
||||||
|
"biomejs",
|
||||||
|
"botreload",
|
||||||
|
"currexp",
|
||||||
|
"currlvl",
|
||||||
|
"dbaeumer",
|
||||||
"deathmatch",
|
"deathmatch",
|
||||||
|
"dprint",
|
||||||
|
"edgeofdarkness",
|
||||||
|
"fulfill",
|
||||||
"gethideout",
|
"gethideout",
|
||||||
|
"gifter",
|
||||||
|
"hpresource",
|
||||||
|
"inraid",
|
||||||
|
"isvalid",
|
||||||
|
"leftbehind",
|
||||||
|
"leveled",
|
||||||
|
"loadout",
|
||||||
|
"maxlvl",
|
||||||
|
"medkit",
|
||||||
|
"MEDSTATION",
|
||||||
|
"nextlvl",
|
||||||
|
"offraid",
|
||||||
|
"peacefullzryachiyevent",
|
||||||
|
"preparetoescape",
|
||||||
|
"prevexp",
|
||||||
"profileid",
|
"profileid",
|
||||||
|
"Protobuf",
|
||||||
|
"pscav",
|
||||||
|
"Ragfair",
|
||||||
|
"Regen",
|
||||||
"requestid",
|
"requestid",
|
||||||
"scavcase"
|
"sanitise",
|
||||||
|
"Sanitised",
|
||||||
|
"scav",
|
||||||
|
"scavcase",
|
||||||
|
"scavs",
|
||||||
|
"Spawnpoint",
|
||||||
|
"spawnpoints",
|
||||||
|
"sptbear",
|
||||||
|
"sptdeveloper",
|
||||||
|
"spteasystart",
|
||||||
|
"sptusec",
|
||||||
|
"sptzerotohero",
|
||||||
|
"stackcount",
|
||||||
|
"statustimer",
|
||||||
|
"Tarkov",
|
||||||
|
"toggleable",
|
||||||
|
"tooshort",
|
||||||
|
"unrestartable",
|
||||||
|
"usec",
|
||||||
|
"userbuilds",
|
||||||
|
"Wishlist"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -43,7 +43,7 @@ export class BotController
|
|||||||
@inject("ProfileHelper") protected profileHelper: ProfileHelper,
|
@inject("ProfileHelper") protected profileHelper: ProfileHelper,
|
||||||
@inject("ConfigServer") protected configServer: ConfigServer,
|
@inject("ConfigServer") protected configServer: ConfigServer,
|
||||||
@inject("ApplicationContext") protected applicationContext: ApplicationContext,
|
@inject("ApplicationContext") protected applicationContext: ApplicationContext,
|
||||||
@inject("JsonUtil") protected jsonUtil: JsonUtil
|
@inject("JsonUtil") protected jsonUtil: JsonUtil,
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
this.botConfig = this.configServer.getConfig(ConfigTypes.BOT);
|
this.botConfig = this.configServer.getConfig(ConfigTypes.BOT);
|
||||||
@ -51,29 +51,29 @@ export class BotController
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the number of bot loadout varieties to be generated
|
* Return the number of bot load-out varieties to be generated
|
||||||
* @param type bot Type we want the loadout gen count for
|
* @param type bot Type we want the load-out gen count for
|
||||||
* @returns number of bots to generate
|
* @returns number of bots to generate
|
||||||
*/
|
*/
|
||||||
public getBotPresetGenerationLimit(type: string): number
|
public getBotPresetGenerationLimit(type: string): number
|
||||||
{
|
{
|
||||||
const value = this.botConfig.presetBatch[(type === "assaultGroup")
|
const value = this.botConfig.presetBatch[
|
||||||
? "assault"
|
(type === "assaultGroup") ?
|
||||||
: type];
|
"assault" :
|
||||||
|
type
|
||||||
|
];
|
||||||
|
|
||||||
if (!value)
|
if (!value)
|
||||||
{
|
{
|
||||||
this.logger.warning(`No value found for bot type ${type}, defaulting to 30`);
|
this.logger.warning(`No value found for bot type ${type}, defaulting to 30`);
|
||||||
|
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle singleplayer/settings/bot/difficulty
|
* Handle singleplayer/settings/bot/difficulty
|
||||||
* Get the core.json difficulty settings from database\bots
|
* Get the core.json difficulty settings from database/bots
|
||||||
* @returns IBotCore
|
* @returns IBotCore
|
||||||
*/
|
*/
|
||||||
public getBotCoreDifficulty(): IBotCore
|
public getBotCoreDifficulty(): IBotCore
|
||||||
@ -90,10 +90,14 @@ export class BotController
|
|||||||
*/
|
*/
|
||||||
public getBotDifficulty(type: string, difficulty: string): Difficulty
|
public getBotDifficulty(type: string, difficulty: string): Difficulty
|
||||||
{
|
{
|
||||||
const raidConfig = this.applicationContext.getLatestValue(ContextVariableType.RAID_CONFIGURATION)?.getValue<IGetRaidConfigurationRequestData>();
|
const raidConfig = this.applicationContext.getLatestValue(ContextVariableType.RAID_CONFIGURATION)?.getValue<
|
||||||
|
IGetRaidConfigurationRequestData
|
||||||
|
>();
|
||||||
if (!raidConfig)
|
if (!raidConfig)
|
||||||
{
|
{
|
||||||
this.logger.error(this.localisationService.getText("bot-missing_application_context", "RAID_CONFIGURATION"));
|
this.logger.error(
|
||||||
|
this.localisationService.getText("bot-missing_application_context", "RAID_CONFIGURATION"),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check value chosen in pre-raid difficulty dropdown
|
// Check value chosen in pre-raid difficulty dropdown
|
||||||
@ -101,7 +105,9 @@ export class BotController
|
|||||||
const botDifficultyDropDownValue = raidConfig.wavesSettings.botDifficulty.toLowerCase();
|
const botDifficultyDropDownValue = raidConfig.wavesSettings.botDifficulty.toLowerCase();
|
||||||
if (botDifficultyDropDownValue !== "asonline")
|
if (botDifficultyDropDownValue !== "asonline")
|
||||||
{
|
{
|
||||||
difficulty = this.botDifficultyHelper.convertBotDifficultyDropdownToBotDifficulty(botDifficultyDropDownValue);
|
difficulty = this.botDifficultyHelper.convertBotDifficultyDropdownToBotDifficulty(
|
||||||
|
botDifficultyDropDownValue,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let difficultySettings: Difficulty;
|
let difficultySettings: Difficulty;
|
||||||
@ -109,19 +115,31 @@ export class BotController
|
|||||||
switch (lowercasedBotType)
|
switch (lowercasedBotType)
|
||||||
{
|
{
|
||||||
case this.pmcConfig.bearType.toLowerCase():
|
case this.pmcConfig.bearType.toLowerCase():
|
||||||
difficultySettings = this.botDifficultyHelper.getPmcDifficultySettings("bear", difficulty, this.pmcConfig.usecType, this.pmcConfig.bearType);
|
difficultySettings = this.botDifficultyHelper.getPmcDifficultySettings(
|
||||||
|
"bear",
|
||||||
|
difficulty,
|
||||||
|
this.pmcConfig.usecType,
|
||||||
|
this.pmcConfig.bearType,
|
||||||
|
);
|
||||||
break;
|
break;
|
||||||
case this.pmcConfig.usecType.toLowerCase():
|
case this.pmcConfig.usecType.toLowerCase():
|
||||||
difficultySettings = this.botDifficultyHelper.getPmcDifficultySettings("usec", difficulty, this.pmcConfig.usecType, this.pmcConfig.bearType);
|
difficultySettings = this.botDifficultyHelper.getPmcDifficultySettings(
|
||||||
|
"usec",
|
||||||
|
difficulty,
|
||||||
|
this.pmcConfig.usecType,
|
||||||
|
this.pmcConfig.bearType,
|
||||||
|
);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
difficultySettings = this.botDifficultyHelper.getBotDifficultySettings(type, difficulty);
|
difficultySettings = this.botDifficultyHelper.getBotDifficultySettings(type, difficulty);
|
||||||
// Don't add pmcs to event enemies (e.g. gifter/peacefullzryachiyevent)
|
// Don't add PMCs to event enemies (e.g. gifter/peacefullzryachiyevent)
|
||||||
if (!this.botConfig.botsToNotAddPMCsAsEnemiesTo.includes(type.toLowerCase()))
|
if (!this.botConfig.botsToNotAddPMCsAsEnemiesTo.includes(type.toLowerCase()))
|
||||||
{
|
{
|
||||||
this.botHelper.addBotToEnemyList(difficultySettings, [this.pmcConfig.bearType, this.pmcConfig.usecType], lowercasedBotType);
|
this.botHelper.addBotToEnemyList(difficultySettings, [
|
||||||
|
this.pmcConfig.bearType,
|
||||||
|
this.pmcConfig.usecType,
|
||||||
|
], lowercasedBotType);
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -149,7 +167,7 @@ export class BotController
|
|||||||
botRelativeLevelDeltaMax: this.pmcConfig.botRelativeLevelDeltaMax,
|
botRelativeLevelDeltaMax: this.pmcConfig.botRelativeLevelDeltaMax,
|
||||||
botCountToGenerate: this.botConfig.presetBatch[condition.Role],
|
botCountToGenerate: this.botConfig.presetBatch[condition.Role],
|
||||||
botDifficulty: condition.Difficulty,
|
botDifficulty: condition.Difficulty,
|
||||||
isPlayerScav: false
|
isPlayerScav: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Event bots need special actions to occur, set data up for them
|
// Event bots need special actions to occur, set data up for them
|
||||||
@ -158,7 +176,9 @@ export class BotController
|
|||||||
{
|
{
|
||||||
// Add eventRole data + reassign role property to be base type
|
// Add eventRole data + reassign role property to be base type
|
||||||
botGenerationDetails.eventRole = condition.Role;
|
botGenerationDetails.eventRole = condition.Role;
|
||||||
botGenerationDetails.role = this.seasonalEventService.getBaseRoleForEventBot(botGenerationDetails.eventRole);
|
botGenerationDetails.role = this.seasonalEventService.getBaseRoleForEventBot(
|
||||||
|
botGenerationDetails.eventRole,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Custom map waves can have spt roles in them
|
// Custom map waves can have spt roles in them
|
||||||
@ -171,10 +191,10 @@ export class BotController
|
|||||||
|
|
||||||
// Loop over and make x bots for this condition
|
// Loop over and make x bots for this condition
|
||||||
let cacheKey = "";
|
let cacheKey = "";
|
||||||
for (let i = 0; i < botGenerationDetails.botCountToGenerate; i ++)
|
for (let i = 0; i < botGenerationDetails.botCountToGenerate; i++)
|
||||||
{
|
{
|
||||||
const details = this.jsonUtil.clone(botGenerationDetails);
|
const details = this.jsonUtil.clone(botGenerationDetails);
|
||||||
const botRole = (isEventBot) ? details.eventRole : details.role;
|
const botRole = isEventBot ? details.eventRole : details.role;
|
||||||
|
|
||||||
// Roll chance to be pmc if type is allowed to be one
|
// Roll chance to be pmc if type is allowed to be one
|
||||||
const botConvertRateMinMax = this.pmcConfig.convertIntoPmcChance[botRole.toLowerCase()];
|
const botConvertRateMinMax = this.pmcConfig.convertIntoPmcChance[botRole.toLowerCase()];
|
||||||
@ -192,6 +212,7 @@ export class BotController
|
|||||||
}
|
}
|
||||||
|
|
||||||
cacheKey = `${botRole}${details.botDifficulty}`;
|
cacheKey = `${botRole}${details.botDifficulty}`;
|
||||||
|
|
||||||
// Check for bot in cache, add if not
|
// Check for bot in cache, add if not
|
||||||
if (!this.botGenerationCacheService.cacheHasBotOfRole(cacheKey))
|
if (!this.botGenerationCacheService.cacheHasBotOfRole(cacheKey))
|
||||||
{
|
{
|
||||||
@ -200,6 +221,7 @@ export class BotController
|
|||||||
this.botGenerationCacheService.storeBots(cacheKey, botsToAddToCache);
|
this.botGenerationCacheService.storeBots(cacheKey, botsToAddToCache);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get bot from cache, add to return array
|
// Get bot from cache, add to return array
|
||||||
const botToReturn = this.botGenerationCacheService.getBot(cacheKey);
|
const botToReturn = this.botGenerationCacheService.getBot(cacheKey);
|
||||||
|
|
||||||
@ -217,13 +239,13 @@ export class BotController
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the difficulty passed in, if its not "asoline", get selected difficulty from config
|
* Get the difficulty passed in, if its not "asonline", get selected difficulty from config
|
||||||
* @param requestedDifficulty
|
* @param requestedDifficulty
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
public getPMCDifficulty(requestedDifficulty: string): string
|
public getPMCDifficulty(requestedDifficulty: string): string
|
||||||
{
|
{
|
||||||
// maybe retrun a random difficulty...
|
// Maybe return a random difficulty...
|
||||||
if (this.pmcConfig.difficulty.toLowerCase() === "asonline")
|
if (this.pmcConfig.difficulty.toLowerCase() === "asonline")
|
||||||
{
|
{
|
||||||
return requestedDifficulty;
|
return requestedDifficulty;
|
||||||
@ -245,20 +267,27 @@ export class BotController
|
|||||||
public getBotCap(): number
|
public getBotCap(): number
|
||||||
{
|
{
|
||||||
const defaultMapCapId = "default";
|
const defaultMapCapId = "default";
|
||||||
const raidConfig = this.applicationContext.getLatestValue(ContextVariableType.RAID_CONFIGURATION).getValue<IGetRaidConfigurationRequestData>();
|
const raidConfig = this.applicationContext.getLatestValue(ContextVariableType.RAID_CONFIGURATION).getValue<
|
||||||
|
IGetRaidConfigurationRequestData
|
||||||
|
>();
|
||||||
if (!raidConfig)
|
if (!raidConfig)
|
||||||
{
|
{
|
||||||
this.logger.warning(this.localisationService.getText("bot-missing_saved_match_info"));
|
this.logger.warning(this.localisationService.getText("bot-missing_saved_match_info"));
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapName = (raidConfig)
|
const mapName = raidConfig ?
|
||||||
? raidConfig.location
|
raidConfig.location :
|
||||||
: defaultMapCapId;
|
defaultMapCapId;
|
||||||
|
|
||||||
let botCap = this.botConfig.maxBotCap[mapName.toLowerCase()];
|
let botCap = this.botConfig.maxBotCap[mapName.toLowerCase()];
|
||||||
if (!botCap)
|
if (!botCap)
|
||||||
{
|
{
|
||||||
this.logger.warning(this.localisationService.getText("bot-no_bot_cap_found_for_location", raidConfig.location.toLowerCase()));
|
this.logger.warning(
|
||||||
|
this.localisationService.getText(
|
||||||
|
"bot-no_bot_cap_found_for_location",
|
||||||
|
raidConfig.location.toLowerCase(),
|
||||||
|
),
|
||||||
|
);
|
||||||
botCap = this.botConfig.maxBotCap[defaultMapCapId];
|
botCap = this.botConfig.maxBotCap[defaultMapCapId];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -269,7 +298,7 @@ export class BotController
|
|||||||
{
|
{
|
||||||
return {
|
return {
|
||||||
pmc: this.pmcConfig.pmcType,
|
pmc: this.pmcConfig.pmcType,
|
||||||
assault: this.botConfig.assaultBrainType
|
assault: this.botConfig.assaultBrainType,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,9 +9,9 @@ import { inject, injectable } from "tsyringe";
|
|||||||
export class ClientLogController
|
export class ClientLogController
|
||||||
{
|
{
|
||||||
constructor(
|
constructor(
|
||||||
@inject("WinstonLogger") protected logger: ILogger
|
@inject("WinstonLogger") protected logger: ILogger,
|
||||||
)
|
)
|
||||||
{ }
|
{}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle /singleplayer/log
|
* Handle /singleplayer/log
|
||||||
|
@ -17,7 +17,7 @@ export class CustomizationController
|
|||||||
{
|
{
|
||||||
protected readonly clothingIds = {
|
protected readonly clothingIds = {
|
||||||
lowerParentId: "5cd944d01388ce000a659df9",
|
lowerParentId: "5cd944d01388ce000a659df9",
|
||||||
upperParentId: "5cd944ca1388ce03a44dc2a4"
|
upperParentId: "5cd944ca1388ce03a44dc2a4",
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@ -26,7 +26,7 @@ export class CustomizationController
|
|||||||
@inject("DatabaseServer") protected databaseServer: DatabaseServer,
|
@inject("DatabaseServer") protected databaseServer: DatabaseServer,
|
||||||
@inject("SaveServer") protected saveServer: SaveServer,
|
@inject("SaveServer") protected saveServer: SaveServer,
|
||||||
@inject("LocalisationService") protected localisationService: LocalisationService,
|
@inject("LocalisationService") protected localisationService: LocalisationService,
|
||||||
@inject("ProfileHelper") protected profileHelper: ProfileHelper
|
@inject("ProfileHelper") protected profileHelper: ProfileHelper,
|
||||||
)
|
)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
@ -42,18 +42,22 @@ export class CustomizationController
|
|||||||
const templates = this.databaseServer.getTables().templates.customization;
|
const templates = this.databaseServer.getTables().templates.customization;
|
||||||
const suits = this.databaseServer.getTables().traders[traderID].suits;
|
const suits = this.databaseServer.getTables().traders[traderID].suits;
|
||||||
|
|
||||||
// Get an inner join of clothing from templates.customization and ragmans suits array
|
// Get an inner join of clothing from templates.customization and Ragman's suits array
|
||||||
const matchingSuits = suits.filter(x => x.suiteId in templates);
|
const matchingSuits = suits.filter((x) => x.suiteId in templates);
|
||||||
|
|
||||||
// Return all suits that have a side array containing the players side (usec/bear)
|
// Return all suits that have a side array containing the players side (usec/bear)
|
||||||
return matchingSuits.filter(x => templates[x.suiteId]._props.Side.includes(pmcData.Info.Side));
|
return matchingSuits.filter((x) => templates[x.suiteId]._props.Side.includes(pmcData.Info.Side));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle CustomizationWear event
|
* Handle CustomizationWear event
|
||||||
* Equip one to many clothing items to player
|
* Equip one to many clothing items to player
|
||||||
*/
|
*/
|
||||||
public wearClothing(pmcData: IPmcData, wearClothingRequest: IWearClothingRequestData, sessionID: string): IItemEventRouterResponse
|
public wearClothing(
|
||||||
|
pmcData: IPmcData,
|
||||||
|
wearClothingRequest: IWearClothingRequestData,
|
||||||
|
sessionID: string,
|
||||||
|
): IItemEventRouterResponse
|
||||||
{
|
{
|
||||||
for (const suitId of wearClothingRequest.suites)
|
for (const suitId of wearClothingRequest.suites)
|
||||||
{
|
{
|
||||||
@ -85,7 +89,11 @@ export class CustomizationController
|
|||||||
* @param sessionId Session id
|
* @param sessionId Session id
|
||||||
* @returns IItemEventRouterResponse
|
* @returns IItemEventRouterResponse
|
||||||
*/
|
*/
|
||||||
public buyClothing(pmcData: IPmcData, buyClothingRequest: IBuyClothingRequestData, sessionId: string): IItemEventRouterResponse
|
public buyClothing(
|
||||||
|
pmcData: IPmcData,
|
||||||
|
buyClothingRequest: IBuyClothingRequestData,
|
||||||
|
sessionId: string,
|
||||||
|
): IItemEventRouterResponse
|
||||||
{
|
{
|
||||||
const db = this.databaseServer.getTables();
|
const db = this.databaseServer.getTables();
|
||||||
const output = this.eventOutputHolder.getOutput(sessionId);
|
const output = this.eventOutputHolder.getOutput(sessionId);
|
||||||
@ -93,7 +101,9 @@ export class CustomizationController
|
|||||||
const traderOffer = this.getTraderClothingOffer(sessionId, buyClothingRequest.offer);
|
const traderOffer = this.getTraderClothingOffer(sessionId, buyClothingRequest.offer);
|
||||||
if (!traderOffer)
|
if (!traderOffer)
|
||||||
{
|
{
|
||||||
this.logger.error(this.localisationService.getText("customisation-unable_to_find_suit_by_id", buyClothingRequest.offer));
|
this.logger.error(
|
||||||
|
this.localisationService.getText("customisation-unable_to_find_suit_by_id", buyClothingRequest.offer),
|
||||||
|
);
|
||||||
|
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
@ -102,7 +112,12 @@ export class CustomizationController
|
|||||||
if (this.outfitAlreadyPurchased(suitId, sessionId))
|
if (this.outfitAlreadyPurchased(suitId, sessionId))
|
||||||
{
|
{
|
||||||
const suitDetails = db.templates.customization[suitId];
|
const suitDetails = db.templates.customization[suitId];
|
||||||
this.logger.error(this.localisationService.getText("customisation-item_already_purchased", {itemId: suitDetails._id, itemName: suitDetails._name}));
|
this.logger.error(
|
||||||
|
this.localisationService.getText("customisation-item_already_purchased", {
|
||||||
|
itemId: suitDetails._id,
|
||||||
|
itemName: suitDetails._name,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
@ -118,7 +133,7 @@ export class CustomizationController
|
|||||||
|
|
||||||
protected getTraderClothingOffer(sessionId: string, offerId: string): ISuit
|
protected getTraderClothingOffer(sessionId: string, offerId: string): ISuit
|
||||||
{
|
{
|
||||||
return this.getAllTraderSuits(sessionId).find(x => x._id === offerId);
|
return this.getAllTraderSuits(sessionId).find((x) => x._id === offerId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -139,7 +154,12 @@ export class CustomizationController
|
|||||||
* @param clothingItems Clothing purchased
|
* @param clothingItems Clothing purchased
|
||||||
* @param output Client response
|
* @param output Client response
|
||||||
*/
|
*/
|
||||||
protected payForClothingItems(sessionId: string, pmcData: IPmcData, clothingItems: ClothingItem[], output: IItemEventRouterResponse): void
|
protected payForClothingItems(
|
||||||
|
sessionId: string,
|
||||||
|
pmcData: IPmcData,
|
||||||
|
clothingItems: ClothingItem[],
|
||||||
|
output: IItemEventRouterResponse,
|
||||||
|
): void
|
||||||
{
|
{
|
||||||
for (const sellItem of clothingItems)
|
for (const sellItem of clothingItems)
|
||||||
{
|
{
|
||||||
@ -154,12 +174,22 @@ export class CustomizationController
|
|||||||
* @param clothingItem Clothing item purchased
|
* @param clothingItem Clothing item purchased
|
||||||
* @param output Client response
|
* @param output Client response
|
||||||
*/
|
*/
|
||||||
protected payForClothingItem(sessionId: string, pmcData: IPmcData, clothingItem: ClothingItem, output: IItemEventRouterResponse): void
|
protected payForClothingItem(
|
||||||
|
sessionId: string,
|
||||||
|
pmcData: IPmcData,
|
||||||
|
clothingItem: ClothingItem,
|
||||||
|
output: IItemEventRouterResponse,
|
||||||
|
): void
|
||||||
{
|
{
|
||||||
const relatedItem = pmcData.Inventory.items.find(x => x._id === clothingItem.id);
|
const relatedItem = pmcData.Inventory.items.find((x) => x._id === clothingItem.id);
|
||||||
if (!relatedItem)
|
if (!relatedItem)
|
||||||
{
|
{
|
||||||
this.logger.error(this.localisationService.getText("customisation-unable_to_find_clothing_item_in_inventory", clothingItem.id));
|
this.logger.error(
|
||||||
|
this.localisationService.getText(
|
||||||
|
"customisation-unable_to_find_clothing_item_in_inventory",
|
||||||
|
clothingItem.id,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -179,7 +209,7 @@ export class CustomizationController
|
|||||||
parentId: relatedItem.parentId,
|
parentId: relatedItem.parentId,
|
||||||
slotId: relatedItem.slotId,
|
slotId: relatedItem.slotId,
|
||||||
location: relatedItem.location,
|
location: relatedItem.location,
|
||||||
upd: { StackObjectsCount: relatedItem.upd.StackObjectsCount }
|
upd: {StackObjectsCount: relatedItem.upd.StackObjectsCount},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -37,7 +37,7 @@ export class DialogueController
|
|||||||
@inject("MailSendService") protected mailSendService: MailSendService,
|
@inject("MailSendService") protected mailSendService: MailSendService,
|
||||||
@inject("GiftService") protected giftService: GiftService,
|
@inject("GiftService") protected giftService: GiftService,
|
||||||
@inject("HashUtil") protected hashUtil: HashUtil,
|
@inject("HashUtil") protected hashUtil: HashUtil,
|
||||||
@inject("ConfigServer") protected configServer: ConfigServer
|
@inject("ConfigServer") protected configServer: ConfigServer,
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
this.coreConfig = this.configServer.getConfig(ConfigTypes.CORE);
|
this.coreConfig = this.configServer.getConfig(ConfigTypes.CORE);
|
||||||
@ -57,14 +57,13 @@ export class DialogueController
|
|||||||
* Handle client/friend/list
|
* Handle client/friend/list
|
||||||
* @returns IGetFriendListDataResponse
|
* @returns IGetFriendListDataResponse
|
||||||
*/
|
*/
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
||||||
public getFriendList(sessionID: string): IGetFriendListDataResponse
|
public getFriendList(sessionID: string): IGetFriendListDataResponse
|
||||||
{
|
{
|
||||||
// Force a fake friend called SPT into friend list
|
// Force a fake friend called SPT into friend list
|
||||||
return {
|
return {
|
||||||
Friends: [this.getSptFriendData()],
|
Friends: [this.getSptFriendData()],
|
||||||
Ignore: [],
|
Ignore: [],
|
||||||
InIgnoreList: []
|
InIgnoreList: [],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -104,7 +103,7 @@ export class DialogueController
|
|||||||
new: dialogue.new,
|
new: dialogue.new,
|
||||||
attachmentsNew: dialogue.attachmentsNew,
|
attachmentsNew: dialogue.attachmentsNew,
|
||||||
pinned: dialogue.pinned,
|
pinned: dialogue.pinned,
|
||||||
Users: this.getDialogueUsers(dialogue, dialogue.type, sessionID)
|
Users: this.getDialogueUsers(dialogue, dialogue.type, sessionID),
|
||||||
};
|
};
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
@ -121,7 +120,9 @@ export class DialogueController
|
|||||||
const profile = this.saveServer.getProfile(sessionID);
|
const profile = this.saveServer.getProfile(sessionID);
|
||||||
|
|
||||||
// User to user messages are special in that they need the player to exist in them, add if they don't
|
// User to user messages are special in that they need the player to exist in them, add if they don't
|
||||||
if (messageType === MessageType.USER_MESSAGE && !dialog.Users?.find(x => x._id === profile.characters.pmc._id))
|
if (
|
||||||
|
messageType === MessageType.USER_MESSAGE && !dialog.Users?.find((x) => x._id === profile.characters.pmc._id)
|
||||||
|
)
|
||||||
{
|
{
|
||||||
if (!dialog.Users)
|
if (!dialog.Users)
|
||||||
{
|
{
|
||||||
@ -134,8 +135,8 @@ export class DialogueController
|
|||||||
Level: profile.characters.pmc.Info.Level,
|
Level: profile.characters.pmc.Info.Level,
|
||||||
Nickname: profile.characters.pmc.Info.Nickname,
|
Nickname: profile.characters.pmc.Info.Nickname,
|
||||||
Side: profile.characters.pmc.Info.Side,
|
Side: profile.characters.pmc.Info.Side,
|
||||||
MemberCategory: profile.characters.pmc.Info.MemberCategory
|
MemberCategory: profile.characters.pmc.Info.MemberCategory,
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -144,14 +145,17 @@ export class DialogueController
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle client/mail/dialog/view
|
* Handle client/mail/dialog/view
|
||||||
* Handle player clicking 'messenger' and seeing all the messages they've recieved
|
* Handle player clicking 'messenger' and seeing all the messages they've received
|
||||||
* Set the content of the dialogue on the details panel, showing all the messages
|
* Set the content of the dialogue on the details panel, showing all the messages
|
||||||
* for the specified dialogue.
|
* for the specified dialogue.
|
||||||
* @param request Get dialog request
|
* @param request Get dialog request
|
||||||
* @param sessionId Session id
|
* @param sessionId Session id
|
||||||
* @returns IGetMailDialogViewResponseData object
|
* @returns IGetMailDialogViewResponseData object
|
||||||
*/
|
*/
|
||||||
public generateDialogueView(request: IGetMailDialogViewRequestData, sessionId: string): IGetMailDialogViewResponseData
|
public generateDialogueView(
|
||||||
|
request: IGetMailDialogViewRequestData,
|
||||||
|
sessionId: string,
|
||||||
|
): IGetMailDialogViewResponseData
|
||||||
{
|
{
|
||||||
const dialogueId = request.dialogId;
|
const dialogueId = request.dialogId;
|
||||||
const fullProfile = this.saveServer.getProfile(sessionId);
|
const fullProfile = this.saveServer.getProfile(sessionId);
|
||||||
@ -166,14 +170,14 @@ export class DialogueController
|
|||||||
return {
|
return {
|
||||||
messages: dialogue.messages,
|
messages: dialogue.messages,
|
||||||
profiles: this.getProfilesForMail(fullProfile, dialogue.Users),
|
profiles: this.getProfilesForMail(fullProfile, dialogue.Users),
|
||||||
hasMessagesWithRewards: this.messagesHaveUncollectedRewards(dialogue.messages)
|
hasMessagesWithRewards: this.messagesHaveUncollectedRewards(dialogue.messages),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get dialog from player profile, create if doesn't exist
|
* Get dialog from player profile, create if doesn't exist
|
||||||
* @param profile Player profile
|
* @param profile Player profile
|
||||||
* @param request get dialog request (params used when dialog doesnt exist in profile)
|
* @param request get dialog request (params used when dialog doesn't exist in profile)
|
||||||
* @returns Dialogue
|
* @returns Dialogue
|
||||||
*/
|
*/
|
||||||
protected getDialogByIdFromProfile(profile: IAkiProfile, request: IGetMailDialogViewRequestData): Dialogue
|
protected getDialogByIdFromProfile(profile: IAkiProfile, request: IGetMailDialogViewRequestData): Dialogue
|
||||||
@ -186,7 +190,7 @@ export class DialogueController
|
|||||||
pinned: false,
|
pinned: false,
|
||||||
messages: [],
|
messages: [],
|
||||||
new: 0,
|
new: 0,
|
||||||
type: request.type
|
type: request.type,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (request.type === MessageType.USER_MESSAGE)
|
if (request.type === MessageType.USER_MESSAGE)
|
||||||
@ -211,8 +215,8 @@ export class DialogueController
|
|||||||
{
|
{
|
||||||
result.push(...dialogUsers);
|
result.push(...dialogUsers);
|
||||||
|
|
||||||
// Player doesnt exist, add them in before returning
|
// Player doesn't exist, add them in before returning
|
||||||
if (!result.find(x => x._id === fullProfile.info.id))
|
if (!result.find((x) => x._id === fullProfile.info.id))
|
||||||
{
|
{
|
||||||
const pmcProfile = fullProfile.characters.pmc;
|
const pmcProfile = fullProfile.characters.pmc;
|
||||||
result.push({
|
result.push({
|
||||||
@ -221,8 +225,8 @@ export class DialogueController
|
|||||||
Nickname: pmcProfile.Info.Nickname,
|
Nickname: pmcProfile.Info.Nickname,
|
||||||
Side: pmcProfile.Info.Side,
|
Side: pmcProfile.Info.Side,
|
||||||
Level: pmcProfile.Info.Level,
|
Level: pmcProfile.Info.Level,
|
||||||
MemberCategory: pmcProfile.Info.MemberCategory
|
MemberCategory: pmcProfile.Info.MemberCategory,
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -258,7 +262,7 @@ export class DialogueController
|
|||||||
*/
|
*/
|
||||||
protected messagesHaveUncollectedRewards(messages: Message[]): boolean
|
protected messagesHaveUncollectedRewards(messages: Message[]): boolean
|
||||||
{
|
{
|
||||||
return messages.some(x => x.items?.data?.length > 0);
|
return messages.some((x) => x.items?.data?.length > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -274,7 +278,6 @@ export class DialogueController
|
|||||||
if (!dialog)
|
if (!dialog)
|
||||||
{
|
{
|
||||||
this.logger.error(`No dialog in profile: ${sessionId} found with id: ${dialogueId}`);
|
this.logger.error(`No dialog in profile: ${sessionId} found with id: ${dialogueId}`);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -288,7 +291,6 @@ export class DialogueController
|
|||||||
if (!dialog)
|
if (!dialog)
|
||||||
{
|
{
|
||||||
this.logger.error(`No dialog in profile: ${sessionId} found with id: ${dialogueId}`);
|
this.logger.error(`No dialog in profile: ${sessionId} found with id: ${dialogueId}`);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -307,7 +309,6 @@ export class DialogueController
|
|||||||
if (!dialogs)
|
if (!dialogs)
|
||||||
{
|
{
|
||||||
this.logger.error(`No dialog object in profile: ${sessionId}`);
|
this.logger.error(`No dialog object in profile: ${sessionId}`);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -332,7 +333,6 @@ export class DialogueController
|
|||||||
if (!dialog)
|
if (!dialog)
|
||||||
{
|
{
|
||||||
this.logger.error(`No dialog in profile: ${sessionId} found with id: ${dialogueId}`);
|
this.logger.error(`No dialog in profile: ${sessionId} found with id: ${dialogueId}`);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -345,12 +345,11 @@ export class DialogueController
|
|||||||
return {
|
return {
|
||||||
messages: messagesWithAttachments,
|
messages: messagesWithAttachments,
|
||||||
profiles: [],
|
profiles: [],
|
||||||
hasMessagesWithRewards: this.messagesHaveUncollectedRewards(messagesWithAttachments)
|
hasMessagesWithRewards: this.messagesHaveUncollectedRewards(messagesWithAttachments),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/** client/mail/msg/send */
|
/** client/mail/msg/send */
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
||||||
public sendMessage(sessionId: string, request: ISendMessageRequest): string
|
public sendMessage(sessionId: string, request: ISendMessageRequest): string
|
||||||
{
|
{
|
||||||
this.mailSendService.sendPlayerMessageToNpc(sessionId, request.dialogId, request.text);
|
this.mailSendService.sendPlayerMessageToNpc(sessionId, request.dialogId, request.text);
|
||||||
@ -372,48 +371,97 @@ export class DialogueController
|
|||||||
protected handleChatWithSPTFriend(sessionId: string, request: ISendMessageRequest): void
|
protected handleChatWithSPTFriend(sessionId: string, request: ISendMessageRequest): void
|
||||||
{
|
{
|
||||||
const sender = this.profileHelper.getPmcProfile(sessionId);
|
const sender = this.profileHelper.getPmcProfile(sessionId);
|
||||||
|
|
||||||
const sptFriendUser = this.getSptFriendData();
|
const sptFriendUser = this.getSptFriendData();
|
||||||
|
|
||||||
const giftSent = this.giftService.sendGiftToPlayer(sessionId, request.text);
|
const giftSent = this.giftService.sendGiftToPlayer(sessionId, request.text);
|
||||||
|
|
||||||
if (giftSent === GiftSentResult.SUCCESS)
|
if (giftSent === GiftSentResult.SUCCESS)
|
||||||
{
|
{
|
||||||
this.mailSendService.sendUserMessageToPlayer(sessionId, sptFriendUser, this.randomUtil.getArrayValue(["Hey! you got the right code!", "A secret code, how exciting!", "You found a gift code!"]));
|
this.mailSendService.sendUserMessageToPlayer(
|
||||||
|
sessionId,
|
||||||
|
sptFriendUser,
|
||||||
|
this.randomUtil.getArrayValue([
|
||||||
|
"Hey! you got the right code!",
|
||||||
|
"A secret code, how exciting!",
|
||||||
|
"You found a gift code!",
|
||||||
|
]),
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (giftSent === GiftSentResult.FAILED_GIFT_ALREADY_RECEIVED)
|
if (giftSent === GiftSentResult.FAILED_GIFT_ALREADY_RECEIVED)
|
||||||
{
|
{
|
||||||
this.mailSendService.sendUserMessageToPlayer(sessionId, sptFriendUser, this.randomUtil.getArrayValue(["Looks like you already used that code", "You already have that!!"]));
|
this.mailSendService.sendUserMessageToPlayer(
|
||||||
|
sessionId,
|
||||||
|
sptFriendUser,
|
||||||
|
this.randomUtil.getArrayValue(["Looks like you already used that code", "You already have that!!"]),
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (request.text.toLowerCase().includes("love you"))
|
if (request.text.toLowerCase().includes("love you"))
|
||||||
{
|
{
|
||||||
this.mailSendService.sendUserMessageToPlayer(sessionId, sptFriendUser, this.randomUtil.getArrayValue(["That's quite forward but i love you too in a purely chatbot-human way", "I love you too buddy :3!", "uwu", `love you too ${sender?.Info?.Nickname}`]));
|
this.mailSendService.sendUserMessageToPlayer(
|
||||||
|
sessionId,
|
||||||
|
sptFriendUser,
|
||||||
|
this.randomUtil.getArrayValue([
|
||||||
|
"That's quite forward but i love you too in a purely chatbot-human way",
|
||||||
|
"I love you too buddy :3!",
|
||||||
|
"uwu",
|
||||||
|
`love you too ${sender?.Info?.Nickname}`,
|
||||||
|
]),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (request.text.toLowerCase() === "spt")
|
if (request.text.toLowerCase() === "spt")
|
||||||
{
|
{
|
||||||
this.mailSendService.sendUserMessageToPlayer(sessionId, sptFriendUser, this.randomUtil.getArrayValue(["Its me!!", "spt? i've heard of that project"]));
|
this.mailSendService.sendUserMessageToPlayer(
|
||||||
|
sessionId,
|
||||||
|
sptFriendUser,
|
||||||
|
this.randomUtil.getArrayValue(["Its me!!", "spt? i've heard of that project"]),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (["hello", "hi", "sup", "yo", "hey"].includes(request.text.toLowerCase()))
|
if (["hello", "hi", "sup", "yo", "hey"].includes(request.text.toLowerCase()))
|
||||||
{
|
{
|
||||||
this.mailSendService.sendUserMessageToPlayer(sessionId, sptFriendUser, this.randomUtil.getArrayValue(["Howdy", "Hi", "Greetings", "Hello", "bonjor", "Yo", "Sup", "Heyyyyy", "Hey there", `Hello ${sender?.Info?.Nickname}`]));
|
this.mailSendService.sendUserMessageToPlayer(
|
||||||
|
sessionId,
|
||||||
|
sptFriendUser,
|
||||||
|
this.randomUtil.getArrayValue([
|
||||||
|
"Howdy",
|
||||||
|
"Hi",
|
||||||
|
"Greetings",
|
||||||
|
"Hello",
|
||||||
|
"Bonjour",
|
||||||
|
"Yo",
|
||||||
|
"Sup",
|
||||||
|
"Heyyyyy",
|
||||||
|
"Hey there",
|
||||||
|
`Hello ${sender?.Info?.Nickname}`,
|
||||||
|
]),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (request.text.toLowerCase() === "nikita")
|
if (request.text.toLowerCase() === "nikita")
|
||||||
{
|
{
|
||||||
this.mailSendService.sendUserMessageToPlayer(sessionId, sptFriendUser, this.randomUtil.getArrayValue(["I know that guy!", "Cool guy, he made EFT!", "Legend", "Remember when he said webel-webel-webel-webel, classic nikita moment"]));
|
this.mailSendService.sendUserMessageToPlayer(
|
||||||
|
sessionId,
|
||||||
|
sptFriendUser,
|
||||||
|
this.randomUtil.getArrayValue([
|
||||||
|
"I know that guy!",
|
||||||
|
"Cool guy, he made EFT!",
|
||||||
|
"Legend",
|
||||||
|
"Remember when he said webel-webel-webel-webel, classic nikita moment",
|
||||||
|
]),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (request.text.toLowerCase() === "are you a bot")
|
if (request.text.toLowerCase() === "are you a bot")
|
||||||
{
|
{
|
||||||
this.mailSendService.sendUserMessageToPlayer(sessionId, sptFriendUser, this.randomUtil.getArrayValue(["beep boop", "**sad boop**", "probably", "sometimes", "yeah lol"]));
|
this.mailSendService.sendUserMessageToPlayer(
|
||||||
|
sessionId,
|
||||||
|
sptFriendUser,
|
||||||
|
this.randomUtil.getArrayValue(["beep boop", "**sad boop**", "probably", "sometimes", "yeah lol"]),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -425,8 +473,8 @@ export class DialogueController
|
|||||||
Level: 1,
|
Level: 1,
|
||||||
MemberCategory: MemberCategory.DEVELOPER,
|
MemberCategory: MemberCategory.DEVELOPER,
|
||||||
Nickname: this.coreConfig.sptFriendNickname,
|
Nickname: this.coreConfig.sptFriendNickname,
|
||||||
Side: "Usec"
|
Side: "Usec",
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -440,7 +488,7 @@ export class DialogueController
|
|||||||
{
|
{
|
||||||
const timeNow = this.timeUtil.getTimestamp();
|
const timeNow = this.timeUtil.getTimestamp();
|
||||||
const dialogs = this.dialogueHelper.getDialogsForProfile(sessionId);
|
const dialogs = this.dialogueHelper.getDialogsForProfile(sessionId);
|
||||||
return dialogs[dialogueId].messages.filter(x => timeNow < (x.dt + x.maxStorageTime));
|
return dialogs[dialogueId].messages.filter((x) => timeNow < (x.dt + x.maxStorageTime));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -450,7 +498,7 @@ export class DialogueController
|
|||||||
*/
|
*/
|
||||||
protected getMessagesWithAttachments(messages: Message[]): Message[]
|
protected getMessagesWithAttachments(messages: Message[]): Message[]
|
||||||
{
|
{
|
||||||
return messages.filter(x => x.items?.data?.length > 0);
|
return messages.filter((x) => x.items?.data?.length > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -69,7 +69,7 @@ export class GameController
|
|||||||
@inject("ItemBaseClassService") protected itemBaseClassService: ItemBaseClassService,
|
@inject("ItemBaseClassService") protected itemBaseClassService: ItemBaseClassService,
|
||||||
@inject("GiftService") protected giftService: GiftService,
|
@inject("GiftService") protected giftService: GiftService,
|
||||||
@inject("ApplicationContext") protected applicationContext: ApplicationContext,
|
@inject("ApplicationContext") protected applicationContext: ApplicationContext,
|
||||||
@inject("ConfigServer") protected configServer: ConfigServer
|
@inject("ConfigServer") protected configServer: ConfigServer,
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
this.httpConfig = this.configServer.getConfig(ConfigTypes.HTTP);
|
this.httpConfig = this.configServer.getConfig(ConfigTypes.HTTP);
|
||||||
@ -82,10 +82,10 @@ export class GameController
|
|||||||
|
|
||||||
public load(): void
|
public load(): void
|
||||||
{
|
{
|
||||||
// Regenerate basecache now mods are loaded and game is starting
|
// Regenerate base cache now mods are loaded and game is starting
|
||||||
// Mods that add items and use the baseclass service generate the cache including their items, the next mod that add items gets left out,causing warnings
|
// Mods that add items and use the baseClass service generate the cache including their items, the next mod that
|
||||||
|
// add items gets left out,causing warnings
|
||||||
this.itemBaseClassService.hydrateItemBaseClassCache();
|
this.itemBaseClassService.hydrateItemBaseClassCache();
|
||||||
|
|
||||||
this.addCustomLooseLootPositions();
|
this.addCustomLooseLootPositions();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -121,9 +121,9 @@ export class GameController
|
|||||||
|
|
||||||
this.checkTraderRepairValuesExist();
|
this.checkTraderRepairValuesExist();
|
||||||
|
|
||||||
// repeatableQuests are stored by in profile.Quests due to the responses of the client (e.g. Quests in offraidData)
|
// repeatableQuests are stored by in profile.Quests due to the responses of the client (e.g. Quests in
|
||||||
// Since we don't want to clutter the Quests list, we need to remove all completed (failed / successful) repeatable quests.
|
// offraidData). Since we don't want to clutter the Quests list, we need to remove all completed (failed or
|
||||||
// We also have to remove the Counters from the repeatableQuests
|
// successful) repeatable quests. We also have to remove the Counters from the repeatableQuests
|
||||||
if (sessionID)
|
if (sessionID)
|
||||||
{
|
{
|
||||||
const fullProfile = this.profileHelper.getFullProfile(sessionID);
|
const fullProfile = this.profileHelper.getFullProfile(sessionID);
|
||||||
@ -240,7 +240,9 @@ export class GameController
|
|||||||
const trader = this.databaseServer.getTables().traders[traderKey];
|
const trader = this.databaseServer.getTables().traders[traderKey];
|
||||||
if (!trader?.base?.repair)
|
if (!trader?.base?.repair)
|
||||||
{
|
{
|
||||||
this.logger.warning(`Trader ${trader.base._id} ${trader.base.name} is missing a repair object, adding in default values`);
|
this.logger.warning(
|
||||||
|
`Trader ${trader.base._id} ${trader.base.name} is missing a repair object, adding in default values`,
|
||||||
|
);
|
||||||
trader.base.repair = this.jsonUtil.clone(this.databaseServer.getTables().traders.ragfair.base.repair);
|
trader.base.repair = this.jsonUtil.clone(this.databaseServer.getTables().traders.ragfair.base.repair);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
@ -248,8 +250,12 @@ export class GameController
|
|||||||
|
|
||||||
if (trader?.base?.repair?.quality)
|
if (trader?.base?.repair?.quality)
|
||||||
{
|
{
|
||||||
this.logger.warning(`Trader ${trader.base._id} ${trader.base.name} is missing a repair quality value, adding in default value`);
|
this.logger.warning(
|
||||||
trader.base.repair.quality = this.jsonUtil.clone(this.databaseServer.getTables().traders.ragfair.base.repair.quality);
|
`Trader ${trader.base._id} ${trader.base.name} is missing a repair quality value, adding in default value`,
|
||||||
|
);
|
||||||
|
trader.base.repair.quality = this.jsonUtil.clone(
|
||||||
|
this.databaseServer.getTables().traders.ragfair.base.repair.quality,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -274,7 +280,9 @@ export class GameController
|
|||||||
for (const positionToAdd of positionsToAdd)
|
for (const positionToAdd of positionsToAdd)
|
||||||
{
|
{
|
||||||
// Exists already, add new items to existing positions pool
|
// Exists already, add new items to existing positions pool
|
||||||
const existingLootPosition = mapLooseLoot.spawnpoints.find(x => x.template.Id === positionToAdd.template.Id);
|
const existingLootPosition = mapLooseLoot.spawnpoints.find((x) =>
|
||||||
|
x.template.Id === positionToAdd.template.Id
|
||||||
|
);
|
||||||
if (existingLootPosition)
|
if (existingLootPosition)
|
||||||
{
|
{
|
||||||
existingLootPosition.template.Items.push(...positionToAdd.template.Items);
|
existingLootPosition.template.Items.push(...positionToAdd.template.Items);
|
||||||
@ -283,7 +291,7 @@ export class GameController
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// new postion, add entire object
|
// New position, add entire object
|
||||||
mapLooseLoot.spawnpoints.push(positionToAdd);
|
mapLooseLoot.spawnpoints.push(positionToAdd);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -303,7 +311,7 @@ export class GameController
|
|||||||
const mapLootAdjustmentsDict = adjustments[mapId];
|
const mapLootAdjustmentsDict = adjustments[mapId];
|
||||||
for (const lootKey in mapLootAdjustmentsDict)
|
for (const lootKey in mapLootAdjustmentsDict)
|
||||||
{
|
{
|
||||||
const lootPostionToAdjust = mapLooseLootData.spawnpoints.find(x => x.template.Id === lootKey);
|
const lootPostionToAdjust = mapLooseLootData.spawnpoints.find((x) => x.template.Id === lootKey);
|
||||||
if (!lootPostionToAdjust)
|
if (!lootPostionToAdjust)
|
||||||
{
|
{
|
||||||
this.logger.warning(`Unable to adjust loot position: ${lootKey} on map: ${mapId}`);
|
this.logger.warning(`Unable to adjust loot position: ${lootKey} on map: ${mapId}`);
|
||||||
@ -363,12 +371,14 @@ export class GameController
|
|||||||
const map: ILocationData = mapsDb[mapId];
|
const map: ILocationData = mapsDb[mapId];
|
||||||
if (!map)
|
if (!map)
|
||||||
{
|
{
|
||||||
this.logger.warning(this.localisationService.getText("bot-unable_to_edit_limits_of_unknown_map", mapId));
|
this.logger.warning(
|
||||||
|
this.localisationService.getText("bot-unable_to_edit_limits_of_unknown_map", mapId),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const botToLimit of this.locationConfig.botTypeLimits[mapId])
|
for (const botToLimit of this.locationConfig.botTypeLimits[mapId])
|
||||||
{
|
{
|
||||||
const index = map.base.MinMaxBots.findIndex(x => x.WildSpawnType === botToLimit.type);
|
const index = map.base.MinMaxBots.findIndex((x) => x.WildSpawnType === botToLimit.type);
|
||||||
if (index !== -1)
|
if (index !== -1)
|
||||||
{
|
{
|
||||||
// Existing bot type found in MinMaxBots array, edit
|
// Existing bot type found in MinMaxBots array, edit
|
||||||
@ -378,14 +388,12 @@ export class GameController
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
map.base.MinMaxBots.push(
|
// Bot type not found, add new object
|
||||||
{
|
map.base.MinMaxBots.push({
|
||||||
// Bot type not found, add new object
|
WildSpawnType: botToLimit.type,
|
||||||
WildSpawnType: botToLimit.type,
|
min: botToLimit.min,
|
||||||
min: botToLimit.min,
|
max: botToLimit.max,
|
||||||
max: botToLimit.max
|
});
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -412,12 +420,11 @@ export class GameController
|
|||||||
Trading: this.httpServerHelper.getBackendUrl(),
|
Trading: this.httpServerHelper.getBackendUrl(),
|
||||||
Messaging: this.httpServerHelper.getBackendUrl(),
|
Messaging: this.httpServerHelper.getBackendUrl(),
|
||||||
Main: this.httpServerHelper.getBackendUrl(),
|
Main: this.httpServerHelper.getBackendUrl(),
|
||||||
RagFair: this.httpServerHelper.getBackendUrl()
|
RagFair: this.httpServerHelper.getBackendUrl(),
|
||||||
},
|
},
|
||||||
useProtobuf: false,
|
useProtobuf: false,
|
||||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
||||||
utc_time: new Date().getTime() / 1000,
|
utc_time: new Date().getTime() / 1000,
|
||||||
totalInGame: profile.Stats?.Eft?.TotalInGameTime ?? 0
|
totalInGame: profile.Stats?.Eft?.TotalInGameTime ?? 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
return config;
|
return config;
|
||||||
@ -426,50 +433,43 @@ export class GameController
|
|||||||
/**
|
/**
|
||||||
* Handle client/server/list
|
* Handle client/server/list
|
||||||
*/
|
*/
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
||||||
public getServer(sessionId: string): IServerDetails[]
|
public getServer(sessionId: string): IServerDetails[]
|
||||||
{
|
{
|
||||||
return [
|
return [{
|
||||||
{
|
ip: this.httpConfig.ip,
|
||||||
ip: this.httpConfig.ip,
|
port: this.httpConfig.port,
|
||||||
port: this.httpConfig.port
|
}];
|
||||||
}
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle client/match/group/current
|
* Handle client/match/group/current
|
||||||
*/
|
*/
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
||||||
public getCurrentGroup(sessionId: string): ICurrentGroupResponse
|
public getCurrentGroup(sessionId: string): ICurrentGroupResponse
|
||||||
{
|
{
|
||||||
return {
|
return {
|
||||||
squad: []
|
squad: [],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle client/checkVersion
|
* Handle client/checkVersion
|
||||||
*/
|
*/
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
||||||
public getValidGameVersion(sessionId: string): ICheckVersionResponse
|
public getValidGameVersion(sessionId: string): ICheckVersionResponse
|
||||||
{
|
{
|
||||||
return {
|
return {
|
||||||
isvalid: true,
|
isvalid: true,
|
||||||
latestVersion: this.coreConfig.compatibleTarkovVersion
|
latestVersion: this.coreConfig.compatibleTarkovVersion,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle client/game/keepalive
|
* Handle client/game/keepalive
|
||||||
*/
|
*/
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
||||||
public getKeepAlive(sessionId: string): IGameKeepAliveResponse
|
public getKeepAlive(sessionId: string): IGameKeepAliveResponse
|
||||||
{
|
{
|
||||||
return {
|
return {
|
||||||
msg: "OK",
|
msg: "OK",
|
||||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
utc_time: new Date().getTime() / 1000,
|
||||||
utc_time: new Date().getTime() / 1000
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -520,7 +520,7 @@ export class GameController
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* When player logs in, iterate over all active effects and reduce timer
|
* When player logs in, iterate over all active effects and reduce timer
|
||||||
* TODO - add body part HP regen
|
* // TODO: Add body part HP regeneration
|
||||||
* @param pmcProfile
|
* @param pmcProfile
|
||||||
*/
|
*/
|
||||||
protected updateProfileHealthValues(pmcProfile: IPmcData): void
|
protected updateProfileHealthValues(pmcProfile: IPmcData): void
|
||||||
@ -538,14 +538,23 @@ export class GameController
|
|||||||
let hpRegenPerHour = 456.6;
|
let hpRegenPerHour = 456.6;
|
||||||
|
|
||||||
// Set new values, whatever is smallest
|
// Set new values, whatever is smallest
|
||||||
energyRegenPerHour += pmcProfile.Bonuses.filter(x => x.type === "EnergyRegeneration").reduce((sum, curr) => sum + curr.value, 0);
|
energyRegenPerHour += pmcProfile.Bonuses.filter((x) => x.type === "EnergyRegeneration").reduce(
|
||||||
hydrationRegenPerHour += pmcProfile.Bonuses.filter(x => x.type === "HydrationRegeneration").reduce((sum, curr) => sum + curr.value, 0);
|
(sum, curr) => sum + curr.value,
|
||||||
hpRegenPerHour += pmcProfile.Bonuses.filter(x => x.type === "HealthRegeneration").reduce((sum, curr) => sum + curr.value, 0);
|
0,
|
||||||
|
);
|
||||||
|
hydrationRegenPerHour += pmcProfile.Bonuses.filter((x) => x.type === "HydrationRegeneration").reduce(
|
||||||
|
(sum, curr) => sum + curr.value,
|
||||||
|
0,
|
||||||
|
);
|
||||||
|
hpRegenPerHour += pmcProfile.Bonuses.filter((x) => x.type === "HealthRegeneration").reduce(
|
||||||
|
(sum, curr) => sum + curr.value,
|
||||||
|
0,
|
||||||
|
);
|
||||||
|
|
||||||
if (pmcProfile.Health.Energy.Current !== pmcProfile.Health.Energy.Maximum)
|
if (pmcProfile.Health.Energy.Current !== pmcProfile.Health.Energy.Maximum)
|
||||||
{
|
{
|
||||||
// Set new value, whatever is smallest
|
// Set new value, whatever is smallest
|
||||||
pmcProfile.Health.Energy.Current += Math.round((energyRegenPerHour * (diffSeconds / 3600)));
|
pmcProfile.Health.Energy.Current += Math.round(energyRegenPerHour * (diffSeconds / 3600));
|
||||||
if (pmcProfile.Health.Energy.Current > pmcProfile.Health.Energy.Maximum)
|
if (pmcProfile.Health.Energy.Current > pmcProfile.Health.Energy.Maximum)
|
||||||
{
|
{
|
||||||
pmcProfile.Health.Energy.Current = pmcProfile.Health.Energy.Maximum;
|
pmcProfile.Health.Energy.Current = pmcProfile.Health.Energy.Maximum;
|
||||||
@ -554,7 +563,7 @@ export class GameController
|
|||||||
|
|
||||||
if (pmcProfile.Health.Hydration.Current !== pmcProfile.Health.Hydration.Maximum)
|
if (pmcProfile.Health.Hydration.Current !== pmcProfile.Health.Hydration.Maximum)
|
||||||
{
|
{
|
||||||
pmcProfile.Health.Hydration.Current += Math.round((hydrationRegenPerHour * (diffSeconds / 3600)));
|
pmcProfile.Health.Hydration.Current += Math.round(hydrationRegenPerHour * (diffSeconds / 3600));
|
||||||
if (pmcProfile.Health.Hydration.Current > pmcProfile.Health.Hydration.Maximum)
|
if (pmcProfile.Health.Hydration.Current > pmcProfile.Health.Hydration.Maximum)
|
||||||
{
|
{
|
||||||
pmcProfile.Health.Hydration.Current = pmcProfile.Health.Hydration.Maximum;
|
pmcProfile.Health.Hydration.Current = pmcProfile.Health.Hydration.Maximum;
|
||||||
@ -569,7 +578,7 @@ export class GameController
|
|||||||
// Check part hp
|
// Check part hp
|
||||||
if (bodyPart.Health.Current < bodyPart.Health.Maximum)
|
if (bodyPart.Health.Current < bodyPart.Health.Maximum)
|
||||||
{
|
{
|
||||||
bodyPart.Health.Current += Math.round((hpRegenPerHour * (diffSeconds / 3600)));
|
bodyPart.Health.Current += Math.round(hpRegenPerHour * (diffSeconds / 3600));
|
||||||
}
|
}
|
||||||
if (bodyPart.Health.Current > bodyPart.Health.Maximum)
|
if (bodyPart.Health.Current > bodyPart.Health.Maximum)
|
||||||
{
|
{
|
||||||
@ -618,7 +627,9 @@ export class GameController
|
|||||||
const location: ILocationData = this.databaseServer.getTables().locations[locationKey];
|
const location: ILocationData = this.databaseServer.getTables().locations[locationKey];
|
||||||
if (!location.base)
|
if (!location.base)
|
||||||
{
|
{
|
||||||
this.logger.warning(this.localisationService.getText("location-unable_to_fix_broken_waves_missing_base", locationKey));
|
this.logger.warning(
|
||||||
|
this.localisationService.getText("location-unable_to_fix_broken_waves_missing_base", locationKey),
|
||||||
|
);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -626,7 +637,9 @@ export class GameController
|
|||||||
{
|
{
|
||||||
if ((wave.slots_max - wave.slots_min === 0))
|
if ((wave.slots_max - wave.slots_min === 0))
|
||||||
{
|
{
|
||||||
this.logger.debug(`Fixed ${wave.WildSpawnType} Spawn: ${locationKey} wave: ${wave.number} of type: ${wave.WildSpawnType} in zone: ${wave.SpawnPoints} with Max Slots of ${wave.slots_max}`);
|
this.logger.debug(
|
||||||
|
`Fixed ${wave.WildSpawnType} Spawn: ${locationKey} wave: ${wave.number} of type: ${wave.WildSpawnType} in zone: ${wave.SpawnPoints} with Max Slots of ${wave.slots_max}`,
|
||||||
|
);
|
||||||
wave.slots_max++;
|
wave.slots_max++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -673,7 +686,8 @@ export class GameController
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find and split waves with large numbers of bots into smaller waves - BSG appears to reduce the size of these waves to one bot when they're waiting to spawn for too long
|
* Find and split waves with large numbers of bots into smaller waves - BSG appears to reduce the size of these
|
||||||
|
* waves to one bot when they're waiting to spawn for too long
|
||||||
*/
|
*/
|
||||||
protected splitBotWavesIntoSingleWaves(): void
|
protected splitBotWavesIntoSingleWaves(): void
|
||||||
{
|
{
|
||||||
@ -689,7 +703,10 @@ export class GameController
|
|||||||
for (const wave of location.base.waves)
|
for (const wave of location.base.waves)
|
||||||
{
|
{
|
||||||
// Wave has size that makes it candidate for splitting
|
// Wave has size that makes it candidate for splitting
|
||||||
if (wave.slots_max - wave.slots_min >= this.locationConfig.splitWaveIntoSingleSpawnsSettings.waveSizeThreshold)
|
if (
|
||||||
|
wave.slots_max - wave.slots_min >=
|
||||||
|
this.locationConfig.splitWaveIntoSingleSpawnsSettings.waveSizeThreshold
|
||||||
|
)
|
||||||
{
|
{
|
||||||
// Get count of bots to be spawned in wave
|
// Get count of bots to be spawned in wave
|
||||||
const waveSize = wave.slots_max - wave.slots_min;
|
const waveSize = wave.slots_max - wave.slots_min;
|
||||||
@ -700,7 +717,9 @@ export class GameController
|
|||||||
|
|
||||||
// Get index of wave
|
// Get index of wave
|
||||||
const indexOfWaveToSplit = location.base.waves.indexOf(wave);
|
const indexOfWaveToSplit = location.base.waves.indexOf(wave);
|
||||||
this.logger.debug(`Splitting map: ${location.base.Id} wave: ${indexOfWaveToSplit} with ${waveSize} bots`);
|
this.logger.debug(
|
||||||
|
`Splitting map: ${location.base.Id} wave: ${indexOfWaveToSplit} with ${waveSize} bots`,
|
||||||
|
);
|
||||||
|
|
||||||
// Add new waves to fill gap from bots we removed in above wave
|
// Add new waves to fill gap from bots we removed in above wave
|
||||||
let wavesAddedCount = 0;
|
let wavesAddedCount = 0;
|
||||||
@ -716,20 +735,23 @@ export class GameController
|
|||||||
waveToAdd.number = index;
|
waveToAdd.number = index;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Place wave into array in just-edited postion + 1
|
// Place wave into array in just-edited position + 1
|
||||||
location.base.waves.splice(index, 0, waveToAdd);
|
location.base.waves.splice(index, 0, waveToAdd);
|
||||||
wavesAddedCount++;
|
wavesAddedCount++;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update subsequent wave number property to accomodate the new waves
|
// Update subsequent wave number property to accommodate the new waves
|
||||||
for (let index = indexOfWaveToSplit + wavesAddedCount + 1; index < location.base.waves.length; index++)
|
for (
|
||||||
|
let index = indexOfWaveToSplit + wavesAddedCount + 1;
|
||||||
|
index < location.base.waves.length;
|
||||||
|
index++
|
||||||
|
)
|
||||||
{
|
{
|
||||||
// Some waves have value of 0, leave them as-is
|
// Some waves have value of 0, leave them as-is
|
||||||
if (location.base.waves[index].number !== 0)
|
if (location.base.waves[index].number !== 0)
|
||||||
{
|
{
|
||||||
location.base.waves[index].number += wavesAddedCount;
|
location.base.waves[index].number += wavesAddedCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -753,9 +775,13 @@ export class GameController
|
|||||||
for (const modKey in activeMods)
|
for (const modKey in activeMods)
|
||||||
{
|
{
|
||||||
const modDetails = activeMods[modKey];
|
const modDetails = activeMods[modKey];
|
||||||
if (fullProfile.aki.mods.some(x => x.author === modDetails.author
|
if (
|
||||||
&& x.name === modDetails.name
|
fullProfile.aki.mods.some((x) =>
|
||||||
&& x.version === modDetails.version))
|
x.author === modDetails.author &&
|
||||||
|
x.name === modDetails.name &&
|
||||||
|
x.version === modDetails.version
|
||||||
|
)
|
||||||
|
)
|
||||||
{
|
{
|
||||||
// Exists already, skip
|
// Exists already, skip
|
||||||
continue;
|
continue;
|
||||||
@ -765,13 +791,13 @@ export class GameController
|
|||||||
author: modDetails.author,
|
author: modDetails.author,
|
||||||
dateAdded: Date.now(),
|
dateAdded: Date.now(),
|
||||||
name: modDetails.name,
|
name: modDetails.name,
|
||||||
version: modDetails.version
|
version: modDetails.version,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check for any missing assorts inside each traders assort.json data, checking against traders qeustassort.json
|
* Check for any missing assorts inside each traders assort.json data, checking against traders questassort.json
|
||||||
*/
|
*/
|
||||||
protected validateQuestAssortUnlocksExist(): void
|
protected validateQuestAssortUnlocksExist(): void
|
||||||
{
|
{
|
||||||
@ -788,19 +814,26 @@ export class GameController
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Merge started/success/fail quest assorts into one dictionary
|
// Merge started/success/fail quest assorts into one dictionary
|
||||||
const mergedQuestAssorts = { ...traderData.questassort["started"], ...traderData.questassort["success"], ...traderData.questassort["fail"]};
|
const mergedQuestAssorts = {
|
||||||
|
...traderData.questassort["started"],
|
||||||
|
...traderData.questassort["success"],
|
||||||
|
...traderData.questassort["fail"],
|
||||||
|
};
|
||||||
|
|
||||||
// loop over all assorts for trader
|
// Loop over all assorts for trader
|
||||||
for (const [assortKey, questKey] of Object.entries(mergedQuestAssorts))
|
for (const [assortKey, questKey] of Object.entries(mergedQuestAssorts))
|
||||||
{
|
{
|
||||||
// Does assort key exist in trader assort file
|
// Does assort key exist in trader assort file
|
||||||
if (!traderAssorts.loyal_level_items[assortKey])
|
if (!traderAssorts.loyal_level_items[assortKey])
|
||||||
{
|
{
|
||||||
// reverse lookup of enum key by value
|
// Reverse lookup of enum key by value
|
||||||
const messageValues = {
|
const messageValues = {
|
||||||
traderName: Object.keys(Traders)[Object.values(Traders).indexOf(traderId)],
|
traderName: Object.keys(Traders)[Object.values(Traders).indexOf(traderId)],
|
||||||
questName: quests[questKey]?.QuestName ?? "UNKNOWN"};
|
questName: quests[questKey]?.QuestName ?? "UNKNOWN",
|
||||||
this.logger.debug(this.localisationService.getText("assort-missing_quest_assort_unlock", messageValues));
|
};
|
||||||
|
this.logger.debug(
|
||||||
|
this.localisationService.getText("assort-missing_quest_assort_unlock", messageValues),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -847,7 +880,7 @@ export class GameController
|
|||||||
*/
|
*/
|
||||||
protected removePraporTestMessage(): void
|
protected removePraporTestMessage(): void
|
||||||
{
|
{
|
||||||
// Iterate over all langauges (e.g. "en", "fr")
|
// Iterate over all languages (e.g. "en", "fr")
|
||||||
for (const localeKey in this.databaseServer.getTables().locales.global)
|
for (const localeKey in this.databaseServer.getTables().locales.global)
|
||||||
{
|
{
|
||||||
this.databaseServer.getTables().locales.global[localeKey]["61687e2c3e526901fa76baf9"] = "";
|
this.databaseServer.getTables().locales.global[localeKey]["61687e2c3e526901fa76baf9"] = "";
|
||||||
@ -860,7 +893,9 @@ export class GameController
|
|||||||
protected adjustLabsRaiderSpawnRate(): void
|
protected adjustLabsRaiderSpawnRate(): void
|
||||||
{
|
{
|
||||||
const labsBase = this.databaseServer.getTables().locations.laboratory.base;
|
const labsBase = this.databaseServer.getTables().locations.laboratory.base;
|
||||||
const nonTriggerLabsBossSpawns = labsBase.BossLocationSpawn.filter(x => x.TriggerId === "" && x.TriggerName === "");
|
const nonTriggerLabsBossSpawns = labsBase.BossLocationSpawn.filter((x) =>
|
||||||
|
x.TriggerId === "" && x.TriggerName === ""
|
||||||
|
);
|
||||||
if (nonTriggerLabsBossSpawns)
|
if (nonTriggerLabsBossSpawns)
|
||||||
{
|
{
|
||||||
for (const boss of nonTriggerLabsBossSpawns)
|
for (const boss of nonTriggerLabsBossSpawns)
|
||||||
|
@ -8,9 +8,9 @@ export class HandbookController
|
|||||||
{
|
{
|
||||||
constructor(
|
constructor(
|
||||||
@inject("DatabaseServer") protected databaseServer: DatabaseServer,
|
@inject("DatabaseServer") protected databaseServer: DatabaseServer,
|
||||||
@inject("HandbookHelper") protected handbookHelper: HandbookHelper
|
@inject("HandbookHelper") protected handbookHelper: HandbookHelper,
|
||||||
)
|
)
|
||||||
{ }
|
{}
|
||||||
|
|
||||||
public load(): void
|
public load(): void
|
||||||
{
|
{
|
||||||
|
@ -31,7 +31,7 @@ export class HealthController
|
|||||||
@inject("InventoryHelper") protected inventoryHelper: InventoryHelper,
|
@inject("InventoryHelper") protected inventoryHelper: InventoryHelper,
|
||||||
@inject("LocalisationService") protected localisationService: LocalisationService,
|
@inject("LocalisationService") protected localisationService: LocalisationService,
|
||||||
@inject("HttpResponseUtil") protected httpResponse: HttpResponseUtil,
|
@inject("HttpResponseUtil") protected httpResponse: HttpResponseUtil,
|
||||||
@inject("HealthHelper") protected healthHelper: HealthHelper
|
@inject("HealthHelper") protected healthHelper: HealthHelper,
|
||||||
)
|
)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
@ -43,7 +43,13 @@ export class HealthController
|
|||||||
* @param addEffects Should effects found be added or removed from profile
|
* @param addEffects Should effects found be added or removed from profile
|
||||||
* @param deleteExistingEffects Should all prior effects be removed before apply new ones
|
* @param deleteExistingEffects Should all prior effects be removed before apply new ones
|
||||||
*/
|
*/
|
||||||
public saveVitality(pmcData: IPmcData, info: ISyncHealthRequestData, sessionID: string, addEffects = true, deleteExistingEffects = true): void
|
public saveVitality(
|
||||||
|
pmcData: IPmcData,
|
||||||
|
info: ISyncHealthRequestData,
|
||||||
|
sessionID: string,
|
||||||
|
addEffects = true,
|
||||||
|
deleteExistingEffects = true,
|
||||||
|
): void
|
||||||
{
|
{
|
||||||
this.healthHelper.saveVitality(pmcData, info, sessionID, addEffects, deleteExistingEffects);
|
this.healthHelper.saveVitality(pmcData, info, sessionID, addEffects, deleteExistingEffects);
|
||||||
}
|
}
|
||||||
@ -60,10 +66,13 @@ export class HealthController
|
|||||||
const output = this.eventOutputHolder.getOutput(sessionID);
|
const output = this.eventOutputHolder.getOutput(sessionID);
|
||||||
|
|
||||||
// Update medkit used (hpresource)
|
// Update medkit used (hpresource)
|
||||||
const healingItemToUse = pmcData.Inventory.items.find(item => item._id === request.item);
|
const healingItemToUse = pmcData.Inventory.items.find((item) => item._id === request.item);
|
||||||
if (!healingItemToUse)
|
if (!healingItemToUse)
|
||||||
{
|
{
|
||||||
const errorMessage = this.localisationService.getText("health-healing_item_not_found", healingItemToUse._id);
|
const errorMessage = this.localisationService.getText(
|
||||||
|
"health-healing_item_not_found",
|
||||||
|
healingItemToUse._id,
|
||||||
|
);
|
||||||
this.logger.error(errorMessage);
|
this.logger.error(errorMessage);
|
||||||
|
|
||||||
return this.httpResponse.appendErrorToOutput(output, errorMessage);
|
return this.httpResponse.appendErrorToOutput(output, errorMessage);
|
||||||
@ -82,8 +91,8 @@ export class HealthController
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Get max healing from db
|
// Get max healing from db
|
||||||
const maxhp = this.itemHelper.getItem(healingItemToUse._tpl)[1]._props.MaxHpResource;
|
const maxHp = this.itemHelper.getItem(healingItemToUse._tpl)[1]._props.MaxHpResource;
|
||||||
healingItemToUse.upd.MedKit = { HpResource: maxhp - request.count }; // Subtract amout used from max
|
healingItemToUse.upd.MedKit = {HpResource: maxHp - request.count}; // Subtract amount used from max
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resource in medkit is spent, delete it
|
// Resource in medkit is spent, delete it
|
||||||
@ -108,11 +117,14 @@ export class HealthController
|
|||||||
let output = this.eventOutputHolder.getOutput(sessionID);
|
let output = this.eventOutputHolder.getOutput(sessionID);
|
||||||
let resourceLeft = 0;
|
let resourceLeft = 0;
|
||||||
|
|
||||||
const itemToConsume = pmcData.Inventory.items.find(x => x._id === request.item);
|
const itemToConsume = pmcData.Inventory.items.find((x) => x._id === request.item);
|
||||||
if (!itemToConsume)
|
if (!itemToConsume)
|
||||||
{
|
{
|
||||||
// Item not found, very bad
|
// Item not found, very bad
|
||||||
return this.httpResponse.appendErrorToOutput(output, this.localisationService.getText("health-unable_to_find_item_to_consume", request.item));
|
return this.httpResponse.appendErrorToOutput(
|
||||||
|
output,
|
||||||
|
this.localisationService.getText("health-unable_to_find_item_to_consume", request.item),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const consumedItemMaxResource = this.itemHelper.getItem(itemToConsume._tpl)[1]._props.MaxResource;
|
const consumedItemMaxResource = this.itemHelper.getItem(itemToConsume._tpl)[1]._props.MaxResource;
|
||||||
@ -120,7 +132,7 @@ export class HealthController
|
|||||||
{
|
{
|
||||||
if (itemToConsume.upd.FoodDrink === undefined)
|
if (itemToConsume.upd.FoodDrink === undefined)
|
||||||
{
|
{
|
||||||
itemToConsume.upd.FoodDrink = { HpPercent: consumedItemMaxResource - request.count };
|
itemToConsume.upd.FoodDrink = {HpPercent: consumedItemMaxResource - request.count};
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -147,20 +159,21 @@ export class HealthController
|
|||||||
* @param sessionID Session id
|
* @param sessionID Session id
|
||||||
* @returns IItemEventRouterResponse
|
* @returns IItemEventRouterResponse
|
||||||
*/
|
*/
|
||||||
public healthTreatment(pmcData: IPmcData, healthTreatmentRequest: IHealthTreatmentRequestData, sessionID: string): IItemEventRouterResponse
|
public healthTreatment(
|
||||||
|
pmcData: IPmcData,
|
||||||
|
healthTreatmentRequest: IHealthTreatmentRequestData,
|
||||||
|
sessionID: string,
|
||||||
|
): IItemEventRouterResponse
|
||||||
{
|
{
|
||||||
let output = this.eventOutputHolder.getOutput(sessionID);
|
let output = this.eventOutputHolder.getOutput(sessionID);
|
||||||
const payMoneyRequest: IProcessBuyTradeRequestData = {
|
const payMoneyRequest: IProcessBuyTradeRequestData = {
|
||||||
Action: healthTreatmentRequest.Action,
|
Action: healthTreatmentRequest.Action,
|
||||||
tid: Traders.THERAPIST,
|
tid: Traders.THERAPIST,
|
||||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
||||||
scheme_items: healthTreatmentRequest.items,
|
scheme_items: healthTreatmentRequest.items,
|
||||||
type: "",
|
type: "",
|
||||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
||||||
item_id: "",
|
item_id: "",
|
||||||
count: 0,
|
count: 0,
|
||||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
scheme_id: 0,
|
||||||
scheme_id: 0
|
|
||||||
};
|
};
|
||||||
|
|
||||||
output = this.paymentService.payMoney(pmcData, payMoneyRequest, sessionID, output);
|
output = this.paymentService.payMoney(pmcData, payMoneyRequest, sessionID, output);
|
||||||
@ -175,7 +188,7 @@ export class HealthController
|
|||||||
const partRequest: BodyPart = healthTreatmentRequest.difference.BodyParts[bodyPartKey];
|
const partRequest: BodyPart = healthTreatmentRequest.difference.BodyParts[bodyPartKey];
|
||||||
const profilePart = pmcData.Health.BodyParts[bodyPartKey];
|
const profilePart = pmcData.Health.BodyParts[bodyPartKey];
|
||||||
|
|
||||||
// Set profile bodypart to max
|
// Set profile body part to max
|
||||||
profilePart.Health.Current = profilePart.Health.Maximum;
|
profilePart.Health.Current = profilePart.Health.Maximum;
|
||||||
|
|
||||||
// Check for effects to remove
|
// Check for effects to remove
|
||||||
@ -207,7 +220,6 @@ export class HealthController
|
|||||||
* @param info Request data
|
* @param info Request data
|
||||||
* @param sessionID
|
* @param sessionID
|
||||||
*/
|
*/
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
||||||
public applyWorkoutChanges(pmcData: IPmcData, info: IWorkoutData, sessionId: string): void
|
public applyWorkoutChanges(pmcData: IPmcData, info: IWorkoutData, sessionId: string): void
|
||||||
{
|
{
|
||||||
// https://dev.sp-tarkov.com/SPT-AKI/Server/issues/2674
|
// https://dev.sp-tarkov.com/SPT-AKI/Server/issues/2674
|
||||||
|
@ -69,7 +69,7 @@ export class HideoutController
|
|||||||
@inject("LocalisationService") protected localisationService: LocalisationService,
|
@inject("LocalisationService") protected localisationService: LocalisationService,
|
||||||
@inject("ConfigServer") protected configServer: ConfigServer,
|
@inject("ConfigServer") protected configServer: ConfigServer,
|
||||||
@inject("JsonUtil") protected jsonUtil: JsonUtil,
|
@inject("JsonUtil") protected jsonUtil: JsonUtil,
|
||||||
@inject("FenceService") protected fenceService: FenceService
|
@inject("FenceService") protected fenceService: FenceService,
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
this.hideoutConfig = this.configServer.getConfig(ConfigTypes.HIDEOUT);
|
this.hideoutConfig = this.configServer.getConfig(ConfigTypes.HIDEOUT);
|
||||||
@ -83,15 +83,19 @@ export class HideoutController
|
|||||||
* @param sessionID Session id
|
* @param sessionID Session id
|
||||||
* @returns IItemEventRouterResponse
|
* @returns IItemEventRouterResponse
|
||||||
*/
|
*/
|
||||||
public startUpgrade(pmcData: IPmcData, request: IHideoutUpgradeRequestData, sessionID: string): IItemEventRouterResponse
|
public startUpgrade(
|
||||||
|
pmcData: IPmcData,
|
||||||
|
request: IHideoutUpgradeRequestData,
|
||||||
|
sessionID: string,
|
||||||
|
): IItemEventRouterResponse
|
||||||
{
|
{
|
||||||
const output = this.eventOutputHolder.getOutput(sessionID);
|
const output = this.eventOutputHolder.getOutput(sessionID);
|
||||||
const items = request.items.map(reqItem =>
|
const items = request.items.map((reqItem) =>
|
||||||
{
|
{
|
||||||
const item = pmcData.Inventory.items.find(invItem => invItem._id === reqItem.id);
|
const item = pmcData.Inventory.items.find((invItem) => invItem._id === reqItem.id);
|
||||||
return {
|
return {
|
||||||
inventoryItem: item,
|
inventoryItem: item,
|
||||||
requestedItem: reqItem
|
requestedItem: reqItem,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -100,14 +104,18 @@ export class HideoutController
|
|||||||
{
|
{
|
||||||
if (!item.inventoryItem)
|
if (!item.inventoryItem)
|
||||||
{
|
{
|
||||||
this.logger.error(this.localisationService.getText("hideout-unable_to_find_item_in_inventory", item.requestedItem.id));
|
this.logger.error(
|
||||||
|
this.localisationService.getText("hideout-unable_to_find_item_in_inventory", item.requestedItem.id),
|
||||||
|
);
|
||||||
return this.httpResponse.appendErrorToOutput(output);
|
return this.httpResponse.appendErrorToOutput(output);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.paymentHelper.isMoneyTpl(item.inventoryItem._tpl)
|
if (
|
||||||
&& item.inventoryItem.upd
|
this.paymentHelper.isMoneyTpl(item.inventoryItem._tpl) &&
|
||||||
&& item.inventoryItem.upd.StackObjectsCount
|
item.inventoryItem.upd &&
|
||||||
&& item.inventoryItem.upd.StackObjectsCount > item.requestedItem.count)
|
item.inventoryItem.upd.StackObjectsCount &&
|
||||||
|
item.inventoryItem.upd.StackObjectsCount > item.requestedItem.count
|
||||||
|
)
|
||||||
{
|
{
|
||||||
item.inventoryItem.upd.StackObjectsCount -= item.requestedItem.count;
|
item.inventoryItem.upd.StackObjectsCount -= item.requestedItem.count;
|
||||||
}
|
}
|
||||||
@ -118,17 +126,21 @@ export class HideoutController
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Construction time management
|
// Construction time management
|
||||||
const hideoutArea = pmcData.Hideout.Areas.find(area => area.type === request.areaType);
|
const hideoutArea = pmcData.Hideout.Areas.find((area) => area.type === request.areaType);
|
||||||
if (!hideoutArea)
|
if (!hideoutArea)
|
||||||
{
|
{
|
||||||
this.logger.error(this.localisationService.getText("hideout-unable_to_find_area", request.areaType));
|
this.logger.error(this.localisationService.getText("hideout-unable_to_find_area", request.areaType));
|
||||||
return this.httpResponse.appendErrorToOutput(output);
|
return this.httpResponse.appendErrorToOutput(output);
|
||||||
}
|
}
|
||||||
|
|
||||||
const hideoutData = this.databaseServer.getTables().hideout.areas.find(area => area.type === request.areaType);
|
const hideoutData = this.databaseServer.getTables().hideout.areas.find((area) =>
|
||||||
|
area.type === request.areaType
|
||||||
|
);
|
||||||
if (!hideoutData)
|
if (!hideoutData)
|
||||||
{
|
{
|
||||||
this.logger.error(this.localisationService.getText("hideout-unable_to_find_area_in_database", request.areaType));
|
this.logger.error(
|
||||||
|
this.localisationService.getText("hideout-unable_to_find_area_in_database", request.areaType),
|
||||||
|
);
|
||||||
return this.httpResponse.appendErrorToOutput(output);
|
return this.httpResponse.appendErrorToOutput(output);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -152,12 +164,16 @@ export class HideoutController
|
|||||||
* @param sessionID Session id
|
* @param sessionID Session id
|
||||||
* @returns IItemEventRouterResponse
|
* @returns IItemEventRouterResponse
|
||||||
*/
|
*/
|
||||||
public upgradeComplete(pmcData: IPmcData, request: HideoutUpgradeCompleteRequestData, sessionID: string): IItemEventRouterResponse
|
public upgradeComplete(
|
||||||
|
pmcData: IPmcData,
|
||||||
|
request: HideoutUpgradeCompleteRequestData,
|
||||||
|
sessionID: string,
|
||||||
|
): IItemEventRouterResponse
|
||||||
{
|
{
|
||||||
const output = this.eventOutputHolder.getOutput(sessionID);
|
const output = this.eventOutputHolder.getOutput(sessionID);
|
||||||
const db = this.databaseServer.getTables();
|
const db = this.databaseServer.getTables();
|
||||||
|
|
||||||
const profileHideoutArea = pmcData.Hideout.Areas.find(area => area.type === request.areaType);
|
const profileHideoutArea = pmcData.Hideout.Areas.find((area) => area.type === request.areaType);
|
||||||
if (!profileHideoutArea)
|
if (!profileHideoutArea)
|
||||||
{
|
{
|
||||||
this.logger.error(this.localisationService.getText("hideout-unable_to_find_area", request.areaType));
|
this.logger.error(this.localisationService.getText("hideout-unable_to_find_area", request.areaType));
|
||||||
@ -169,10 +185,12 @@ export class HideoutController
|
|||||||
profileHideoutArea.completeTime = 0;
|
profileHideoutArea.completeTime = 0;
|
||||||
profileHideoutArea.constructing = false;
|
profileHideoutArea.constructing = false;
|
||||||
|
|
||||||
const hideoutData = db.hideout.areas.find(area => area.type === profileHideoutArea.type);
|
const hideoutData = db.hideout.areas.find((area) => area.type === profileHideoutArea.type);
|
||||||
if (!hideoutData)
|
if (!hideoutData)
|
||||||
{
|
{
|
||||||
this.logger.error(this.localisationService.getText("hideout-unable_to_find_area_in_database", request.areaType));
|
this.logger.error(
|
||||||
|
this.localisationService.getText("hideout-unable_to_find_area_in_database", request.areaType),
|
||||||
|
);
|
||||||
return this.httpResponse.appendErrorToOutput(output);
|
return this.httpResponse.appendErrorToOutput(output);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -190,17 +208,31 @@ export class HideoutController
|
|||||||
// Upgrade includes a container improvement/addition
|
// Upgrade includes a container improvement/addition
|
||||||
if (hideoutStage?.container)
|
if (hideoutStage?.container)
|
||||||
{
|
{
|
||||||
this.addContainerImprovementToProfile(output, sessionID, pmcData, profileHideoutArea, hideoutData, hideoutStage);
|
this.addContainerImprovementToProfile(
|
||||||
|
output,
|
||||||
|
sessionID,
|
||||||
|
pmcData,
|
||||||
|
profileHideoutArea,
|
||||||
|
hideoutData,
|
||||||
|
hideoutStage,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Upgrading water collector / med station
|
// Upgrading water collector / med station
|
||||||
if (profileHideoutArea.type === HideoutAreas.WATER_COLLECTOR || profileHideoutArea.type === HideoutAreas.MEDSTATION)
|
if (
|
||||||
|
profileHideoutArea.type === HideoutAreas.WATER_COLLECTOR ||
|
||||||
|
profileHideoutArea.type === HideoutAreas.MEDSTATION
|
||||||
|
)
|
||||||
{
|
{
|
||||||
this.checkAndUpgradeWall(pmcData);
|
this.checkAndUpgradeWall(pmcData);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add Skill Points Per Area Upgrade
|
// Add Skill Points Per Area Upgrade
|
||||||
this.profileHelper.addSkillPointsToPlayer(pmcData, SkillTypes.HIDEOUT_MANAGEMENT, db.globals.config.SkillsSettings.HideoutManagement.SkillPointsPerAreaUpgrade);
|
this.profileHelper.addSkillPointsToPlayer(
|
||||||
|
pmcData,
|
||||||
|
SkillTypes.HIDEOUT_MANAGEMENT,
|
||||||
|
db.globals.config.SkillsSettings.HideoutManagement.SkillPointsPerAreaUpgrade,
|
||||||
|
);
|
||||||
|
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
@ -211,11 +243,11 @@ export class HideoutController
|
|||||||
*/
|
*/
|
||||||
protected checkAndUpgradeWall(pmcData: IPmcData): void
|
protected checkAndUpgradeWall(pmcData: IPmcData): void
|
||||||
{
|
{
|
||||||
const medStation = pmcData.Hideout.Areas.find(area => area.type === HideoutAreas.MEDSTATION);
|
const medStation = pmcData.Hideout.Areas.find((area) => area.type === HideoutAreas.MEDSTATION);
|
||||||
const waterCollector = pmcData.Hideout.Areas.find(area => area.type === HideoutAreas.WATER_COLLECTOR);
|
const waterCollector = pmcData.Hideout.Areas.find((area) => area.type === HideoutAreas.WATER_COLLECTOR);
|
||||||
if (medStation?.level >= 1 && waterCollector?.level >= 1)
|
if (medStation?.level >= 1 && waterCollector?.level >= 1)
|
||||||
{
|
{
|
||||||
const wall = pmcData.Hideout.Areas.find(area => area.type === HideoutAreas.EMERGENCY_WALL);
|
const wall = pmcData.Hideout.Areas.find((area) => area.type === HideoutAreas.EMERGENCY_WALL);
|
||||||
if (wall?.level === 0)
|
if (wall?.level === 0)
|
||||||
{
|
{
|
||||||
wall.level = 3;
|
wall.level = 3;
|
||||||
@ -224,7 +256,6 @@ export class HideoutController
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
|
||||||
* @param pmcData Profile to edit
|
* @param pmcData Profile to edit
|
||||||
* @param output Object to send back to client
|
* @param output Object to send back to client
|
||||||
* @param sessionID Session/player id
|
* @param sessionID Session/player id
|
||||||
@ -232,7 +263,14 @@ export class HideoutController
|
|||||||
* @param dbHideoutArea Hideout area being upgraded
|
* @param dbHideoutArea Hideout area being upgraded
|
||||||
* @param hideoutStage Stage hideout area is being upgraded to
|
* @param hideoutStage Stage hideout area is being upgraded to
|
||||||
*/
|
*/
|
||||||
protected addContainerImprovementToProfile(output: IItemEventRouterResponse, sessionID: string, pmcData: IPmcData, profileParentHideoutArea: HideoutArea, dbHideoutArea: IHideoutArea, hideoutStage: Stage): void
|
protected addContainerImprovementToProfile(
|
||||||
|
output: IItemEventRouterResponse,
|
||||||
|
sessionID: string,
|
||||||
|
pmcData: IPmcData,
|
||||||
|
profileParentHideoutArea: HideoutArea,
|
||||||
|
dbHideoutArea: IHideoutArea,
|
||||||
|
hideoutStage: Stage,
|
||||||
|
): void
|
||||||
{
|
{
|
||||||
// Add key/value to `hideoutAreaStashes` dictionary - used to link hideout area to inventory stash by its id
|
// Add key/value to `hideoutAreaStashes` dictionary - used to link hideout area to inventory stash by its id
|
||||||
if (!pmcData.Inventory.hideoutAreaStashes[dbHideoutArea.type])
|
if (!pmcData.Inventory.hideoutAreaStashes[dbHideoutArea.type])
|
||||||
@ -247,7 +285,9 @@ export class HideoutController
|
|||||||
this.addContainerUpgradeToClientOutput(output, sessionID, dbHideoutArea.type, dbHideoutArea, hideoutStage);
|
this.addContainerUpgradeToClientOutput(output, sessionID, dbHideoutArea.type, dbHideoutArea, hideoutStage);
|
||||||
|
|
||||||
// Some areas like gun stand have a child area linked to it, it needs to do the same as above
|
// Some areas like gun stand have a child area linked to it, it needs to do the same as above
|
||||||
const childDbArea = this.databaseServer.getTables().hideout.areas.find(x => x.parentArea === dbHideoutArea._id);
|
const childDbArea = this.databaseServer.getTables().hideout.areas.find((x) =>
|
||||||
|
x.parentArea === dbHideoutArea._id
|
||||||
|
);
|
||||||
if (childDbArea)
|
if (childDbArea)
|
||||||
{
|
{
|
||||||
// Add key/value to `hideoutAreaStashes` dictionary - used to link hideout area to inventory stash by its id
|
// Add key/value to `hideoutAreaStashes` dictionary - used to link hideout area to inventory stash by its id
|
||||||
@ -257,7 +297,9 @@ export class HideoutController
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Set child area level to same as parent area
|
// Set child area level to same as parent area
|
||||||
pmcData.Hideout.Areas.find(x => x.type === childDbArea.type).level = pmcData.Hideout.Areas.find(x => x.type === profileParentHideoutArea.type).level;
|
pmcData.Hideout.Areas.find((x) => x.type === childDbArea.type).level = pmcData.Hideout.Areas.find((x) =>
|
||||||
|
x.type === profileParentHideoutArea.type
|
||||||
|
).level;
|
||||||
|
|
||||||
// Add/upgrade stash item in player inventory
|
// Add/upgrade stash item in player inventory
|
||||||
const childDbAreaStage = childDbArea.stages[profileParentHideoutArea.level];
|
const childDbAreaStage = childDbArea.stages[profileParentHideoutArea.level];
|
||||||
@ -276,38 +318,41 @@ export class HideoutController
|
|||||||
*/
|
*/
|
||||||
protected addUpdateInventoryItemToProfile(pmcData: IPmcData, dbHideoutData: IHideoutArea, hideoutStage: Stage): void
|
protected addUpdateInventoryItemToProfile(pmcData: IPmcData, dbHideoutData: IHideoutArea, hideoutStage: Stage): void
|
||||||
{
|
{
|
||||||
const existingInventoryItem = pmcData.Inventory.items.find(x => x._id === dbHideoutData._id);
|
const existingInventoryItem = pmcData.Inventory.items.find((x) => x._id === dbHideoutData._id);
|
||||||
if (existingInventoryItem)
|
if (existingInventoryItem)
|
||||||
{
|
{
|
||||||
// Update existing items container tpl to point to new id (tpl)
|
// Update existing items container tpl to point to new id (tpl)
|
||||||
existingInventoryItem._tpl = hideoutStage.container;
|
existingInventoryItem._tpl = hideoutStage.container;
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add new item as none exists
|
// Add new item as none exists
|
||||||
pmcData.Inventory.items.push({ _id: dbHideoutData._id, _tpl: hideoutStage.container });
|
pmcData.Inventory.items.push({_id: dbHideoutData._id, _tpl: hideoutStage.container});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
|
||||||
* @param output Objet to send to client
|
* @param output Objet to send to client
|
||||||
* @param sessionID Session/player id
|
* @param sessionID Session/player id
|
||||||
* @param areaType Hideout area that had stash added
|
* @param areaType Hideout area that had stash added
|
||||||
* @param hideoutDbData Hideout area that caused addition of stash
|
* @param hideoutDbData Hideout area that caused addition of stash
|
||||||
* @param hideoutStage Hideout area upgraded to this
|
* @param hideoutStage Hideout area upgraded to this
|
||||||
*/
|
*/
|
||||||
protected addContainerUpgradeToClientOutput(output: IItemEventRouterResponse, sessionID: string, areaType: HideoutAreas, hideoutDbData: IHideoutArea, hideoutStage: Stage): void
|
protected addContainerUpgradeToClientOutput(
|
||||||
|
output: IItemEventRouterResponse,
|
||||||
|
sessionID: string,
|
||||||
|
areaType: HideoutAreas,
|
||||||
|
hideoutDbData: IHideoutArea,
|
||||||
|
hideoutStage: Stage,
|
||||||
|
): void
|
||||||
{
|
{
|
||||||
if (!output.profileChanges[sessionID].changedHideoutStashes)
|
if (!output.profileChanges[sessionID].changedHideoutStashes)
|
||||||
{
|
{
|
||||||
output.profileChanges[sessionID].changedHideoutStashes = {};
|
output.profileChanges[sessionID].changedHideoutStashes = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
output.profileChanges[sessionID].changedHideoutStashes[areaType] =
|
output.profileChanges[sessionID].changedHideoutStashes[areaType] = {
|
||||||
{
|
|
||||||
Id: hideoutDbData._id,
|
Id: hideoutDbData._id,
|
||||||
Tpl: hideoutStage.container
|
Tpl: hideoutStage.container,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -315,28 +360,37 @@ export class HideoutController
|
|||||||
* Handle HideoutPutItemsInAreaSlots
|
* Handle HideoutPutItemsInAreaSlots
|
||||||
* Create item in hideout slot item array, remove item from player inventory
|
* Create item in hideout slot item array, remove item from player inventory
|
||||||
* @param pmcData Profile data
|
* @param pmcData Profile data
|
||||||
* @param addItemToHideoutRequest reqeust from client to place item in area slot
|
* @param addItemToHideoutRequest request from client to place item in area slot
|
||||||
* @param sessionID Session id
|
* @param sessionID Session id
|
||||||
* @returns IItemEventRouterResponse object
|
* @returns IItemEventRouterResponse object
|
||||||
*/
|
*/
|
||||||
public putItemsInAreaSlots(pmcData: IPmcData, addItemToHideoutRequest: IHideoutPutItemInRequestData, sessionID: string): IItemEventRouterResponse
|
public putItemsInAreaSlots(
|
||||||
|
pmcData: IPmcData,
|
||||||
|
addItemToHideoutRequest: IHideoutPutItemInRequestData,
|
||||||
|
sessionID: string,
|
||||||
|
): IItemEventRouterResponse
|
||||||
{
|
{
|
||||||
let output = this.eventOutputHolder.getOutput(sessionID);
|
let output = this.eventOutputHolder.getOutput(sessionID);
|
||||||
|
|
||||||
const itemsToAdd = Object.entries(addItemToHideoutRequest.items).map(kvp =>
|
const itemsToAdd = Object.entries(addItemToHideoutRequest.items).map((kvp) =>
|
||||||
{
|
{
|
||||||
const item = pmcData.Inventory.items.find(invItem => invItem._id === kvp[1]["id"]);
|
const item = pmcData.Inventory.items.find((invItem) => invItem._id === kvp[1]["id"]);
|
||||||
return {
|
return {
|
||||||
inventoryItem: item,
|
inventoryItem: item,
|
||||||
requestedItem: kvp[1],
|
requestedItem: kvp[1],
|
||||||
slot: kvp[0]
|
slot: kvp[0],
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
const hideoutArea = pmcData.Hideout.Areas.find(area => area.type === addItemToHideoutRequest.areaType);
|
const hideoutArea = pmcData.Hideout.Areas.find((area) => area.type === addItemToHideoutRequest.areaType);
|
||||||
if (!hideoutArea)
|
if (!hideoutArea)
|
||||||
{
|
{
|
||||||
this.logger.error(this.localisationService.getText("hideout-unable_to_find_area_in_database", addItemToHideoutRequest.areaType));
|
this.logger.error(
|
||||||
|
this.localisationService.getText(
|
||||||
|
"hideout-unable_to_find_area_in_database",
|
||||||
|
addItemToHideoutRequest.areaType,
|
||||||
|
),
|
||||||
|
);
|
||||||
return this.httpResponse.appendErrorToOutput(output);
|
return this.httpResponse.appendErrorToOutput(output);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -344,17 +398,22 @@ export class HideoutController
|
|||||||
{
|
{
|
||||||
if (!item.inventoryItem)
|
if (!item.inventoryItem)
|
||||||
{
|
{
|
||||||
this.logger.error(this.localisationService.getText("hideout-unable_to_find_item_in_inventory", {itemId: item.requestedItem["id"], area: hideoutArea.type}));
|
this.logger.error(
|
||||||
|
this.localisationService.getText("hideout-unable_to_find_item_in_inventory", {
|
||||||
|
itemId: item.requestedItem["id"],
|
||||||
|
area: hideoutArea.type,
|
||||||
|
}),
|
||||||
|
);
|
||||||
return this.httpResponse.appendErrorToOutput(output);
|
return this.httpResponse.appendErrorToOutput(output);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add item to area.slots
|
// Add item to area.slots
|
||||||
const destinationLocationIndex = Number(item.slot);
|
const destinationLocationIndex = Number(item.slot);
|
||||||
const hideoutSlotIndex = hideoutArea.slots.findIndex(x => x.locationIndex === destinationLocationIndex);
|
const hideoutSlotIndex = hideoutArea.slots.findIndex((x) => x.locationIndex === destinationLocationIndex);
|
||||||
hideoutArea.slots[hideoutSlotIndex].item = [{
|
hideoutArea.slots[hideoutSlotIndex].item = [{
|
||||||
_id: item.inventoryItem._id,
|
_id: item.inventoryItem._id,
|
||||||
_tpl: item.inventoryItem._tpl,
|
_tpl: item.inventoryItem._tpl,
|
||||||
upd: item.inventoryItem.upd
|
upd: item.inventoryItem.upd,
|
||||||
}];
|
}];
|
||||||
|
|
||||||
output = this.inventoryHelper.removeItem(pmcData, item.inventoryItem._id, sessionID, output);
|
output = this.inventoryHelper.removeItem(pmcData, item.inventoryItem._id, sessionID, output);
|
||||||
@ -374,11 +433,15 @@ export class HideoutController
|
|||||||
* @param sessionID Session id
|
* @param sessionID Session id
|
||||||
* @returns IItemEventRouterResponse
|
* @returns IItemEventRouterResponse
|
||||||
*/
|
*/
|
||||||
public takeItemsFromAreaSlots(pmcData: IPmcData, request: IHideoutTakeItemOutRequestData, sessionID: string): IItemEventRouterResponse
|
public takeItemsFromAreaSlots(
|
||||||
|
pmcData: IPmcData,
|
||||||
|
request: IHideoutTakeItemOutRequestData,
|
||||||
|
sessionID: string,
|
||||||
|
): IItemEventRouterResponse
|
||||||
{
|
{
|
||||||
const output = this.eventOutputHolder.getOutput(sessionID);
|
const output = this.eventOutputHolder.getOutput(sessionID);
|
||||||
|
|
||||||
const hideoutArea = pmcData.Hideout.Areas.find(area => area.type === request.areaType);
|
const hideoutArea = pmcData.Hideout.Areas.find((area) => area.type === request.areaType);
|
||||||
if (!hideoutArea)
|
if (!hideoutArea)
|
||||||
{
|
{
|
||||||
this.logger.error(this.localisationService.getText("hideout-unable_to_find_area", request.areaType));
|
this.logger.error(this.localisationService.getText("hideout-unable_to_find_area", request.areaType));
|
||||||
@ -387,19 +450,30 @@ export class HideoutController
|
|||||||
|
|
||||||
if (!hideoutArea.slots || hideoutArea.slots.length === 0)
|
if (!hideoutArea.slots || hideoutArea.slots.length === 0)
|
||||||
{
|
{
|
||||||
this.logger.error(this.localisationService.getText("hideout-unable_to_find_item_to_remove_from_area", hideoutArea.type));
|
this.logger.error(
|
||||||
|
this.localisationService.getText("hideout-unable_to_find_item_to_remove_from_area", hideoutArea.type),
|
||||||
|
);
|
||||||
return this.httpResponse.appendErrorToOutput(output);
|
return this.httpResponse.appendErrorToOutput(output);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle areas that have resources that can be placed in/taken out of slots from the area
|
// Handle areas that have resources that can be placed in/taken out of slots from the area
|
||||||
if ([HideoutAreas.AIR_FILTERING, HideoutAreas.WATER_COLLECTOR, HideoutAreas.GENERATOR, HideoutAreas.BITCOIN_FARM].includes(hideoutArea.type))
|
if (
|
||||||
|
[
|
||||||
|
HideoutAreas.AIR_FILTERING,
|
||||||
|
HideoutAreas.WATER_COLLECTOR,
|
||||||
|
HideoutAreas.GENERATOR,
|
||||||
|
HideoutAreas.BITCOIN_FARM,
|
||||||
|
].includes(hideoutArea.type)
|
||||||
|
)
|
||||||
{
|
{
|
||||||
const response = this.removeResourceFromArea(sessionID, pmcData, request, output, hideoutArea);
|
const response = this.removeResourceFromArea(sessionID, pmcData, request, output, hideoutArea);
|
||||||
this.update();
|
this.update();
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new Error(this.localisationService.getText("hideout-unhandled_remove_item_from_area_request", hideoutArea.type));
|
throw new Error(
|
||||||
|
this.localisationService.getText("hideout-unhandled_remove_item_from_area_request", hideoutArea.type),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -411,22 +485,36 @@ export class HideoutController
|
|||||||
* @param hideoutArea Area fuel is being removed from
|
* @param hideoutArea Area fuel is being removed from
|
||||||
* @returns IItemEventRouterResponse response
|
* @returns IItemEventRouterResponse response
|
||||||
*/
|
*/
|
||||||
protected removeResourceFromArea(sessionID: string, pmcData: IPmcData, removeResourceRequest: IHideoutTakeItemOutRequestData, output: IItemEventRouterResponse, hideoutArea: HideoutArea): IItemEventRouterResponse
|
protected removeResourceFromArea(
|
||||||
|
sessionID: string,
|
||||||
|
pmcData: IPmcData,
|
||||||
|
removeResourceRequest: IHideoutTakeItemOutRequestData,
|
||||||
|
output: IItemEventRouterResponse,
|
||||||
|
hideoutArea: HideoutArea,
|
||||||
|
): IItemEventRouterResponse
|
||||||
{
|
{
|
||||||
const slotIndexToRemove = removeResourceRequest.slots[0];
|
const slotIndexToRemove = removeResourceRequest.slots[0];
|
||||||
|
|
||||||
const itemToReturn = hideoutArea.slots.find(x => x.locationIndex === slotIndexToRemove).item[0];
|
const itemToReturn = hideoutArea.slots.find((x) => x.locationIndex === slotIndexToRemove).item[0];
|
||||||
|
|
||||||
const newReq = {
|
const newReq = {
|
||||||
items: [{
|
items: [{
|
||||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||||
item_id: itemToReturn._tpl,
|
item_id: itemToReturn._tpl,
|
||||||
count: 1
|
count: 1,
|
||||||
}],
|
}],
|
||||||
tid: "ragfair"
|
tid: "ragfair",
|
||||||
};
|
};
|
||||||
|
|
||||||
output = this.inventoryHelper.addItem(pmcData, newReq, output, sessionID, null, !!itemToReturn.upd.SpawnedInSession, itemToReturn.upd);
|
output = this.inventoryHelper.addItem(
|
||||||
|
pmcData,
|
||||||
|
newReq,
|
||||||
|
output,
|
||||||
|
sessionID,
|
||||||
|
null,
|
||||||
|
!!itemToReturn.upd.SpawnedInSession,
|
||||||
|
itemToReturn.upd,
|
||||||
|
);
|
||||||
|
|
||||||
// If addItem returned with errors, drop out
|
// If addItem returned with errors, drop out
|
||||||
if (output.warnings && output.warnings.length > 0)
|
if (output.warnings && output.warnings.length > 0)
|
||||||
@ -435,7 +523,7 @@ export class HideoutController
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Remove items from slot, locationIndex remains
|
// Remove items from slot, locationIndex remains
|
||||||
const hideoutSlotIndex = hideoutArea.slots.findIndex(x => x.locationIndex === slotIndexToRemove);
|
const hideoutSlotIndex = hideoutArea.slots.findIndex((x) => x.locationIndex === slotIndexToRemove);
|
||||||
hideoutArea.slots[hideoutSlotIndex].item = undefined;
|
hideoutArea.slots[hideoutSlotIndex].item = undefined;
|
||||||
|
|
||||||
return output;
|
return output;
|
||||||
@ -449,14 +537,18 @@ export class HideoutController
|
|||||||
* @param sessionID Session id
|
* @param sessionID Session id
|
||||||
* @returns IItemEventRouterResponse
|
* @returns IItemEventRouterResponse
|
||||||
*/
|
*/
|
||||||
public toggleArea(pmcData: IPmcData, request: IHideoutToggleAreaRequestData, sessionID: string): IItemEventRouterResponse
|
public toggleArea(
|
||||||
|
pmcData: IPmcData,
|
||||||
|
request: IHideoutToggleAreaRequestData,
|
||||||
|
sessionID: string,
|
||||||
|
): IItemEventRouterResponse
|
||||||
{
|
{
|
||||||
const output = this.eventOutputHolder.getOutput(sessionID);
|
const output = this.eventOutputHolder.getOutput(sessionID);
|
||||||
|
|
||||||
// Force a production update (occur before area is toggled as it could be generator and doing it after generator enabled would cause incorrect calculaton of production progress)
|
// Force a production update (occur before area is toggled as it could be generator and doing it after generator enabled would cause incorrect calculaton of production progress)
|
||||||
this.hideoutHelper.updatePlayerHideout(sessionID);
|
this.hideoutHelper.updatePlayerHideout(sessionID);
|
||||||
|
|
||||||
const hideoutArea = pmcData.Hideout.Areas.find(area => area.type === request.areaType);
|
const hideoutArea = pmcData.Hideout.Areas.find((area) => area.type === request.areaType);
|
||||||
if (!hideoutArea)
|
if (!hideoutArea)
|
||||||
{
|
{
|
||||||
this.logger.error(this.localisationService.getText("hideout-unable_to_find_area", request.areaType));
|
this.logger.error(this.localisationService.getText("hideout-unable_to_find_area", request.areaType));
|
||||||
@ -472,27 +564,31 @@ export class HideoutController
|
|||||||
* Handle HideoutSingleProductionStart event
|
* Handle HideoutSingleProductionStart event
|
||||||
* Start production for an item from hideout area
|
* Start production for an item from hideout area
|
||||||
* @param pmcData Player profile
|
* @param pmcData Player profile
|
||||||
* @param body Start prodution of single item request
|
* @param body Start production of single item request
|
||||||
* @param sessionID Session id
|
* @param sessionID Session id
|
||||||
* @returns IItemEventRouterResponse
|
* @returns IItemEventRouterResponse
|
||||||
*/
|
*/
|
||||||
public singleProductionStart(pmcData: IPmcData, body: IHideoutSingleProductionStartRequestData, sessionID: string): IItemEventRouterResponse
|
public singleProductionStart(
|
||||||
|
pmcData: IPmcData,
|
||||||
|
body: IHideoutSingleProductionStartRequestData,
|
||||||
|
sessionID: string,
|
||||||
|
): IItemEventRouterResponse
|
||||||
{
|
{
|
||||||
// Start production
|
// Start production
|
||||||
this.registerProduction(pmcData, body, sessionID);
|
this.registerProduction(pmcData, body, sessionID);
|
||||||
|
|
||||||
// Find the recipe of the production
|
// Find the recipe of the production
|
||||||
const recipe = this.databaseServer.getTables().hideout.production.find(p => p._id === body.recipeId);
|
const recipe = this.databaseServer.getTables().hideout.production.find((p) => p._id === body.recipeId);
|
||||||
|
|
||||||
// Find the actual amount of items we need to remove because body can send weird data
|
// Find the actual amount of items we need to remove because body can send weird data
|
||||||
const requirements = this.jsonUtil.clone(recipe.requirements.filter(i => i.type === "Item"));
|
const requirements = this.jsonUtil.clone(recipe.requirements.filter((i) => i.type === "Item"));
|
||||||
|
|
||||||
const output = this.eventOutputHolder.getOutput(sessionID);
|
const output = this.eventOutputHolder.getOutput(sessionID);
|
||||||
|
|
||||||
for (const itemToDelete of body.items)
|
for (const itemToDelete of body.items)
|
||||||
{
|
{
|
||||||
const itemToCheck = pmcData.Inventory.items.find(i => i._id === itemToDelete.id);
|
const itemToCheck = pmcData.Inventory.items.find((i) => i._id === itemToDelete.id);
|
||||||
const requirement = requirements.find(requirement => requirement.templateId === itemToCheck._tpl);
|
const requirement = requirements.find((requirement) => requirement.templateId === itemToCheck._tpl);
|
||||||
if (requirement.count <= 0)
|
if (requirement.count <= 0)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
@ -513,21 +609,32 @@ export class HideoutController
|
|||||||
* @param sessionID session id
|
* @param sessionID session id
|
||||||
* @returns item event router response
|
* @returns item event router response
|
||||||
*/
|
*/
|
||||||
public scavCaseProductionStart(pmcData: IPmcData, body: IHideoutScavCaseStartRequestData, sessionID: string): IItemEventRouterResponse
|
public scavCaseProductionStart(
|
||||||
|
pmcData: IPmcData,
|
||||||
|
body: IHideoutScavCaseStartRequestData,
|
||||||
|
sessionID: string,
|
||||||
|
): IItemEventRouterResponse
|
||||||
{
|
{
|
||||||
let output = this.eventOutputHolder.getOutput(sessionID);
|
let output = this.eventOutputHolder.getOutput(sessionID);
|
||||||
|
|
||||||
for (const requestedItem of body.items)
|
for (const requestedItem of body.items)
|
||||||
{
|
{
|
||||||
const inventoryItem = pmcData.Inventory.items.find(item => item._id === requestedItem.id);
|
const inventoryItem = pmcData.Inventory.items.find((item) => item._id === requestedItem.id);
|
||||||
if (!inventoryItem)
|
if (!inventoryItem)
|
||||||
{
|
{
|
||||||
this.logger.error(this.localisationService.getText("hideout-unable_to_find_scavcase_requested_item_in_profile_inventory", requestedItem.id));
|
this.logger.error(
|
||||||
|
this.localisationService.getText(
|
||||||
|
"hideout-unable_to_find_scavcase_requested_item_in_profile_inventory",
|
||||||
|
requestedItem.id,
|
||||||
|
),
|
||||||
|
);
|
||||||
return this.httpResponse.appendErrorToOutput(output);
|
return this.httpResponse.appendErrorToOutput(output);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (inventoryItem.upd?.StackObjectsCount
|
if (
|
||||||
&& inventoryItem.upd.StackObjectsCount > requestedItem.count)
|
inventoryItem.upd?.StackObjectsCount &&
|
||||||
|
inventoryItem.upd.StackObjectsCount > requestedItem.count
|
||||||
|
)
|
||||||
{
|
{
|
||||||
inventoryItem.upd.StackObjectsCount -= requestedItem.count;
|
inventoryItem.upd.StackObjectsCount -= requestedItem.count;
|
||||||
}
|
}
|
||||||
@ -537,10 +644,12 @@ export class HideoutController
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const recipe = this.databaseServer.getTables().hideout.scavcase.find(r => r._id === body.recipeId);
|
const recipe = this.databaseServer.getTables().hideout.scavcase.find((r) => r._id === body.recipeId);
|
||||||
if (!recipe)
|
if (!recipe)
|
||||||
{
|
{
|
||||||
this.logger.error(this.localisationService.getText("hideout-unable_to_find_scav_case_recipie_in_database", body.recipeId));
|
this.logger.error(
|
||||||
|
this.localisationService.getText("hideout-unable_to_find_scav_case_recipie_in_database", body.recipeId),
|
||||||
|
);
|
||||||
return this.httpResponse.appendErrorToOutput(output);
|
return this.httpResponse.appendErrorToOutput(output);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -549,7 +658,11 @@ export class HideoutController
|
|||||||
// - scav case recipe: Production time value is stored in attribute "ProductionType" with capital "P"
|
// - scav case recipe: Production time value is stored in attribute "ProductionType" with capital "P"
|
||||||
const modifiedScavCaseTime = this.getScavCaseTime(pmcData, recipe.ProductionTime);
|
const modifiedScavCaseTime = this.getScavCaseTime(pmcData, recipe.ProductionTime);
|
||||||
|
|
||||||
pmcData.Hideout.Production[body.recipeId] = this.hideoutHelper.initProduction(body.recipeId, modifiedScavCaseTime, false);
|
pmcData.Hideout.Production[body.recipeId] = this.hideoutHelper.initProduction(
|
||||||
|
body.recipeId,
|
||||||
|
modifiedScavCaseTime,
|
||||||
|
false,
|
||||||
|
);
|
||||||
pmcData.Hideout.Production[body.recipeId].sptIsScavCase = true;
|
pmcData.Hideout.Production[body.recipeId].sptIsScavCase = true;
|
||||||
|
|
||||||
return output;
|
return output;
|
||||||
@ -569,7 +682,6 @@ export class HideoutController
|
|||||||
{
|
{
|
||||||
return productionTime;
|
return productionTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
return productionTime * fenceLevel.ScavCaseTimeModifier;
|
return productionTime * fenceLevel.ScavCaseTimeModifier;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -582,21 +694,24 @@ export class HideoutController
|
|||||||
protected addScavCaseRewardsToProfile(pmcData: IPmcData, rewards: Product[], recipeId: string): void
|
protected addScavCaseRewardsToProfile(pmcData: IPmcData, rewards: Product[], recipeId: string): void
|
||||||
{
|
{
|
||||||
pmcData.Hideout.Production[`ScavCase${recipeId}`] = {
|
pmcData.Hideout.Production[`ScavCase${recipeId}`] = {
|
||||||
Products: rewards
|
Products: rewards,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Start production of continuously created item
|
* Start production of continuously created item
|
||||||
* @param pmcData Player profile
|
* @param pmcData Player profile
|
||||||
* @param request Continious production request
|
* @param request Continuous production request
|
||||||
* @param sessionID Session id
|
* @param sessionID Session id
|
||||||
* @returns IItemEventRouterResponse
|
* @returns IItemEventRouterResponse
|
||||||
*/
|
*/
|
||||||
public continuousProductionStart(pmcData: IPmcData, request: IHideoutContinuousProductionStartRequestData, sessionID: string): IItemEventRouterResponse
|
public continuousProductionStart(
|
||||||
|
pmcData: IPmcData,
|
||||||
|
request: IHideoutContinuousProductionStartRequestData,
|
||||||
|
sessionID: string,
|
||||||
|
): IItemEventRouterResponse
|
||||||
{
|
{
|
||||||
this.registerProduction(pmcData, request, sessionID);
|
this.registerProduction(pmcData, request, sessionID);
|
||||||
|
|
||||||
return this.eventOutputHolder.getOutput(sessionID);
|
return this.eventOutputHolder.getOutput(sessionID);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -608,7 +723,11 @@ export class HideoutController
|
|||||||
* @param sessionID Session id
|
* @param sessionID Session id
|
||||||
* @returns IItemEventRouterResponse
|
* @returns IItemEventRouterResponse
|
||||||
*/
|
*/
|
||||||
public takeProduction(pmcData: IPmcData, request: IHideoutTakeProductionRequestData, sessionID: string): IItemEventRouterResponse
|
public takeProduction(
|
||||||
|
pmcData: IPmcData,
|
||||||
|
request: IHideoutTakeProductionRequestData,
|
||||||
|
sessionID: string,
|
||||||
|
): IItemEventRouterResponse
|
||||||
{
|
{
|
||||||
const output = this.eventOutputHolder.getOutput(sessionID);
|
const output = this.eventOutputHolder.getOutput(sessionID);
|
||||||
|
|
||||||
@ -617,19 +736,24 @@ export class HideoutController
|
|||||||
return this.hideoutHelper.getBTC(pmcData, request, sessionID);
|
return this.hideoutHelper.getBTC(pmcData, request, sessionID);
|
||||||
}
|
}
|
||||||
|
|
||||||
const recipe = this.databaseServer.getTables().hideout.production.find(r => r._id === request.recipeId);
|
const recipe = this.databaseServer.getTables().hideout.production.find((r) => r._id === request.recipeId);
|
||||||
if (recipe)
|
if (recipe)
|
||||||
{
|
{
|
||||||
return this.handleRecipe(sessionID, recipe, pmcData, request, output);
|
return this.handleRecipe(sessionID, recipe, pmcData, request, output);
|
||||||
}
|
}
|
||||||
|
|
||||||
const scavCase = this.databaseServer.getTables().hideout.scavcase.find(r => r._id === request.recipeId);
|
const scavCase = this.databaseServer.getTables().hideout.scavcase.find((r) => r._id === request.recipeId);
|
||||||
if (scavCase)
|
if (scavCase)
|
||||||
{
|
{
|
||||||
return this.handleScavCase(sessionID, pmcData, request, output);
|
return this.handleScavCase(sessionID, pmcData, request, output);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.logger.error(this.localisationService.getText("hideout-unable_to_find_production_in_profile_by_recipie_id", request.recipeId));
|
this.logger.error(
|
||||||
|
this.localisationService.getText(
|
||||||
|
"hideout-unable_to_find_production_in_profile_by_recipie_id",
|
||||||
|
request.recipeId,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
return this.httpResponse.appendErrorToOutput(output);
|
return this.httpResponse.appendErrorToOutput(output);
|
||||||
}
|
}
|
||||||
@ -643,9 +767,15 @@ export class HideoutController
|
|||||||
* @param output Output object to update
|
* @param output Output object to update
|
||||||
* @returns IItemEventRouterResponse
|
* @returns IItemEventRouterResponse
|
||||||
*/
|
*/
|
||||||
protected handleRecipe(sessionID: string, recipe: IHideoutProduction, pmcData: IPmcData, request: IHideoutTakeProductionRequestData, output: IItemEventRouterResponse): IItemEventRouterResponse
|
protected handleRecipe(
|
||||||
|
sessionID: string,
|
||||||
|
recipe: IHideoutProduction,
|
||||||
|
pmcData: IPmcData,
|
||||||
|
request: IHideoutTakeProductionRequestData,
|
||||||
|
output: IItemEventRouterResponse,
|
||||||
|
): IItemEventRouterResponse
|
||||||
{
|
{
|
||||||
// Variables for managemnet of skill
|
// Variables for management of skill
|
||||||
let craftingExpAmount = 0;
|
let craftingExpAmount = 0;
|
||||||
|
|
||||||
// ? move the logic of BackendCounters in new method?
|
// ? move the logic of BackendCounters in new method?
|
||||||
@ -654,13 +784,12 @@ export class HideoutController
|
|||||||
{
|
{
|
||||||
pmcData.BackendCounters[HideoutController.nameBackendCountersCrafting] = {
|
pmcData.BackendCounters[HideoutController.nameBackendCountersCrafting] = {
|
||||||
id: HideoutController.nameBackendCountersCrafting,
|
id: HideoutController.nameBackendCountersCrafting,
|
||||||
value: 0
|
value: 0,
|
||||||
};
|
};
|
||||||
counterHoursCrafting = pmcData.BackendCounters[HideoutController.nameBackendCountersCrafting];
|
counterHoursCrafting = pmcData.BackendCounters[HideoutController.nameBackendCountersCrafting];
|
||||||
}
|
}
|
||||||
let hoursCrafting = counterHoursCrafting.value;
|
let hoursCrafting = counterHoursCrafting.value;
|
||||||
|
|
||||||
|
|
||||||
// create item and throw it into profile
|
// create item and throw it into profile
|
||||||
let id = recipe.endProduct;
|
let id = recipe.endProduct;
|
||||||
|
|
||||||
@ -672,18 +801,18 @@ export class HideoutController
|
|||||||
|
|
||||||
const newReq = {
|
const newReq = {
|
||||||
items: [{
|
items: [{
|
||||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
||||||
item_id: id,
|
item_id: id,
|
||||||
count: recipe.count
|
count: recipe.count,
|
||||||
}],
|
}],
|
||||||
tid: "ragfair"
|
tid: "ragfair",
|
||||||
};
|
};
|
||||||
|
|
||||||
const entries = Object.entries(pmcData.Hideout.Production);
|
const entries = Object.entries(pmcData.Hideout.Production);
|
||||||
let prodId: string;
|
let prodId: string;
|
||||||
for (const x of entries)
|
for (const x of entries)
|
||||||
{
|
{
|
||||||
if (this.hideoutHelper.isProductionType(x[1])) // Production or ScavCase
|
// Production or ScavCase
|
||||||
|
if (this.hideoutHelper.isProductionType(x[1]))
|
||||||
{
|
{
|
||||||
if ((x[1] as Production).RecipeId === request.recipeId)
|
if ((x[1] as Production).RecipeId === request.recipeId)
|
||||||
{
|
{
|
||||||
@ -695,7 +824,12 @@ export class HideoutController
|
|||||||
|
|
||||||
if (prodId === undefined)
|
if (prodId === undefined)
|
||||||
{
|
{
|
||||||
this.logger.error(this.localisationService.getText("hideout-unable_to_find_production_in_profile_by_recipie_id", request.recipeId));
|
this.logger.error(
|
||||||
|
this.localisationService.getText(
|
||||||
|
"hideout-unable_to_find_production_in_profile_by_recipie_id",
|
||||||
|
request.recipeId,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
return this.httpResponse.appendErrorToOutput(output);
|
return this.httpResponse.appendErrorToOutput(output);
|
||||||
}
|
}
|
||||||
@ -712,9 +846,9 @@ export class HideoutController
|
|||||||
hoursCrafting += recipe.productionTime;
|
hoursCrafting += recipe.productionTime;
|
||||||
if ((hoursCrafting / this.hideoutConfig.hoursForSkillCrafting) >= 1)
|
if ((hoursCrafting / this.hideoutConfig.hoursForSkillCrafting) >= 1)
|
||||||
{
|
{
|
||||||
const multiplierCrafting = Math.floor((hoursCrafting / this.hideoutConfig.hoursForSkillCrafting));
|
const multiplierCrafting = Math.floor(hoursCrafting / this.hideoutConfig.hoursForSkillCrafting);
|
||||||
craftingExpAmount += (1 * multiplierCrafting);
|
craftingExpAmount += 1 * multiplierCrafting;
|
||||||
hoursCrafting -= (this.hideoutConfig.hoursForSkillCrafting * multiplierCrafting);
|
hoursCrafting -= this.hideoutConfig.hoursForSkillCrafting * multiplierCrafting;
|
||||||
}
|
}
|
||||||
|
|
||||||
// increment
|
// increment
|
||||||
@ -726,12 +860,21 @@ export class HideoutController
|
|||||||
// manager Hideout skill
|
// manager Hideout skill
|
||||||
// ? use a configuration variable for the value?
|
// ? use a configuration variable for the value?
|
||||||
const globals = this.databaseServer.getTables().globals;
|
const globals = this.databaseServer.getTables().globals;
|
||||||
this.profileHelper.addSkillPointsToPlayer(pmcData, SkillTypes.HIDEOUT_MANAGEMENT, globals.config.SkillsSettings.HideoutManagement.SkillPointsPerCraft, true);
|
this.profileHelper.addSkillPointsToPlayer(
|
||||||
//manager Crafting skill
|
pmcData,
|
||||||
|
SkillTypes.HIDEOUT_MANAGEMENT,
|
||||||
|
globals.config.SkillsSettings.HideoutManagement.SkillPointsPerCraft,
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
// manager Crafting skill
|
||||||
if (craftingExpAmount > 0)
|
if (craftingExpAmount > 0)
|
||||||
{
|
{
|
||||||
this.profileHelper.addSkillPointsToPlayer(pmcData, SkillTypes.CRAFTING, craftingExpAmount);
|
this.profileHelper.addSkillPointsToPlayer(pmcData, SkillTypes.CRAFTING, craftingExpAmount);
|
||||||
this.profileHelper.addSkillPointsToPlayer(pmcData, SkillTypes.INTELLECT, 0.5 * (Math.round(craftingExpAmount / 15)));
|
this.profileHelper.addSkillPointsToPlayer(
|
||||||
|
pmcData,
|
||||||
|
SkillTypes.INTELLECT,
|
||||||
|
0.5 * (Math.round(craftingExpAmount / 15)),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
area.lastRecipe = request.recipeId;
|
area.lastRecipe = request.recipeId;
|
||||||
counterHoursCrafting.value = hoursCrafting;
|
counterHoursCrafting.value = hoursCrafting;
|
||||||
@ -747,7 +890,7 @@ export class HideoutController
|
|||||||
if (recipe.isEncoded)
|
if (recipe.isEncoded)
|
||||||
{
|
{
|
||||||
const upd: Upd = {
|
const upd: Upd = {
|
||||||
RecodableComponent: { IsEncoded: true}
|
RecodableComponent: {IsEncoded: true},
|
||||||
};
|
};
|
||||||
|
|
||||||
return this.inventoryHelper.addItem(pmcData, newReq, output, sessionID, callback, true, upd);
|
return this.inventoryHelper.addItem(pmcData, newReq, output, sessionID, callback, true, upd);
|
||||||
@ -764,13 +907,19 @@ export class HideoutController
|
|||||||
* @param output Output object to update
|
* @param output Output object to update
|
||||||
* @returns IItemEventRouterResponse
|
* @returns IItemEventRouterResponse
|
||||||
*/
|
*/
|
||||||
protected handleScavCase(sessionID: string, pmcData: IPmcData, request: IHideoutTakeProductionRequestData, output: IItemEventRouterResponse): IItemEventRouterResponse
|
protected handleScavCase(
|
||||||
|
sessionID: string,
|
||||||
|
pmcData: IPmcData,
|
||||||
|
request: IHideoutTakeProductionRequestData,
|
||||||
|
output: IItemEventRouterResponse,
|
||||||
|
): IItemEventRouterResponse
|
||||||
{
|
{
|
||||||
const ongoingProductions = Object.entries(pmcData.Hideout.Production);
|
const ongoingProductions = Object.entries(pmcData.Hideout.Production);
|
||||||
let prodId: string;
|
let prodId: string;
|
||||||
for (const production of ongoingProductions)
|
for (const production of ongoingProductions)
|
||||||
{
|
{
|
||||||
if (this.hideoutHelper.isProductionType(production[1])) // Production or ScavCase
|
// Production or ScavCase
|
||||||
|
if (this.hideoutHelper.isProductionType(production[1]))
|
||||||
{
|
{
|
||||||
if ((production[1] as ScavCase).RecipeId === request.recipeId)
|
if ((production[1] as ScavCase).RecipeId === request.recipeId)
|
||||||
{
|
{
|
||||||
@ -782,7 +931,12 @@ export class HideoutController
|
|||||||
|
|
||||||
if (prodId === undefined)
|
if (prodId === undefined)
|
||||||
{
|
{
|
||||||
this.logger.error(this.localisationService.getText("hideout-unable_to_find_production_in_profile_by_recipie_id", request.recipeId));
|
this.logger.error(
|
||||||
|
this.localisationService.getText(
|
||||||
|
"hideout-unable_to_find_production_in_profile_by_recipie_id",
|
||||||
|
request.recipeId,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
return this.httpResponse.appendErrorToOutput(output);
|
return this.httpResponse.appendErrorToOutput(output);
|
||||||
}
|
}
|
||||||
@ -795,28 +949,29 @@ export class HideoutController
|
|||||||
// Remove the old production from output object before its sent to client
|
// Remove the old production from output object before its sent to client
|
||||||
delete output.profileChanges[sessionID].production[request.recipeId];
|
delete output.profileChanges[sessionID].production[request.recipeId];
|
||||||
|
|
||||||
const itemsToAdd = pmcData.Hideout.Production[prodId].Products.map((x: { _tpl: string; upd?: { StackObjectsCount?: number; }; }) =>
|
const itemsToAdd = pmcData.Hideout.Production[prodId].Products.map(
|
||||||
{
|
(x: {_tpl: string; upd?: {StackObjectsCount?: number;};}) =>
|
||||||
let id = x._tpl;
|
|
||||||
if (this.presetHelper.hasPreset(id))
|
|
||||||
{
|
{
|
||||||
id = this.presetHelper.getDefaultPreset(id)._id;
|
let id = x._tpl;
|
||||||
}
|
if (this.presetHelper.hasPreset(id))
|
||||||
const numOfItems = !x.upd?.StackObjectsCount
|
{
|
||||||
? 1
|
id = this.presetHelper.getDefaultPreset(id)._id;
|
||||||
: x.upd.StackObjectsCount;
|
}
|
||||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
const numOfItems = !x.upd?.StackObjectsCount ?
|
||||||
return { item_id: id, count: numOfItems };
|
1 :
|
||||||
});
|
x.upd.StackObjectsCount;
|
||||||
|
|
||||||
|
return {item_id: id, count: numOfItems};
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
const newReq = {
|
const newReq = {
|
||||||
items: itemsToAdd,
|
items: itemsToAdd,
|
||||||
tid: "ragfair"
|
tid: "ragfair",
|
||||||
};
|
};
|
||||||
|
|
||||||
const callback = () =>
|
const callback = () =>
|
||||||
{
|
{
|
||||||
|
|
||||||
// Null production data now it's complete - will be cleaned up later by update() process
|
// Null production data now it's complete - will be cleaned up later by update() process
|
||||||
pmcData.Hideout.Production[prodId] = null;
|
pmcData.Hideout.Production[prodId] = null;
|
||||||
};
|
};
|
||||||
@ -831,18 +986,21 @@ export class HideoutController
|
|||||||
* @param sessionID Session id
|
* @param sessionID Session id
|
||||||
* @returns IItemEventRouterResponse
|
* @returns IItemEventRouterResponse
|
||||||
*/
|
*/
|
||||||
public registerProduction(pmcData: IPmcData, request: IHideoutSingleProductionStartRequestData | IHideoutContinuousProductionStartRequestData, sessionID: string): IItemEventRouterResponse
|
public registerProduction(
|
||||||
|
pmcData: IPmcData,
|
||||||
|
request: IHideoutSingleProductionStartRequestData | IHideoutContinuousProductionStartRequestData,
|
||||||
|
sessionID: string,
|
||||||
|
): IItemEventRouterResponse
|
||||||
{
|
{
|
||||||
return this.hideoutHelper.registerProduction(pmcData, request, sessionID);
|
return this.hideoutHelper.registerProduction(pmcData, request, sessionID);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get quick time event list for hideout
|
* Get quick time event list for hideout
|
||||||
* // TODO - implement this
|
* // TODO: Implement this
|
||||||
* @param sessionId Session id
|
* @param sessionId Session id
|
||||||
* @returns IQteData array
|
* @returns IQteData array
|
||||||
*/
|
*/
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
||||||
public getQteList(sessionId: string): IQteData[]
|
public getQteList(sessionId: string): IQteData[]
|
||||||
{
|
{
|
||||||
return this.databaseServer.getTables().hideout.qte;
|
return this.databaseServer.getTables().hideout.qte;
|
||||||
@ -855,8 +1013,11 @@ export class HideoutController
|
|||||||
* @param pmcData Profile to adjust
|
* @param pmcData Profile to adjust
|
||||||
* @param request QTE result object
|
* @param request QTE result object
|
||||||
*/
|
*/
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
public handleQTEEventOutcome(
|
||||||
public handleQTEEventOutcome(sessionId: string, pmcData: IPmcData, request: IHandleQTEEventRequestData): IItemEventRouterResponse
|
sessionId: string,
|
||||||
|
pmcData: IPmcData,
|
||||||
|
request: IHandleQTEEventRequestData,
|
||||||
|
): IItemEventRouterResponse
|
||||||
{
|
{
|
||||||
// {
|
// {
|
||||||
// Action: "HideoutQuickTimeEvent",
|
// Action: "HideoutQuickTimeEvent",
|
||||||
@ -890,22 +1051,28 @@ export class HideoutController
|
|||||||
* @param request shooting range score request
|
* @param request shooting range score request
|
||||||
* @returns IItemEventRouterResponse
|
* @returns IItemEventRouterResponse
|
||||||
*/
|
*/
|
||||||
public recordShootingRangePoints(sessionId: string, pmcData: IPmcData, request: IRecordShootingRangePoints): IItemEventRouterResponse
|
public recordShootingRangePoints(
|
||||||
|
sessionId: string,
|
||||||
|
pmcData: IPmcData,
|
||||||
|
request: IRecordShootingRangePoints,
|
||||||
|
): IItemEventRouterResponse
|
||||||
{
|
{
|
||||||
// Check if counter exists, add placeholder if it doesnt
|
// Check if counter exists, add placeholder if it doesn't
|
||||||
if (!pmcData.Stats.Eft.OverallCounters.Items.find(x => x.Key.includes("ShootingRangePoints")))
|
if (!pmcData.Stats.Eft.OverallCounters.Items.find((x) => x.Key.includes("ShootingRangePoints")))
|
||||||
{
|
{
|
||||||
pmcData.Stats.Eft.OverallCounters.Items.push({
|
pmcData.Stats.Eft.OverallCounters.Items.push({
|
||||||
Key: ["ShootingRangePoints"],
|
Key: ["ShootingRangePoints"],
|
||||||
Value: 0
|
Value: 0,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find counter by key and update value
|
// Find counter by key and update value
|
||||||
const shootingRangeHighScore = pmcData.Stats.Eft.OverallCounters.Items.find(x => x.Key.includes("ShootingRangePoints"));
|
const shootingRangeHighScore = pmcData.Stats.Eft.OverallCounters.Items.find((x) =>
|
||||||
|
x.Key.includes("ShootingRangePoints")
|
||||||
|
);
|
||||||
shootingRangeHighScore.Value = request.points;
|
shootingRangeHighScore.Value = request.points;
|
||||||
|
|
||||||
// Check against live, maybe a response isnt necessary
|
// Check against live, maybe a response isn't necessary
|
||||||
return this.eventOutputHolder.getOutput(sessionId);
|
return this.eventOutputHolder.getOutput(sessionId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -915,17 +1082,21 @@ export class HideoutController
|
|||||||
* @param pmcData Profile to improve area in
|
* @param pmcData Profile to improve area in
|
||||||
* @param request Improve area request data
|
* @param request Improve area request data
|
||||||
*/
|
*/
|
||||||
public improveArea(sessionId: string, pmcData: IPmcData, request: IHideoutImproveAreaRequestData): IItemEventRouterResponse
|
public improveArea(
|
||||||
|
sessionId: string,
|
||||||
|
pmcData: IPmcData,
|
||||||
|
request: IHideoutImproveAreaRequestData,
|
||||||
|
): IItemEventRouterResponse
|
||||||
{
|
{
|
||||||
const output = this.eventOutputHolder.getOutput(sessionId);
|
const output = this.eventOutputHolder.getOutput(sessionId);
|
||||||
|
|
||||||
// Create mapping of required item with corrisponding item from player inventory
|
// Create mapping of required item with corresponding item from player inventory
|
||||||
const items = request.items.map(reqItem =>
|
const items = request.items.map((reqItem) =>
|
||||||
{
|
{
|
||||||
const item = pmcData.Inventory.items.find(invItem => invItem._id === reqItem.id);
|
const item = pmcData.Inventory.items.find((invItem) => invItem._id === reqItem.id);
|
||||||
return {
|
return {
|
||||||
inventoryItem: item,
|
inventoryItem: item,
|
||||||
requestedItem: reqItem
|
requestedItem: reqItem,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -934,14 +1105,18 @@ export class HideoutController
|
|||||||
{
|
{
|
||||||
if (!item.inventoryItem)
|
if (!item.inventoryItem)
|
||||||
{
|
{
|
||||||
this.logger.error(this.localisationService.getText("hideout-unable_to_find_item_in_inventory", item.requestedItem.id));
|
this.logger.error(
|
||||||
|
this.localisationService.getText("hideout-unable_to_find_item_in_inventory", item.requestedItem.id),
|
||||||
|
);
|
||||||
return this.httpResponse.appendErrorToOutput(output);
|
return this.httpResponse.appendErrorToOutput(output);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.paymentHelper.isMoneyTpl(item.inventoryItem._tpl)
|
if (
|
||||||
&& item.inventoryItem.upd
|
this.paymentHelper.isMoneyTpl(item.inventoryItem._tpl) &&
|
||||||
&& item.inventoryItem.upd.StackObjectsCount
|
item.inventoryItem.upd &&
|
||||||
&& item.inventoryItem.upd.StackObjectsCount > item.requestedItem.count)
|
item.inventoryItem.upd.StackObjectsCount &&
|
||||||
|
item.inventoryItem.upd.StackObjectsCount > item.requestedItem.count
|
||||||
|
)
|
||||||
{
|
{
|
||||||
item.inventoryItem.upd.StackObjectsCount -= item.requestedItem.count;
|
item.inventoryItem.upd.StackObjectsCount -= item.requestedItem.count;
|
||||||
}
|
}
|
||||||
@ -951,21 +1126,23 @@ export class HideoutController
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const profileHideoutArea = pmcData.Hideout.Areas.find(x => x.type === request.areaType);
|
const profileHideoutArea = pmcData.Hideout.Areas.find((x) => x.type === request.areaType);
|
||||||
if (!profileHideoutArea)
|
if (!profileHideoutArea)
|
||||||
{
|
{
|
||||||
this.logger.error(this.localisationService.getText("hideout-unable_to_find_area", request.areaType));
|
this.logger.error(this.localisationService.getText("hideout-unable_to_find_area", request.areaType));
|
||||||
return this.httpResponse.appendErrorToOutput(output);
|
return this.httpResponse.appendErrorToOutput(output);
|
||||||
}
|
}
|
||||||
|
|
||||||
const hideoutDbData = this.databaseServer.getTables().hideout.areas.find(x => x.type === request.areaType);
|
const hideoutDbData = this.databaseServer.getTables().hideout.areas.find((x) => x.type === request.areaType);
|
||||||
if (!hideoutDbData)
|
if (!hideoutDbData)
|
||||||
{
|
{
|
||||||
this.logger.error(this.localisationService.getText("hideout-unable_to_find_area_in_database", request.areaType));
|
this.logger.error(
|
||||||
|
this.localisationService.getText("hideout-unable_to_find_area_in_database", request.areaType),
|
||||||
|
);
|
||||||
return this.httpResponse.appendErrorToOutput(output);
|
return this.httpResponse.appendErrorToOutput(output);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add all improvemets to output object
|
// Add all improvements to output object
|
||||||
const improvements = hideoutDbData.stages[profileHideoutArea.level].improvements;
|
const improvements = hideoutDbData.stages[profileHideoutArea.level].improvements;
|
||||||
const timestamp = this.timeUtil.getTimestamp();
|
const timestamp = this.timeUtil.getTimestamp();
|
||||||
for (const improvement of improvements)
|
for (const improvement of improvements)
|
||||||
@ -975,7 +1152,10 @@ export class HideoutController
|
|||||||
output.profileChanges[sessionId].improvements = {};
|
output.profileChanges[sessionId].improvements = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
const improvementDetails = {completed: false, improveCompleteTimestamp: timestamp + improvement.improvementTime};
|
const improvementDetails = {
|
||||||
|
completed: false,
|
||||||
|
improveCompleteTimestamp: timestamp + improvement.improvementTime,
|
||||||
|
};
|
||||||
output.profileChanges[sessionId].improvements[improvement.id] = improvementDetails;
|
output.profileChanges[sessionId].improvements[improvement.id] = improvementDetails;
|
||||||
pmcData.Hideout.Improvement[improvement.id] = improvementDetails;
|
pmcData.Hideout.Improvement[improvement.id] = improvementDetails;
|
||||||
}
|
}
|
||||||
@ -990,7 +1170,11 @@ export class HideoutController
|
|||||||
* @param request Cancel production request data
|
* @param request Cancel production request data
|
||||||
* @returns IItemEventRouterResponse
|
* @returns IItemEventRouterResponse
|
||||||
*/
|
*/
|
||||||
public cancelProduction(sessionId: string, pmcData: IPmcData, request: IHideoutCancelProductionRequestData): IItemEventRouterResponse
|
public cancelProduction(
|
||||||
|
sessionId: string,
|
||||||
|
pmcData: IPmcData,
|
||||||
|
request: IHideoutCancelProductionRequestData,
|
||||||
|
): IItemEventRouterResponse
|
||||||
{
|
{
|
||||||
const output = this.eventOutputHolder.getOutput(sessionId);
|
const output = this.eventOutputHolder.getOutput(sessionId);
|
||||||
|
|
||||||
@ -1006,7 +1190,7 @@ export class HideoutController
|
|||||||
// Null out production data so client gets informed when response send back
|
// Null out production data so client gets informed when response send back
|
||||||
pmcData.Hideout.Production[request.recipeId] = null;
|
pmcData.Hideout.Production[request.recipeId] = null;
|
||||||
|
|
||||||
// TODO - handle timestamp somehow?
|
// TODO: handle timestamp somehow?
|
||||||
|
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
@ -56,7 +56,7 @@ export class InraidController
|
|||||||
@inject("InsuranceService") protected insuranceService: InsuranceService,
|
@inject("InsuranceService") protected insuranceService: InsuranceService,
|
||||||
@inject("InRaidHelper") protected inRaidHelper: InRaidHelper,
|
@inject("InRaidHelper") protected inRaidHelper: InRaidHelper,
|
||||||
@inject("ApplicationContext") protected applicationContext: ApplicationContext,
|
@inject("ApplicationContext") protected applicationContext: ApplicationContext,
|
||||||
@inject("ConfigServer") protected configServer: ConfigServer
|
@inject("ConfigServer") protected configServer: ConfigServer,
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
this.airdropConfig = this.configServer.getConfig(ConfigTypes.AIRDROP);
|
this.airdropConfig = this.configServer.getConfig(ConfigTypes.AIRDROP);
|
||||||
@ -124,7 +124,12 @@ export class InraidController
|
|||||||
// Check for exit status
|
// Check for exit status
|
||||||
this.markOrRemoveFoundInRaidItems(postRaidRequest);
|
this.markOrRemoveFoundInRaidItems(postRaidRequest);
|
||||||
|
|
||||||
postRaidRequest.profile.Inventory.items = this.itemHelper.replaceIDs(postRaidRequest.profile, postRaidRequest.profile.Inventory.items, serverPmcData.InsuredItems, postRaidRequest.profile.Inventory.fastPanel);
|
postRaidRequest.profile.Inventory.items = this.itemHelper.replaceIDs(
|
||||||
|
postRaidRequest.profile,
|
||||||
|
postRaidRequest.profile.Inventory.items,
|
||||||
|
serverPmcData.InsuredItems,
|
||||||
|
postRaidRequest.profile.Inventory.fastPanel,
|
||||||
|
);
|
||||||
this.inRaidHelper.addUpdToMoneyFromRaid(postRaidRequest.profile.Inventory.items);
|
this.inRaidHelper.addUpdToMoneyFromRaid(postRaidRequest.profile.Inventory.items);
|
||||||
|
|
||||||
// Purge profile of equipment/container items
|
// Purge profile of equipment/container items
|
||||||
@ -146,22 +151,30 @@ export class InraidController
|
|||||||
if (locationName === "lighthouse" && postRaidRequest.profile.Info.Side.toLowerCase() === "usec")
|
if (locationName === "lighthouse" && postRaidRequest.profile.Info.Side.toLowerCase() === "usec")
|
||||||
{
|
{
|
||||||
// Decrement counter if it exists, don't go below 0
|
// Decrement counter if it exists, don't go below 0
|
||||||
const remainingCounter = serverPmcData?.Stats.Eft.OverallCounters.Items.find(x => x.Key.includes("UsecRaidRemainKills"));
|
const remainingCounter = serverPmcData?.Stats.Eft.OverallCounters.Items.find((x) =>
|
||||||
|
x.Key.includes("UsecRaidRemainKills")
|
||||||
|
);
|
||||||
if (remainingCounter?.Value > 0)
|
if (remainingCounter?.Value > 0)
|
||||||
{
|
{
|
||||||
remainingCounter.Value --;
|
remainingCounter.Value--;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isDead)
|
if (isDead)
|
||||||
{
|
{
|
||||||
this.pmcChatResponseService.sendKillerResponse(sessionID, serverPmcData, postRaidRequest.profile.Stats.Eft.Aggressor);
|
this.pmcChatResponseService.sendKillerResponse(
|
||||||
|
sessionID,
|
||||||
|
serverPmcData,
|
||||||
|
postRaidRequest.profile.Stats.Eft.Aggressor,
|
||||||
|
);
|
||||||
this.matchBotDetailsCacheService.clearCache();
|
this.matchBotDetailsCacheService.clearCache();
|
||||||
|
|
||||||
serverPmcData = this.performPostRaidActionsWhenDead(postRaidRequest, serverPmcData, sessionID);
|
serverPmcData = this.performPostRaidActionsWhenDead(postRaidRequest, serverPmcData, sessionID);
|
||||||
}
|
}
|
||||||
|
|
||||||
const victims = postRaidRequest.profile.Stats.Eft.Victims.filter(x => ["sptbear", "sptusec"].includes(x.Role.toLowerCase()));
|
const victims = postRaidRequest.profile.Stats.Eft.Victims.filter((x) =>
|
||||||
|
["sptbear", "sptusec"].includes(x.Role.toLowerCase())
|
||||||
|
);
|
||||||
if (victims?.length > 0)
|
if (victims?.length > 0)
|
||||||
{
|
{
|
||||||
this.pmcChatResponseService.sendVictimResponse(sessionID, victims, serverPmcData);
|
this.pmcChatResponseService.sendVictimResponse(sessionID, victims, serverPmcData);
|
||||||
@ -175,26 +188,37 @@ export class InraidController
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Make changes to pmc profile after they've died in raid,
|
* Make changes to pmc profile after they've died in raid,
|
||||||
* Alter bodypart hp, handle insurance, delete inventory items, remove carried quest items
|
* Alter body part hp, handle insurance, delete inventory items, remove carried quest items
|
||||||
* @param postRaidSaveRequest Post-raid save request
|
* @param postRaidSaveRequest Post-raid save request
|
||||||
* @param pmcData Pmc profile
|
* @param pmcData Pmc profile
|
||||||
* @param sessionID Session id
|
* @param sessionID Session id
|
||||||
* @returns Updated profile object
|
* @returns Updated profile object
|
||||||
*/
|
*/
|
||||||
protected performPostRaidActionsWhenDead(postRaidSaveRequest: ISaveProgressRequestData, pmcData: IPmcData, sessionID: string): IPmcData
|
protected performPostRaidActionsWhenDead(
|
||||||
|
postRaidSaveRequest: ISaveProgressRequestData,
|
||||||
|
pmcData: IPmcData,
|
||||||
|
sessionID: string,
|
||||||
|
): IPmcData
|
||||||
{
|
{
|
||||||
this.updatePmcHealthPostRaid(postRaidSaveRequest, pmcData);
|
this.updatePmcHealthPostRaid(postRaidSaveRequest, pmcData);
|
||||||
this.inRaidHelper.deleteInventory(pmcData, sessionID);
|
this.inRaidHelper.deleteInventory(pmcData, sessionID);
|
||||||
|
|
||||||
if (this.inRaidHelper.removeQuestItemsOnDeath())
|
if (this.inRaidHelper.removeQuestItemsOnDeath())
|
||||||
{
|
{
|
||||||
// Find and remove the completed condition from profile if player died, otherwise quest is stuck in limbo and quest items cannot be picked up again
|
// Find and remove the completed condition from profile if player died, otherwise quest is stuck in limbo
|
||||||
|
// and quest items cannot be picked up again
|
||||||
const allQuests = this.questHelper.getQuestsFromDb();
|
const allQuests = this.questHelper.getQuestsFromDb();
|
||||||
const activeQuestIdsInProfile = pmcData.Quests.filter(x => ![QuestStatus.AvailableForStart, QuestStatus.Success, QuestStatus.Expired].includes(x.status)).map(x => x.qid);
|
const activeQuestIdsInProfile = pmcData.Quests.filter((x) =>
|
||||||
|
![QuestStatus.AvailableForStart, QuestStatus.Success, QuestStatus.Expired].includes(x.status)
|
||||||
|
).map((x) => x.qid);
|
||||||
for (const questItem of postRaidSaveRequest.profile.Stats.Eft.CarriedQuestItems)
|
for (const questItem of postRaidSaveRequest.profile.Stats.Eft.CarriedQuestItems)
|
||||||
{
|
{
|
||||||
// Get quest/find condition for carried quest item
|
// Get quest/find condition for carried quest item
|
||||||
const questAndFindItemConditionId = this.questHelper.getFindItemConditionByQuestItem(questItem, activeQuestIdsInProfile, allQuests);
|
const questAndFindItemConditionId = this.questHelper.getFindItemConditionByQuestItem(
|
||||||
|
questItem,
|
||||||
|
activeQuestIdsInProfile,
|
||||||
|
allQuests,
|
||||||
|
);
|
||||||
if (questAndFindItemConditionId)
|
if (questAndFindItemConditionId)
|
||||||
{
|
{
|
||||||
this.profileHelper.removeCompletedQuestConditionFromProfile(pmcData, questAndFindItemConditionId);
|
this.profileHelper.removeCompletedQuestConditionFromProfile(pmcData, questAndFindItemConditionId);
|
||||||
@ -209,7 +233,7 @@ export class InraidController
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adjust player characters bodypart hp post-raid
|
* Adjust player characters body part hp post-raid
|
||||||
* @param postRaidSaveRequest post raid data
|
* @param postRaidSaveRequest post raid data
|
||||||
* @param pmcData player profile
|
* @param pmcData player profile
|
||||||
*/
|
*/
|
||||||
@ -234,13 +258,13 @@ export class InraidController
|
|||||||
/**
|
/**
|
||||||
* Reduce body part hp to % of max
|
* Reduce body part hp to % of max
|
||||||
* @param pmcData profile to edit
|
* @param pmcData profile to edit
|
||||||
* @param multipler multipler to apply to max health
|
* @param multiplier multiplier to apply to max health
|
||||||
*/
|
*/
|
||||||
protected reducePmcHealthToPercent(pmcData: IPmcData, multipler: number): void
|
protected reducePmcHealthToPercent(pmcData: IPmcData, multiplier: number): void
|
||||||
{
|
{
|
||||||
for (const bodyPart of Object.values(pmcData.Health.BodyParts))
|
for (const bodyPart of Object.values(pmcData.Health.BodyParts))
|
||||||
{
|
{
|
||||||
(<BodyPartHealth>bodyPart).Health.Current = (<BodyPartHealth>bodyPart).Health.Maximum * multipler;
|
(<BodyPartHealth>bodyPart).Health.Current = (<BodyPartHealth>bodyPart).Health.Maximum * multiplier;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -269,7 +293,12 @@ export class InraidController
|
|||||||
// Check for exit status
|
// Check for exit status
|
||||||
this.markOrRemoveFoundInRaidItems(postRaidRequest);
|
this.markOrRemoveFoundInRaidItems(postRaidRequest);
|
||||||
|
|
||||||
postRaidRequest.profile.Inventory.items = this.itemHelper.replaceIDs(postRaidRequest.profile, postRaidRequest.profile.Inventory.items, pmcData.InsuredItems, postRaidRequest.profile.Inventory.fastPanel);
|
postRaidRequest.profile.Inventory.items = this.itemHelper.replaceIDs(
|
||||||
|
postRaidRequest.profile,
|
||||||
|
postRaidRequest.profile.Inventory.items,
|
||||||
|
pmcData.InsuredItems,
|
||||||
|
postRaidRequest.profile.Inventory.fastPanel,
|
||||||
|
);
|
||||||
this.inRaidHelper.addUpdToMoneyFromRaid(postRaidRequest.profile.Inventory.items);
|
this.inRaidHelper.addUpdToMoneyFromRaid(postRaidRequest.profile.Inventory.items);
|
||||||
|
|
||||||
this.handlePostRaidPlayerScavProcess(scavData, sessionID, postRaidRequest, pmcData, isDead);
|
this.handlePostRaidPlayerScavProcess(scavData, sessionID, postRaidRequest, pmcData, isDead);
|
||||||
@ -286,7 +315,6 @@ export class InraidController
|
|||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return profile.ConditionCounters.Counters.length > 0;
|
return profile.ConditionCounters.Counters.length > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -294,7 +322,7 @@ export class InraidController
|
|||||||
{
|
{
|
||||||
for (const quest of scavProfile.Quests)
|
for (const quest of scavProfile.Quests)
|
||||||
{
|
{
|
||||||
const pmcQuest = pmcProfile.Quests.find(x => x.qid === quest.qid);
|
const pmcQuest = pmcProfile.Quests.find((x) => x.qid === quest.qid);
|
||||||
if (!pmcQuest)
|
if (!pmcQuest)
|
||||||
{
|
{
|
||||||
this.logger.warning(`No PMC quest found for ID: ${quest.qid}`);
|
this.logger.warning(`No PMC quest found for ID: ${quest.qid}`);
|
||||||
@ -303,9 +331,14 @@ export class InraidController
|
|||||||
|
|
||||||
// Post-raid status is enum word e.g. `Started` but pmc quest status is number e.g. 2
|
// Post-raid status is enum word e.g. `Started` but pmc quest status is number e.g. 2
|
||||||
// Status values mismatch or statusTimers counts mismatch
|
// Status values mismatch or statusTimers counts mismatch
|
||||||
if (quest.status !== <any>QuestStatus[pmcQuest.status] || quest.statusTimers.length !== pmcQuest.statusTimers.length)
|
if (
|
||||||
|
quest.status !== <any>QuestStatus[pmcQuest.status] ||
|
||||||
|
quest.statusTimers.length !== pmcQuest.statusTimers.length
|
||||||
|
)
|
||||||
{
|
{
|
||||||
this.logger.warning(`Quest: ${quest.qid} found in PMC profile has different status/statustimer. Scav: ${quest.status} vs PMC: ${pmcQuest.status}`);
|
this.logger.warning(
|
||||||
|
`Quest: ${quest.qid} found in PMC profile has different status/statustimer. Scav: ${quest.status} vs PMC: ${pmcQuest.status}`,
|
||||||
|
);
|
||||||
pmcQuest.status = <any>QuestStatus[quest.status];
|
pmcQuest.status = <any>QuestStatus[quest.status];
|
||||||
|
|
||||||
// Copy status timers over + fix bad enum key for each
|
// Copy status timers over + fix bad enum key for each
|
||||||
@ -324,17 +357,20 @@ export class InraidController
|
|||||||
// Loop over all scav counters and add into pmc profile
|
// Loop over all scav counters and add into pmc profile
|
||||||
for (const scavCounter of scavProfile.ConditionCounters.Counters)
|
for (const scavCounter of scavProfile.ConditionCounters.Counters)
|
||||||
{
|
{
|
||||||
this.logger.warning(`Processing counter: ${scavCounter.id} value:${scavCounter.value} quest:${scavCounter.qid}`);
|
this.logger.warning(
|
||||||
const counterInPmcProfile = pmcProfile.ConditionCounters.Counters.find(x => x.id === scavCounter.id);
|
`Processing counter: ${scavCounter.id} value:${scavCounter.value} quest:${scavCounter.qid}`,
|
||||||
|
);
|
||||||
|
const counterInPmcProfile = pmcProfile.ConditionCounters.Counters.find((x) => x.id === scavCounter.id);
|
||||||
if (!counterInPmcProfile)
|
if (!counterInPmcProfile)
|
||||||
{
|
{
|
||||||
// Doesn't exist yet, push it straight in
|
// Doesn't exist yet, push it straight in
|
||||||
pmcProfile.ConditionCounters.Counters.push(scavCounter);
|
pmcProfile.ConditionCounters.Counters.push(scavCounter);
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.logger.warning(`Counter id: ${scavCounter.id} already exists in pmc profile! with value: ${counterInPmcProfile.value} for quest: ${counterInPmcProfile.qid}`);
|
this.logger.warning(
|
||||||
|
`Counter id: ${scavCounter.id} already exists in pmc profile! with value: ${counterInPmcProfile.value} for quest: ${counterInPmcProfile.qid}`,
|
||||||
|
);
|
||||||
|
|
||||||
// Only adjust counter value if its changed
|
// Only adjust counter value if its changed
|
||||||
if (counterInPmcProfile.value !== scavCounter.value)
|
if (counterInPmcProfile.value !== scavCounter.value)
|
||||||
@ -363,7 +399,7 @@ export class InraidController
|
|||||||
{
|
{
|
||||||
if (offraidData.exit !== PlayerRaidEndState.SURVIVED)
|
if (offraidData.exit !== PlayerRaidEndState.SURVIVED)
|
||||||
{
|
{
|
||||||
// Remove FIR status if the player havn't survived
|
// Remove FIR status if the player hasn't survived
|
||||||
offraidData.profile = this.inRaidHelper.removeSpawnedInSessionPropertyFromItems(offraidData.profile);
|
offraidData.profile = this.inRaidHelper.removeSpawnedInSessionPropertyFromItems(offraidData.profile);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -376,7 +412,13 @@ export class InraidController
|
|||||||
* @param pmcData Pmc profile
|
* @param pmcData Pmc profile
|
||||||
* @param isDead Is player dead
|
* @param isDead Is player dead
|
||||||
*/
|
*/
|
||||||
protected handlePostRaidPlayerScavProcess(scavData: IPmcData, sessionID: string, offraidData: ISaveProgressRequestData, pmcData: IPmcData, isDead: boolean): void
|
protected handlePostRaidPlayerScavProcess(
|
||||||
|
scavData: IPmcData,
|
||||||
|
sessionID: string,
|
||||||
|
offraidData: ISaveProgressRequestData,
|
||||||
|
pmcData: IPmcData,
|
||||||
|
isDead: boolean,
|
||||||
|
): void
|
||||||
{
|
{
|
||||||
// Update scav profile inventory
|
// Update scav profile inventory
|
||||||
scavData = this.inRaidHelper.setInventory(sessionID, scavData, offraidData.profile);
|
scavData = this.inRaidHelper.setInventory(sessionID, scavData, offraidData.profile);
|
||||||
@ -411,7 +453,10 @@ export class InraidController
|
|||||||
|
|
||||||
let fenceStanding = Number(pmcData.TradersInfo[fenceId].standing);
|
let fenceStanding = Number(pmcData.TradersInfo[fenceId].standing);
|
||||||
this.logger.debug(`Old fence standing: ${fenceStanding}`);
|
this.logger.debug(`Old fence standing: ${fenceStanding}`);
|
||||||
fenceStanding = this.inRaidHelper.calculateFenceStandingChangeFromKills(fenceStanding, offraidData.profile.Stats.Eft.Victims);
|
fenceStanding = this.inRaidHelper.calculateFenceStandingChangeFromKills(
|
||||||
|
fenceStanding,
|
||||||
|
offraidData.profile.Stats.Eft.Victims,
|
||||||
|
);
|
||||||
|
|
||||||
// Successful extract with scav adds 0.01 standing
|
// Successful extract with scav adds 0.01 standing
|
||||||
if (offraidData.exit === PlayerRaidEndState.SURVIVED)
|
if (offraidData.exit === PlayerRaidEndState.SURVIVED)
|
||||||
|
@ -10,7 +10,7 @@ import { IGetInsuranceCostRequestData } from "@spt-aki/models/eft/insurance/IGet
|
|||||||
import { IGetInsuranceCostResponseData } from "@spt-aki/models/eft/insurance/IGetInsuranceCostResponseData";
|
import { IGetInsuranceCostResponseData } from "@spt-aki/models/eft/insurance/IGetInsuranceCostResponseData";
|
||||||
import { IInsureRequestData } from "@spt-aki/models/eft/insurance/IInsureRequestData";
|
import { IInsureRequestData } from "@spt-aki/models/eft/insurance/IInsureRequestData";
|
||||||
import { IItemEventRouterResponse } from "@spt-aki/models/eft/itemEvent/IItemEventRouterResponse";
|
import { IItemEventRouterResponse } from "@spt-aki/models/eft/itemEvent/IItemEventRouterResponse";
|
||||||
import { ISystemData, Insurance } from "@spt-aki/models/eft/profile/IAkiProfile";
|
import { Insurance, ISystemData } from "@spt-aki/models/eft/profile/IAkiProfile";
|
||||||
import { IProcessBuyTradeRequestData } from "@spt-aki/models/eft/trade/IProcessBuyTradeRequestData";
|
import { IProcessBuyTradeRequestData } from "@spt-aki/models/eft/trade/IProcessBuyTradeRequestData";
|
||||||
import { ConfigTypes } from "@spt-aki/models/enums/ConfigTypes";
|
import { ConfigTypes } from "@spt-aki/models/enums/ConfigTypes";
|
||||||
import { MessageType } from "@spt-aki/models/enums/MessageType";
|
import { MessageType } from "@spt-aki/models/enums/MessageType";
|
||||||
@ -24,9 +24,9 @@ import { SaveServer } from "@spt-aki/servers/SaveServer";
|
|||||||
import { InsuranceService } from "@spt-aki/services/InsuranceService";
|
import { InsuranceService } from "@spt-aki/services/InsuranceService";
|
||||||
import { MailSendService } from "@spt-aki/services/MailSendService";
|
import { MailSendService } from "@spt-aki/services/MailSendService";
|
||||||
import { PaymentService } from "@spt-aki/services/PaymentService";
|
import { PaymentService } from "@spt-aki/services/PaymentService";
|
||||||
|
import { MathUtil } from "@spt-aki/utils/MathUtil";
|
||||||
import { RandomUtil } from "@spt-aki/utils/RandomUtil";
|
import { RandomUtil } from "@spt-aki/utils/RandomUtil";
|
||||||
import { TimeUtil } from "@spt-aki/utils/TimeUtil";
|
import { TimeUtil } from "@spt-aki/utils/TimeUtil";
|
||||||
import { MathUtil } from "@spt-aki/utils/MathUtil";
|
|
||||||
|
|
||||||
@injectable()
|
@injectable()
|
||||||
export class InsuranceController
|
export class InsuranceController
|
||||||
@ -48,7 +48,7 @@ export class InsuranceController
|
|||||||
@inject("PaymentService") protected paymentService: PaymentService,
|
@inject("PaymentService") protected paymentService: PaymentService,
|
||||||
@inject("InsuranceService") protected insuranceService: InsuranceService,
|
@inject("InsuranceService") protected insuranceService: InsuranceService,
|
||||||
@inject("MailSendService") protected mailSendService: MailSendService,
|
@inject("MailSendService") protected mailSendService: MailSendService,
|
||||||
@inject("ConfigServer") protected configServer: ConfigServer
|
@inject("ConfigServer") protected configServer: ConfigServer,
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
this.insuranceConfig = this.configServer.getConfig(ConfigTypes.INSURANCE);
|
this.insuranceConfig = this.configServer.getConfig(ConfigTypes.INSURANCE);
|
||||||
@ -58,7 +58,7 @@ export class InsuranceController
|
|||||||
* Process insurance items of all profiles prior to being given back to the player through the mail service.
|
* Process insurance items of all profiles prior to being given back to the player through the mail service.
|
||||||
*
|
*
|
||||||
* @returns void
|
* @returns void
|
||||||
*/
|
*/
|
||||||
public processReturn(): void
|
public processReturn(): void
|
||||||
{
|
{
|
||||||
// Process each installed profile.
|
// Process each installed profile.
|
||||||
@ -72,7 +72,7 @@ export class InsuranceController
|
|||||||
* Process insurance items of a single profile prior to being given back to the player through the mail service.
|
* Process insurance items of a single profile prior to being given back to the player through the mail service.
|
||||||
*
|
*
|
||||||
* @returns void
|
* @returns void
|
||||||
*/
|
*/
|
||||||
public processReturnByProfile(sessionID: string): void
|
public processReturnByProfile(sessionID: string): void
|
||||||
{
|
{
|
||||||
// Filter out items that don't need to be processed yet.
|
// Filter out items that don't need to be processed yet.
|
||||||
@ -117,7 +117,11 @@ export class InsuranceController
|
|||||||
*/
|
*/
|
||||||
protected processInsuredItems(insuranceDetails: Insurance[], sessionID: string): void
|
protected processInsuredItems(insuranceDetails: Insurance[], sessionID: string): void
|
||||||
{
|
{
|
||||||
this.logger.debug(`Processing ${insuranceDetails.length} insurance packages, which includes a total of ${this.countAllInsuranceItems(insuranceDetails)} items, in profile ${sessionID}`);
|
this.logger.debug(
|
||||||
|
`Processing ${insuranceDetails.length} insurance packages, which includes a total of ${
|
||||||
|
this.countAllInsuranceItems(insuranceDetails)
|
||||||
|
} items, in profile ${sessionID}`,
|
||||||
|
);
|
||||||
|
|
||||||
// Iterate over each of the insurance packages.
|
// Iterate over each of the insurance packages.
|
||||||
for (const insured of insuranceDetails)
|
for (const insured of insuranceDetails)
|
||||||
@ -146,7 +150,7 @@ export class InsuranceController
|
|||||||
*/
|
*/
|
||||||
protected countAllInsuranceItems(insurance: Insurance[]): number
|
protected countAllInsuranceItems(insurance: Insurance[]): number
|
||||||
{
|
{
|
||||||
return this.mathUtil.arraySum(insurance.map(ins => ins.items.length));
|
return this.mathUtil.arraySum(insurance.map((ins) => ins.items.length));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -159,13 +163,15 @@ export class InsuranceController
|
|||||||
protected removeInsurancePackageFromProfile(sessionID: string, packageInfo: ISystemData): void
|
protected removeInsurancePackageFromProfile(sessionID: string, packageInfo: ISystemData): void
|
||||||
{
|
{
|
||||||
const profile = this.saveServer.getProfile(sessionID);
|
const profile = this.saveServer.getProfile(sessionID);
|
||||||
profile.insurance = profile.insurance.filter(insurance =>
|
profile.insurance = profile.insurance.filter((insurance) =>
|
||||||
insurance.messageContent.systemData.date !== packageInfo.date ||
|
insurance.messageContent.systemData.date !== packageInfo.date ||
|
||||||
insurance.messageContent.systemData.time !== packageInfo.time ||
|
insurance.messageContent.systemData.time !== packageInfo.time ||
|
||||||
insurance.messageContent.systemData.location !== packageInfo.location
|
insurance.messageContent.systemData.location !== packageInfo.location
|
||||||
);
|
);
|
||||||
|
|
||||||
this.logger.debug(`Removed insurance package with date: ${packageInfo.date}, time: ${packageInfo.time}, and location: ${packageInfo.location} from profile ${sessionID}. Remaining packages: ${profile.insurance.length}`);
|
this.logger.debug(
|
||||||
|
`Removed insurance package with date: ${packageInfo.date}, time: ${packageInfo.time}, and location: ${packageInfo.location} from profile ${sessionID}. Remaining packages: ${profile.insurance.length}`,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -184,7 +190,9 @@ export class InsuranceController
|
|||||||
const parentAttachmentsMap = this.populateParentAttachmentsMap(insured, itemsMap);
|
const parentAttachmentsMap = this.populateParentAttachmentsMap(insured, itemsMap);
|
||||||
|
|
||||||
// Check to see if any regular items are present.
|
// Check to see if any regular items are present.
|
||||||
const hasRegularItems = Array.from(itemsMap.values()).some(item => !this.itemHelper.isAttachmentAttached(item));
|
const hasRegularItems = Array.from(itemsMap.values()).some((item) =>
|
||||||
|
!this.itemHelper.isAttachmentAttached(item)
|
||||||
|
);
|
||||||
|
|
||||||
// Process all items that are not attached, attachments. Those are handled separately, by value.
|
// Process all items that are not attached, attachments. Those are handled separately, by value.
|
||||||
if (hasRegularItems)
|
if (hasRegularItems)
|
||||||
@ -238,12 +246,14 @@ export class InsuranceController
|
|||||||
for (const insuredItem of insured.items)
|
for (const insuredItem of insured.items)
|
||||||
{
|
{
|
||||||
// Use the parent ID from the item to get the parent item.
|
// Use the parent ID from the item to get the parent item.
|
||||||
const parentItem = insured.items.find(item => item._id === insuredItem.parentId);
|
const parentItem = insured.items.find((item) => item._id === insuredItem.parentId);
|
||||||
|
|
||||||
// The parent (not the hideout) could not be found. Skip and warn.
|
// The parent (not the hideout) could not be found. Skip and warn.
|
||||||
if (!parentItem && insuredItem.parentId !== this.fetchHideoutItemParent(insured.items))
|
if (!parentItem && insuredItem.parentId !== this.fetchHideoutItemParent(insured.items))
|
||||||
{
|
{
|
||||||
this.logger.warning(`Could not find parent for insured item - ID: ${insuredItem._id}, Template: ${insuredItem._tpl}, Parent ID: ${insuredItem.parentId}`);
|
this.logger.warning(
|
||||||
|
`Could not find parent for insured item - ID: ${insuredItem._id}, Template: ${insuredItem._tpl}, Parent ID: ${insuredItem.parentId}`,
|
||||||
|
);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -261,7 +271,9 @@ export class InsuranceController
|
|||||||
if (!mainParent)
|
if (!mainParent)
|
||||||
{
|
{
|
||||||
// Odd. The parent couldn't be found. Skip this attachment and warn.
|
// Odd. The parent couldn't be found. Skip this attachment and warn.
|
||||||
this.logger.warning(`Could not find main-parent for insured attachment - ID: ${insuredItem._id}, Template: ${insuredItem._tpl}, Parent ID: ${insuredItem.parentId}`);
|
this.logger.warning(
|
||||||
|
`Could not find main-parent for insured attachment - ID: ${insuredItem._id}, Template: ${insuredItem._tpl}, Parent ID: ${insuredItem.parentId}`,
|
||||||
|
);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -310,8 +322,10 @@ export class InsuranceController
|
|||||||
|
|
||||||
// Check if the item has any children and mark those for deletion as well, but only if those
|
// Check if the item has any children and mark those for deletion as well, but only if those
|
||||||
// children are currently attached attachments.
|
// children are currently attached attachments.
|
||||||
const directChildren = insured.items.filter(item => item.parentId === insuredItem._id);
|
const directChildren = insured.items.filter((item) => item.parentId === insuredItem._id);
|
||||||
const allChildrenAreAttachments = directChildren.every(child => this.itemHelper.isAttachmentAttached(child));
|
const allChildrenAreAttachments = directChildren.every((child) =>
|
||||||
|
this.itemHelper.isAttachmentAttached(child)
|
||||||
|
);
|
||||||
if (allChildrenAreAttachments)
|
if (allChildrenAreAttachments)
|
||||||
{
|
{
|
||||||
for (const item of itemAndChildren)
|
for (const item of itemAndChildren)
|
||||||
@ -331,9 +345,14 @@ export class InsuranceController
|
|||||||
* @param traderId The trader ID from the Insurance object.
|
* @param traderId The trader ID from the Insurance object.
|
||||||
* @param toDelete A Set object to keep track of items marked for deletion.
|
* @param toDelete A Set object to keep track of items marked for deletion.
|
||||||
*/
|
*/
|
||||||
protected processAttachments(mainParentToAttachmentsMap: Map<string, Item[]>, itemsMap: Map<string, Item>, traderId: string, toDelete: Set<string>): void
|
protected processAttachments(
|
||||||
|
mainParentToAttachmentsMap: Map<string, Item[]>,
|
||||||
|
itemsMap: Map<string, Item>,
|
||||||
|
traderId: string,
|
||||||
|
toDelete: Set<string>,
|
||||||
|
): void
|
||||||
{
|
{
|
||||||
for (const [ parentId, attachmentItems ] of mainParentToAttachmentsMap)
|
for (const [parentId, attachmentItems] of mainParentToAttachmentsMap)
|
||||||
{
|
{
|
||||||
// Log the parent item's name.
|
// Log the parent item's name.
|
||||||
const parentItem = itemsMap.get(parentId);
|
const parentItem = itemsMap.get(parentId);
|
||||||
@ -375,10 +394,10 @@ export class InsuranceController
|
|||||||
*/
|
*/
|
||||||
protected sortAttachmentsByPrice(attachments: Item[]): EnrichedItem[]
|
protected sortAttachmentsByPrice(attachments: Item[]): EnrichedItem[]
|
||||||
{
|
{
|
||||||
return attachments.map(item => ({
|
return attachments.map((item) => ({
|
||||||
...item,
|
...item,
|
||||||
name: this.itemHelper.getItemName(item._tpl),
|
name: this.itemHelper.getItemName(item._tpl),
|
||||||
maxPrice: this.itemHelper.getItemMaxPrice(item._tpl)
|
maxPrice: this.itemHelper.getItemMaxPrice(item._tpl),
|
||||||
})).sort((a, b) => b.maxPrice - a.maxPrice);
|
})).sort((a, b) => b.maxPrice - a.maxPrice);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -389,7 +408,7 @@ export class InsuranceController
|
|||||||
*/
|
*/
|
||||||
protected logAttachmentsDetails(attachments: EnrichedItem[]): void
|
protected logAttachmentsDetails(attachments: EnrichedItem[]): void
|
||||||
{
|
{
|
||||||
for ( const attachment of attachments)
|
for (const attachment of attachments)
|
||||||
{
|
{
|
||||||
this.logger.debug(`Child Item - Name: ${attachment.name}, Max Price: ${attachment.maxPrice}`);
|
this.logger.debug(`Child Item - Name: ${attachment.name}, Max Price: ${attachment.maxPrice}`);
|
||||||
}
|
}
|
||||||
@ -404,7 +423,7 @@ export class InsuranceController
|
|||||||
*/
|
*/
|
||||||
protected countSuccessfulRolls(attachments: Item[], traderId: string): number
|
protected countSuccessfulRolls(attachments: Item[], traderId: string): number
|
||||||
{
|
{
|
||||||
const rolls = Array.from({ length: attachments.length }, () => this.rollForDelete(traderId));
|
const rolls = Array.from({length: attachments.length}, () => this.rollForDelete(traderId));
|
||||||
return rolls.filter(Boolean).length;
|
return rolls.filter(Boolean).length;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -415,16 +434,20 @@ export class InsuranceController
|
|||||||
* @param successfulRolls The number of successful rolls.
|
* @param successfulRolls The number of successful rolls.
|
||||||
* @param toDelete The array that accumulates the IDs of the items to be deleted.
|
* @param toDelete The array that accumulates the IDs of the items to be deleted.
|
||||||
*/
|
*/
|
||||||
protected attachmentDeletionByValue(attachments: EnrichedItem[], successfulRolls: number, toDelete: Set<string>): void
|
protected attachmentDeletionByValue(
|
||||||
|
attachments: EnrichedItem[],
|
||||||
|
successfulRolls: number,
|
||||||
|
toDelete: Set<string>,
|
||||||
|
): void
|
||||||
{
|
{
|
||||||
const valuableToDelete = attachments.slice(0, successfulRolls).map(({ _id }) => _id);
|
const valuableToDelete = attachments.slice(0, successfulRolls).map(({_id}) => _id);
|
||||||
|
|
||||||
for (const attachmentsId of valuableToDelete)
|
for (const attachmentsId of valuableToDelete)
|
||||||
{
|
{
|
||||||
const valuableChild = attachments.find(({ _id }) => _id === attachmentsId);
|
const valuableChild = attachments.find(({_id}) => _id === attachmentsId);
|
||||||
if (valuableChild)
|
if (valuableChild)
|
||||||
{
|
{
|
||||||
const { name, maxPrice } = valuableChild;
|
const {name, maxPrice} = valuableChild;
|
||||||
this.logger.debug(`Marked for removal - Child Item: ${name}, Max Price: ${maxPrice}`);
|
this.logger.debug(`Marked for removal - Child Item: ${name}, Max Price: ${maxPrice}`);
|
||||||
toDelete.add(attachmentsId);
|
toDelete.add(attachmentsId);
|
||||||
}
|
}
|
||||||
@ -440,7 +463,7 @@ export class InsuranceController
|
|||||||
*/
|
*/
|
||||||
protected removeItemsFromInsurance(insured: Insurance, toDelete: Set<string>): void
|
protected removeItemsFromInsurance(insured: Insurance, toDelete: Set<string>): void
|
||||||
{
|
{
|
||||||
insured.items = insured.items.filter(item => !toDelete.has(item._id));
|
insured.items = insured.items.filter((item) => !toDelete.has(item._id));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -457,7 +480,7 @@ export class InsuranceController
|
|||||||
for (const item of insured.items)
|
for (const item of insured.items)
|
||||||
{
|
{
|
||||||
// Check if the item's parent exists in the insured items list.
|
// Check if the item's parent exists in the insured items list.
|
||||||
const parentExists = insured.items.some(parentItem => parentItem._id === item.parentId);
|
const parentExists = insured.items.some((parentItem) => parentItem._id === item.parentId);
|
||||||
|
|
||||||
// If the parent does not exist and the item is not already a 'hideout' item, adopt the orphaned item.
|
// If the parent does not exist and the item is not already a 'hideout' item, adopt the orphaned item.
|
||||||
if (!parentExists && item.parentId !== hideoutParentId && item.slotId !== "hideout")
|
if (!parentExists && item.parentId !== hideoutParentId && item.slotId !== "hideout")
|
||||||
@ -478,7 +501,7 @@ export class InsuranceController
|
|||||||
*/
|
*/
|
||||||
protected fetchHideoutItemParent(items: Item[]): string
|
protected fetchHideoutItemParent(items: Item[]): string
|
||||||
{
|
{
|
||||||
const hideoutItem = items.find(item => item.slotId === "hideout");
|
const hideoutItem = items.find((item) => item.slotId === "hideout");
|
||||||
const hideoutParentId = hideoutItem ? hideoutItem?.parentId : "";
|
const hideoutParentId = hideoutItem ? hideoutItem?.parentId : "";
|
||||||
|
|
||||||
if (hideoutParentId === "")
|
if (hideoutParentId === "")
|
||||||
@ -502,7 +525,8 @@ export class InsuranceController
|
|||||||
// successfully "failed" to return anything and an appropriate message should be sent to the player.
|
// successfully "failed" to return anything and an appropriate message should be sent to the player.
|
||||||
if (insurance.items.length === 0)
|
if (insurance.items.length === 0)
|
||||||
{
|
{
|
||||||
const insuranceFailedTemplates = this.databaseServer.getTables().traders[insurance.traderId].dialogue.insuranceFailed;
|
const insuranceFailedTemplates =
|
||||||
|
this.databaseServer.getTables().traders[insurance.traderId].dialogue.insuranceFailed;
|
||||||
insurance.messageContent.templateId = this.randomUtil.getArrayValue(insuranceFailedTemplates);
|
insurance.messageContent.templateId = this.randomUtil.getArrayValue(insuranceFailedTemplates);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -514,7 +538,7 @@ export class InsuranceController
|
|||||||
insurance.messageContent.templateId,
|
insurance.messageContent.templateId,
|
||||||
insurance.items,
|
insurance.items,
|
||||||
insurance.messageContent.maxStorageTime,
|
insurance.messageContent.maxStorageTime,
|
||||||
insurance.messageContent.systemData
|
insurance.messageContent.systemData,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -544,7 +568,9 @@ export class InsuranceController
|
|||||||
// Log the roll with as much detail as possible.
|
// Log the roll with as much detail as possible.
|
||||||
const itemName = insuredItem ? ` for "${this.itemHelper.getItemName(insuredItem._tpl)}"` : "";
|
const itemName = insuredItem ? ` for "${this.itemHelper.getItemName(insuredItem._tpl)}"` : "";
|
||||||
const status = roll ? "Delete" : "Keep";
|
const status = roll ? "Delete" : "Keep";
|
||||||
this.logger.debug(`Rolling deletion${itemName} with ${trader} - Return ${traderReturnChance}% - Roll: ${returnChance} - Status: ${status}`);
|
this.logger.debug(
|
||||||
|
`Rolling deletion${itemName} with ${trader} - Return ${traderReturnChance}% - Roll: ${returnChance} - Status: ${status}`,
|
||||||
|
);
|
||||||
|
|
||||||
return roll;
|
return roll;
|
||||||
}
|
}
|
||||||
@ -575,21 +601,18 @@ export class InsuranceController
|
|||||||
{
|
{
|
||||||
itemsToPay.push({
|
itemsToPay.push({
|
||||||
id: inventoryItemsHash[key]._id,
|
id: inventoryItemsHash[key]._id,
|
||||||
count: Math.round(this.insuranceService.getPremium(pmcData, inventoryItemsHash[key], body.tid))
|
count: Math.round(this.insuranceService.getPremium(pmcData, inventoryItemsHash[key], body.tid)),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const options: IProcessBuyTradeRequestData = {
|
const options: IProcessBuyTradeRequestData = {
|
||||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
||||||
scheme_items: itemsToPay,
|
scheme_items: itemsToPay,
|
||||||
tid: body.tid,
|
tid: body.tid,
|
||||||
Action: "",
|
Action: "",
|
||||||
type: "",
|
type: "",
|
||||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
||||||
item_id: "",
|
item_id: "",
|
||||||
count: 0,
|
count: 0,
|
||||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
scheme_id: 0,
|
||||||
scheme_id: 0
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// pay for the item insurance
|
// pay for the item insurance
|
||||||
@ -604,7 +627,7 @@ export class InsuranceController
|
|||||||
{
|
{
|
||||||
pmcData.InsuredItems.push({
|
pmcData.InsuredItems.push({
|
||||||
tid: body.tid,
|
tid: body.tid,
|
||||||
itemId: inventoryItemsHash[key]._id
|
itemId: inventoryItemsHash[key]._id,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -645,7 +668,9 @@ export class InsuranceController
|
|||||||
this.logger.debug(`Item with id: ${itemId} missing from player inventory, skipping`);
|
this.logger.debug(`Item with id: ${itemId} missing from player inventory, skipping`);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
items[inventoryItemsHash[itemId]._tpl] = Math.round(this.insuranceService.getPremium(pmcData, inventoryItemsHash[itemId], trader));
|
items[inventoryItemsHash[itemId]._tpl] = Math.round(
|
||||||
|
this.insuranceService.getPremium(pmcData, inventoryItemsHash[itemId], trader),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
output[trader] = items;
|
output[trader] = items;
|
||||||
|
@ -62,21 +62,25 @@ export class InventoryController
|
|||||||
@inject("LocalisationService") protected localisationService: LocalisationService,
|
@inject("LocalisationService") protected localisationService: LocalisationService,
|
||||||
@inject("LootGenerator") protected lootGenerator: LootGenerator,
|
@inject("LootGenerator") protected lootGenerator: LootGenerator,
|
||||||
@inject("EventOutputHolder") protected eventOutputHolder: EventOutputHolder,
|
@inject("EventOutputHolder") protected eventOutputHolder: EventOutputHolder,
|
||||||
@inject("HttpResponseUtil") protected httpResponseUtil: HttpResponseUtil
|
@inject("HttpResponseUtil") protected httpResponseUtil: HttpResponseUtil,
|
||||||
)
|
)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Move Item
|
* Move Item
|
||||||
* change location of item with parentId and slotId
|
* change location of item with parentId and slotId
|
||||||
* transfers items from one profile to another if fromOwner/toOwner is set in the body.
|
* transfers items from one profile to another if fromOwner/toOwner is set in the body.
|
||||||
* otherwise, move is contained within the same profile_f.
|
* otherwise, move is contained within the same profile_f.
|
||||||
* @param pmcData Profile
|
* @param pmcData Profile
|
||||||
* @param moveRequest Move request data
|
* @param moveRequest Move request data
|
||||||
* @param sessionID Session id
|
* @param sessionID Session id
|
||||||
* @returns IItemEventRouterResponse
|
* @returns IItemEventRouterResponse
|
||||||
*/
|
*/
|
||||||
public moveItem(pmcData: IPmcData, moveRequest: IInventoryMoveRequestData, sessionID: string): IItemEventRouterResponse
|
public moveItem(
|
||||||
|
pmcData: IPmcData,
|
||||||
|
moveRequest: IInventoryMoveRequestData,
|
||||||
|
sessionID: string,
|
||||||
|
): IItemEventRouterResponse
|
||||||
{
|
{
|
||||||
const output = this.eventOutputHolder.getOutput(sessionID);
|
const output = this.eventOutputHolder.getOutput(sessionID);
|
||||||
|
|
||||||
@ -89,14 +93,14 @@ export class InventoryController
|
|||||||
const ownerInventoryItems = this.inventoryHelper.getOwnerInventoryItems(moveRequest, sessionID);
|
const ownerInventoryItems = this.inventoryHelper.getOwnerInventoryItems(moveRequest, sessionID);
|
||||||
if (ownerInventoryItems.sameInventory)
|
if (ownerInventoryItems.sameInventory)
|
||||||
{
|
{
|
||||||
// Dont move items from trader to profile, this can happen when editing a traders preset weapons
|
// Don't move items from trader to profile, this can happen when editing a traders preset weapons
|
||||||
if (moveRequest.fromOwner?.type === "Trader" && !ownerInventoryItems.isMail)
|
if (moveRequest.fromOwner?.type === "Trader" && !ownerInventoryItems.isMail)
|
||||||
{
|
{
|
||||||
return this.getTraderExploitErrorResponse(output);
|
return this.getTraderExploitErrorResponse(output);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for item in inventory before allowing internal transfer
|
// Check for item in inventory before allowing internal transfer
|
||||||
const originalItemLocation = ownerInventoryItems.from.find(x => x._id === moveRequest.item);
|
const originalItemLocation = ownerInventoryItems.from.find((x) => x._id === moveRequest.item);
|
||||||
if (!originalItemLocation)
|
if (!originalItemLocation)
|
||||||
{
|
{
|
||||||
// Internal item move but item never existed, possible dupe glitch
|
// Internal item move but item never existed, possible dupe glitch
|
||||||
@ -123,14 +127,23 @@ export class InventoryController
|
|||||||
*/
|
*/
|
||||||
protected getTraderExploitErrorResponse(output: IItemEventRouterResponse): IItemEventRouterResponse
|
protected getTraderExploitErrorResponse(output: IItemEventRouterResponse): IItemEventRouterResponse
|
||||||
{
|
{
|
||||||
return this.httpResponseUtil.appendErrorToOutput(output, this.localisationService.getText("inventory-edit_trader_item"), <BackendErrorCodes>228);
|
return this.httpResponseUtil.appendErrorToOutput(
|
||||||
|
output,
|
||||||
|
this.localisationService.getText("inventory-edit_trader_item"),
|
||||||
|
<BackendErrorCodes>228,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove Item from Profile
|
* Remove Item from Profile
|
||||||
* Deep tree item deletion, also removes items from insurance list
|
* Deep tree item deletion, also removes items from insurance list
|
||||||
*/
|
*/
|
||||||
public removeItem(pmcData: IPmcData, itemId: string, sessionID: string, output: IItemEventRouterResponse = undefined): IItemEventRouterResponse
|
public removeItem(
|
||||||
|
pmcData: IPmcData,
|
||||||
|
itemId: string,
|
||||||
|
sessionID: string,
|
||||||
|
output: IItemEventRouterResponse = undefined,
|
||||||
|
): IItemEventRouterResponse
|
||||||
{
|
{
|
||||||
return this.inventoryHelper.removeItem(pmcData, itemId, sessionID, output);
|
return this.inventoryHelper.removeItem(pmcData, itemId, sessionID, output);
|
||||||
}
|
}
|
||||||
@ -140,29 +153,46 @@ export class InventoryController
|
|||||||
* Implements functionality "Discard" from Main menu (Stash etc.)
|
* Implements functionality "Discard" from Main menu (Stash etc.)
|
||||||
* Removes item from PMC Profile
|
* Removes item from PMC Profile
|
||||||
*/
|
*/
|
||||||
public discardItem(pmcData: IPmcData, body: IInventoryRemoveRequestData, sessionID: string): IItemEventRouterResponse
|
public discardItem(
|
||||||
|
pmcData: IPmcData,
|
||||||
|
body: IInventoryRemoveRequestData,
|
||||||
|
sessionID: string,
|
||||||
|
): IItemEventRouterResponse
|
||||||
{
|
{
|
||||||
if (body.fromOwner?.type === "Mail")
|
if (body.fromOwner?.type === "Mail")
|
||||||
{
|
{
|
||||||
return this.inventoryHelper.removeItemAndChildrenFromMailRewards(sessionID, body, this.eventOutputHolder.getOutput(sessionID));
|
return this.inventoryHelper.removeItemAndChildrenFromMailRewards(
|
||||||
|
sessionID,
|
||||||
|
body,
|
||||||
|
this.eventOutputHolder.getOutput(sessionID),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const profileToRemoveItemFrom = (!body.fromOwner || body.fromOwner.id === pmcData._id)
|
const profileToRemoveItemFrom = (!body.fromOwner || body.fromOwner.id === pmcData._id) ?
|
||||||
? pmcData
|
pmcData :
|
||||||
: this.profileHelper.getFullProfile(sessionID).characters.scav;
|
this.profileHelper.getFullProfile(sessionID).characters.scav;
|
||||||
|
|
||||||
return this.inventoryHelper.removeItem(profileToRemoveItemFrom, body.item, sessionID, this.eventOutputHolder.getOutput(sessionID));
|
return this.inventoryHelper.removeItem(
|
||||||
|
profileToRemoveItemFrom,
|
||||||
|
body.item,
|
||||||
|
sessionID,
|
||||||
|
this.eventOutputHolder.getOutput(sessionID),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Split Item
|
* Split Item
|
||||||
* spliting 1 stack into 2
|
* splitting 1 stack into 2
|
||||||
* @param pmcData Player profile (unused, getOwnerInventoryItems() gets profile)
|
* @param pmcData Player profile (unused, getOwnerInventoryItems() gets profile)
|
||||||
* @param request Split request
|
* @param request Split request
|
||||||
* @param sessionID Session/player id
|
* @param sessionID Session/player id
|
||||||
* @returns IItemEventRouterResponse
|
* @returns IItemEventRouterResponse
|
||||||
*/
|
*/
|
||||||
public splitItem(pmcData: IPmcData, request: IInventorySplitRequestData, sessionID: string): IItemEventRouterResponse
|
public splitItem(
|
||||||
|
pmcData: IPmcData,
|
||||||
|
request: IInventorySplitRequestData,
|
||||||
|
sessionID: string,
|
||||||
|
): IItemEventRouterResponse
|
||||||
{
|
{
|
||||||
const output = this.eventOutputHolder.getOutput(sessionID);
|
const output = this.eventOutputHolder.getOutput(sessionID);
|
||||||
|
|
||||||
@ -172,15 +202,16 @@ export class InventoryController
|
|||||||
// Handle cartridge edge-case
|
// Handle cartridge edge-case
|
||||||
if (!request.container.location && request.container.container === "cartridges")
|
if (!request.container.location && request.container.container === "cartridges")
|
||||||
{
|
{
|
||||||
const matchingItems = inventoryItems.to.filter(x => x.parentId === request.container.id);
|
const matchingItems = inventoryItems.to.filter((x) => x.parentId === request.container.id);
|
||||||
request.container.location = matchingItems.length; // Wrong location for first cartridge
|
request.container.location = matchingItems.length; // Wrong location for first cartridge
|
||||||
}
|
}
|
||||||
|
|
||||||
// The item being merged has three possible sources: pmc, scav or mail, getOwnerInventoryItems() handles getting correct one
|
// The item being merged has three possible sources: pmc, scav or mail, getOwnerInventoryItems() handles getting
|
||||||
const itemToSplit = inventoryItems.from.find(x => x._id === request.splitItem);
|
// correct one.
|
||||||
|
const itemToSplit = inventoryItems.from.find((x) => x._id === request.splitItem);
|
||||||
if (!itemToSplit)
|
if (!itemToSplit)
|
||||||
{
|
{
|
||||||
const errorMessage = (`Unable to split stack as source item: ${request.splitItem} cannot be found`);
|
const errorMessage = `Unable to split stack as source item: ${request.splitItem} cannot be found`;
|
||||||
this.logger.error(errorMessage);
|
this.logger.error(errorMessage);
|
||||||
|
|
||||||
return this.httpResponseUtil.appendErrorToOutput(output, errorMessage);
|
return this.httpResponseUtil.appendErrorToOutput(output, errorMessage);
|
||||||
@ -197,7 +228,7 @@ export class InventoryController
|
|||||||
output.profileChanges[sessionID].items.new.push({
|
output.profileChanges[sessionID].items.new.push({
|
||||||
_id: request.newItem,
|
_id: request.newItem,
|
||||||
_tpl: itemToSplit._tpl,
|
_tpl: itemToSplit._tpl,
|
||||||
upd: updatedUpd
|
upd: updatedUpd,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Update player inventory
|
// Update player inventory
|
||||||
@ -207,7 +238,7 @@ export class InventoryController
|
|||||||
parentId: request.container.id,
|
parentId: request.container.id,
|
||||||
slotId: request.container.container,
|
slotId: request.container.container,
|
||||||
location: request.container.location,
|
location: request.container.location,
|
||||||
upd: updatedUpd
|
upd: updatedUpd,
|
||||||
});
|
});
|
||||||
|
|
||||||
return output;
|
return output;
|
||||||
@ -229,20 +260,20 @@ export class InventoryController
|
|||||||
const inventoryItems = this.inventoryHelper.getOwnerInventoryItems(body, sessionID);
|
const inventoryItems = this.inventoryHelper.getOwnerInventoryItems(body, sessionID);
|
||||||
|
|
||||||
// Get source item (can be from player or trader or mail)
|
// Get source item (can be from player or trader or mail)
|
||||||
const sourceItem = inventoryItems.from.find(x => x._id === body.item);
|
const sourceItem = inventoryItems.from.find((x) => x._id === body.item);
|
||||||
if (!sourceItem)
|
if (!sourceItem)
|
||||||
{
|
{
|
||||||
const errorMessage = (`Unable to merge stacks as source item: ${body.with} cannot be found`);
|
const errorMessage = `Unable to merge stacks as source item: ${body.with} cannot be found`;
|
||||||
this.logger.error(errorMessage);
|
this.logger.error(errorMessage);
|
||||||
|
|
||||||
return this.httpResponseUtil.appendErrorToOutput(output, errorMessage);
|
return this.httpResponseUtil.appendErrorToOutput(output, errorMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get item being merged into
|
// Get item being merged into
|
||||||
const destinationItem = inventoryItems.to.find(x => x._id === body.with);
|
const destinationItem = inventoryItems.to.find((x) => x._id === body.with);
|
||||||
if (!destinationItem)
|
if (!destinationItem)
|
||||||
{
|
{
|
||||||
const errorMessage = (`Unable to merge stacks as destination item: ${body.with} cannot be found`);
|
const errorMessage = `Unable to merge stacks as destination item: ${body.with} cannot be found`;
|
||||||
this.logger.error(errorMessage);
|
this.logger.error(errorMessage);
|
||||||
|
|
||||||
return this.httpResponseUtil.appendErrorToOutput(output, errorMessage);
|
return this.httpResponseUtil.appendErrorToOutput(output, errorMessage);
|
||||||
@ -250,29 +281,29 @@ export class InventoryController
|
|||||||
|
|
||||||
if (!(destinationItem.upd?.StackObjectsCount))
|
if (!(destinationItem.upd?.StackObjectsCount))
|
||||||
{
|
{
|
||||||
// No stackcount on destination, add one
|
// No stack count on destination, add one
|
||||||
destinationItem.upd = { StackObjectsCount: 1 };
|
destinationItem.upd = {StackObjectsCount: 1};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!sourceItem.upd)
|
if (!sourceItem.upd)
|
||||||
{
|
{
|
||||||
sourceItem.upd = {
|
sourceItem.upd = {
|
||||||
StackObjectsCount: 1
|
StackObjectsCount: 1,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
else if (!sourceItem.upd.StackObjectsCount)
|
else if (!sourceItem.upd.StackObjectsCount)
|
||||||
{
|
{
|
||||||
// Items pulled out of raid can have no stackcount if the stack should be 1
|
// Items pulled out of raid can have no stack count if the stack should be 1
|
||||||
sourceItem.upd.StackObjectsCount = 1;
|
sourceItem.upd.StackObjectsCount = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
destinationItem.upd.StackObjectsCount += sourceItem.upd.StackObjectsCount; // Add source stackcount to destination
|
destinationItem.upd.StackObjectsCount += sourceItem.upd.StackObjectsCount; // Add source stack count to destination
|
||||||
output.profileChanges[sessionID].items.del.push({ _id: sourceItem._id }); // Inform client source item being deleted
|
output.profileChanges[sessionID].items.del.push({_id: sourceItem._id}); // Inform client source item being deleted
|
||||||
|
|
||||||
const indexOfItemToRemove = inventoryItems.from.findIndex(x => x._id === sourceItem._id);
|
const indexOfItemToRemove = inventoryItems.from.findIndex((x) => x._id === sourceItem._id);
|
||||||
if (indexOfItemToRemove === -1)
|
if (indexOfItemToRemove === -1)
|
||||||
{
|
{
|
||||||
const errorMessage = (`Unable to find item: ${sourceItem._id} to remove from sender inventory`);
|
const errorMessage = `Unable to find item: ${sourceItem._id} to remove from sender inventory`;
|
||||||
this.logger.error(errorMessage);
|
this.logger.error(errorMessage);
|
||||||
|
|
||||||
return this.httpResponseUtil.appendErrorToOutput(output, errorMessage);
|
return this.httpResponseUtil.appendErrorToOutput(output, errorMessage);
|
||||||
@ -283,8 +314,8 @@ export class InventoryController
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO: Adds no data to output to send to client, is this by design?
|
* // TODO: Adds no data to output to send to client, is this by design?
|
||||||
* TODO: should make use of getOwnerInventoryItems(), stack being transferred may not always be on pmc
|
* // TODO: should make use of getOwnerInventoryItems(), stack being transferred may not always be on pmc
|
||||||
* Transfer items from one stack into another while keeping original stack
|
* Transfer items from one stack into another while keeping original stack
|
||||||
* Used to take items from scav inventory into stash or to insert ammo into mags (shotgun ones) and reloading weapon by clicking "Reload"
|
* Used to take items from scav inventory into stash or to insert ammo into mags (shotgun ones) and reloading weapon by clicking "Reload"
|
||||||
* @param pmcData Player profile
|
* @param pmcData Player profile
|
||||||
@ -292,7 +323,11 @@ export class InventoryController
|
|||||||
* @param sessionID Session id
|
* @param sessionID Session id
|
||||||
* @returns IItemEventRouterResponse
|
* @returns IItemEventRouterResponse
|
||||||
*/
|
*/
|
||||||
public transferItem(pmcData: IPmcData, body: IInventoryTransferRequestData, sessionID: string): IItemEventRouterResponse
|
public transferItem(
|
||||||
|
pmcData: IPmcData,
|
||||||
|
body: IInventoryTransferRequestData,
|
||||||
|
sessionID: string,
|
||||||
|
): IItemEventRouterResponse
|
||||||
{
|
{
|
||||||
const output = this.eventOutputHolder.getOutput(sessionID);
|
const output = this.eventOutputHolder.getOutput(sessionID);
|
||||||
|
|
||||||
@ -359,7 +394,7 @@ export class InventoryController
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Object.assign(destinationItem, { upd: { StackObjectsCount: 1 } });
|
Object.assign(destinationItem, {upd: {StackObjectsCount: 1}});
|
||||||
}
|
}
|
||||||
|
|
||||||
destinationItem.upd.StackObjectsCount = destinationStackCount + body.count;
|
destinationItem.upd.StackObjectsCount = destinationStackCount + body.count;
|
||||||
@ -368,28 +403,28 @@ export class InventoryController
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Swap Item
|
* Swap Item
|
||||||
* its used for "reload" if you have weapon in hands and magazine is somewhere else in rig or backpack in equipment
|
* its used for "reload" if you have weapon in hands and magazine is somewhere else in rig or backpack in equipment
|
||||||
* Also used to swap items using quick selection on character screen
|
* Also used to swap items using quick selection on character screen
|
||||||
*/
|
*/
|
||||||
public swapItem(pmcData: IPmcData, request: IInventorySwapRequestData, sessionID: string): IItemEventRouterResponse
|
public swapItem(pmcData: IPmcData, request: IInventorySwapRequestData, sessionID: string): IItemEventRouterResponse
|
||||||
{
|
{
|
||||||
const itemOne = pmcData.Inventory.items.find(x => x._id === request.item);
|
const itemOne = pmcData.Inventory.items.find((x) => x._id === request.item);
|
||||||
if (!itemOne)
|
if (!itemOne)
|
||||||
{
|
{
|
||||||
this.logger.error(`Unable to find item: ${request.item} to swap positions with: ${request.item2}`);
|
this.logger.error(`Unable to find item: ${request.item} to swap positions with: ${request.item2}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const itemTwo = pmcData.Inventory.items.find(x => x._id === request.item2);
|
const itemTwo = pmcData.Inventory.items.find((x) => x._id === request.item2);
|
||||||
if (!itemTwo)
|
if (!itemTwo)
|
||||||
{
|
{
|
||||||
this.logger.error(`Unable to find item: ${request.item2} to swap positions with: ${request.item}`);
|
this.logger.error(`Unable to find item: ${request.item2} to swap positions with: ${request.item}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// to.id is the parentid
|
// to.id is the parentId
|
||||||
itemOne.parentId = request.to.id;
|
itemOne.parentId = request.to.id;
|
||||||
|
|
||||||
// to.container is the slotid
|
// to.container is the slotId
|
||||||
itemOne.slotId = request.to.container;
|
itemOne.slotId = request.to.container;
|
||||||
|
|
||||||
// Request object has location data, add it in, otherwise remove existing location from object
|
// Request object has location data, add it in, otherwise remove existing location from object
|
||||||
@ -423,9 +458,11 @@ export class InventoryController
|
|||||||
public foldItem(pmcData: IPmcData, body: IInventoryFoldRequestData, sessionID: string): IItemEventRouterResponse
|
public foldItem(pmcData: IPmcData, body: IInventoryFoldRequestData, sessionID: string): IItemEventRouterResponse
|
||||||
{
|
{
|
||||||
// Fix for folding weapons while on they're in the Scav inventory
|
// Fix for folding weapons while on they're in the Scav inventory
|
||||||
if (body.fromOwner
|
if (
|
||||||
&& body.fromOwner.type === "Profile"
|
body.fromOwner &&
|
||||||
&& body.fromOwner.id !== pmcData._id)
|
body.fromOwner.type === "Profile" &&
|
||||||
|
body.fromOwner.id !== pmcData._id
|
||||||
|
)
|
||||||
{
|
{
|
||||||
pmcData = this.profileHelper.getScavProfile(sessionID);
|
pmcData = this.profileHelper.getScavProfile(sessionID);
|
||||||
}
|
}
|
||||||
@ -434,19 +471,19 @@ export class InventoryController
|
|||||||
{
|
{
|
||||||
if (item._id && item._id === body.item)
|
if (item._id && item._id === body.item)
|
||||||
{
|
{
|
||||||
item.upd.Foldable = { Folded: body.value };
|
item.upd.Foldable = {Folded: body.value};
|
||||||
return this.eventOutputHolder.getOutput(sessionID);
|
return this.eventOutputHolder.getOutput(sessionID);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
warnings: [],
|
warnings: [],
|
||||||
profileChanges: {}
|
profileChanges: {},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Toggles "Toggleable" items like night vision goggles and face shields.
|
* Toggles "toggleable" items like night vision goggles and face shields.
|
||||||
* @param pmcData player profile
|
* @param pmcData player profile
|
||||||
* @param body Toggle request
|
* @param body Toggle request
|
||||||
* @param sessionID Session id
|
* @param sessionID Session id
|
||||||
@ -460,27 +497,31 @@ export class InventoryController
|
|||||||
pmcData = this.profileHelper.getScavProfile(sessionID);
|
pmcData = this.profileHelper.getScavProfile(sessionID);
|
||||||
}
|
}
|
||||||
|
|
||||||
const itemToToggle = pmcData.Inventory.items.find(x => x._id === body.item);
|
const itemToToggle = pmcData.Inventory.items.find((x) => x._id === body.item);
|
||||||
if (itemToToggle)
|
if (itemToToggle)
|
||||||
{
|
{
|
||||||
if (!itemToToggle.upd)
|
if (!itemToToggle.upd)
|
||||||
{
|
{
|
||||||
this.logger.warning(this.localisationService.getText("inventory-item_to_toggle_missing_upd", itemToToggle._id));
|
this.logger.warning(
|
||||||
|
this.localisationService.getText("inventory-item_to_toggle_missing_upd", itemToToggle._id),
|
||||||
|
);
|
||||||
itemToToggle.upd = {};
|
itemToToggle.upd = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
itemToToggle.upd.Togglable = { On: body.value };
|
itemToToggle.upd.Togglable = {On: body.value};
|
||||||
|
|
||||||
return this.eventOutputHolder.getOutput(sessionID);
|
return this.eventOutputHolder.getOutput(sessionID);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
this.logger.warning(this.localisationService.getText("inventory-unable_to_toggle_item_not_found", body.item));
|
this.logger.warning(
|
||||||
|
this.localisationService.getText("inventory-unable_to_toggle_item_not_found", body.item),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
warnings: [],
|
warnings: [],
|
||||||
profileChanges: {}
|
profileChanges: {},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -499,11 +540,11 @@ export class InventoryController
|
|||||||
{
|
{
|
||||||
if ("upd" in item)
|
if ("upd" in item)
|
||||||
{
|
{
|
||||||
item.upd.Tag = { Color: body.TagColor, Name: body.TagName };
|
item.upd.Tag = {Color: body.TagColor, Name: body.TagName};
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
item.upd = { Tag: { Color: body.TagColor, Name: body.TagName } };
|
item.upd = {Tag: {Color: body.TagColor, Name: body.TagName}};
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.eventOutputHolder.getOutput(sessionID);
|
return this.eventOutputHolder.getOutput(sessionID);
|
||||||
@ -512,18 +553,22 @@ export class InventoryController
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
warnings: [],
|
warnings: [],
|
||||||
profileChanges: {}
|
profileChanges: {},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Bind an inventory item to the quick access menu at bottom of player screen
|
* Bind an inventory item to the quick access menu at bottom of player screen
|
||||||
* @param pmcData Player profile
|
* @param pmcData Player profile
|
||||||
* @param bindRequest Reqeust object
|
* @param bindRequest Request object
|
||||||
* @param sessionID Session id
|
* @param sessionID Session id
|
||||||
* @returns IItemEventRouterResponse
|
* @returns IItemEventRouterResponse
|
||||||
*/
|
*/
|
||||||
public bindItem(pmcData: IPmcData, bindRequest: IInventoryBindRequestData, sessionID: string): IItemEventRouterResponse
|
public bindItem(
|
||||||
|
pmcData: IPmcData,
|
||||||
|
bindRequest: IInventoryBindRequestData,
|
||||||
|
sessionID: string,
|
||||||
|
): IItemEventRouterResponse
|
||||||
{
|
{
|
||||||
for (const index in pmcData.Inventory.fastPanel)
|
for (const index in pmcData.Inventory.fastPanel)
|
||||||
{
|
{
|
||||||
@ -538,7 +583,6 @@ export class InventoryController
|
|||||||
return this.eventOutputHolder.getOutput(sessionID);
|
return this.eventOutputHolder.getOutput(sessionID);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles examining an item
|
* Handles examining an item
|
||||||
* @param pmcData player profile
|
* @param pmcData player profile
|
||||||
@ -546,7 +590,11 @@ export class InventoryController
|
|||||||
* @param sessionID session id
|
* @param sessionID session id
|
||||||
* @returns response
|
* @returns response
|
||||||
*/
|
*/
|
||||||
public examineItem(pmcData: IPmcData, body: IInventoryExamineRequestData, sessionID: string): IItemEventRouterResponse
|
public examineItem(
|
||||||
|
pmcData: IPmcData,
|
||||||
|
body: IInventoryExamineRequestData,
|
||||||
|
sessionID: string,
|
||||||
|
): IItemEventRouterResponse
|
||||||
{
|
{
|
||||||
let itemId = "";
|
let itemId = "";
|
||||||
if ("fromOwner" in body)
|
if ("fromOwner" in body)
|
||||||
@ -606,9 +654,9 @@ export class InventoryController
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the tplid of an item from the examine request object
|
* Get the tplId of an item from the examine request object
|
||||||
* @param body response request
|
* @param body response request
|
||||||
* @returns tplid
|
* @returns string
|
||||||
*/
|
*/
|
||||||
protected getExaminedItemTpl(body: IInventoryExamineRequestData): string
|
protected getExaminedItemTpl(body: IInventoryExamineRequestData): string
|
||||||
{
|
{
|
||||||
@ -618,49 +666,55 @@ export class InventoryController
|
|||||||
}
|
}
|
||||||
else if (body.fromOwner.id === Traders.FENCE)
|
else if (body.fromOwner.id === Traders.FENCE)
|
||||||
{
|
{
|
||||||
// get tpl from fence assorts
|
// Get tpl from fence assorts
|
||||||
return this.fenceService.getRawFenceAssorts().items.find(x => x._id === body.item)._tpl;
|
return this.fenceService.getRawFenceAssorts().items.find((x) => x._id === body.item)._tpl;
|
||||||
}
|
}
|
||||||
else if (body.fromOwner.type === "Trader") // not fence
|
else if (body.fromOwner.type === "Trader")
|
||||||
{
|
{
|
||||||
// get tpl from trader assort
|
// Not fence
|
||||||
return this.databaseServer.getTables().traders[body.fromOwner.id].assort.items.find(item => item._id === body.item)._tpl;
|
// Get tpl from trader assort
|
||||||
|
return this.databaseServer.getTables().traders[body.fromOwner.id].assort.items.find((item) =>
|
||||||
|
item._id === body.item
|
||||||
|
)._tpl;
|
||||||
}
|
}
|
||||||
else if (body.fromOwner.type === "RagFair")
|
else if (body.fromOwner.type === "RagFair")
|
||||||
{
|
{
|
||||||
// try to get tplid from items.json first
|
// try to get tplId from items.json first
|
||||||
const item = this.databaseServer.getTables().templates.items[body.item];
|
const item = this.databaseServer.getTables().templates.items[body.item];
|
||||||
if (item)
|
if (item)
|
||||||
{
|
{
|
||||||
return item._id;
|
return item._id;
|
||||||
}
|
}
|
||||||
|
|
||||||
// try alternate way of getting offer if first approach fails
|
// Try alternate way of getting offer if first approach fails
|
||||||
let offer = this.ragfairOfferService.getOfferByOfferId(body.item);
|
let offer = this.ragfairOfferService.getOfferByOfferId(body.item);
|
||||||
if (!offer)
|
if (!offer)
|
||||||
{
|
{
|
||||||
offer = this.ragfairOfferService.getOfferByOfferId(body.fromOwner.id);
|
offer = this.ragfairOfferService.getOfferByOfferId(body.fromOwner.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
// try find examine item inside offer items array
|
// Try find examine item inside offer items array
|
||||||
const matchingItem = offer.items.find(x => x._id === body.item);
|
const matchingItem = offer.items.find((x) => x._id === body.item);
|
||||||
if (matchingItem)
|
if (matchingItem)
|
||||||
{
|
{
|
||||||
return matchingItem._tpl;
|
return matchingItem._tpl;
|
||||||
}
|
}
|
||||||
|
|
||||||
// unable to find item in database or ragfair
|
// Unable to find item in database or ragfair
|
||||||
throw new Error(this.localisationService.getText("inventory-unable_to_find_item", body.item));
|
throw new Error(this.localisationService.getText("inventory-unable_to_find_item", body.item));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public readEncyclopedia(pmcData: IPmcData, body: IInventoryReadEncyclopediaRequestData, sessionID: string): IItemEventRouterResponse
|
public readEncyclopedia(
|
||||||
|
pmcData: IPmcData,
|
||||||
|
body: IInventoryReadEncyclopediaRequestData,
|
||||||
|
sessionID: string,
|
||||||
|
): IItemEventRouterResponse
|
||||||
{
|
{
|
||||||
for (const id of body.ids)
|
for (const id of body.ids)
|
||||||
{
|
{
|
||||||
pmcData.Encyclopedia[id] = true;
|
pmcData.Encyclopedia[id] = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.eventOutputHolder.getOutput(sessionID);
|
return this.eventOutputHolder.getOutput(sessionID);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -672,15 +726,20 @@ export class InventoryController
|
|||||||
* @param sessionID Session id
|
* @param sessionID Session id
|
||||||
* @returns IItemEventRouterResponse
|
* @returns IItemEventRouterResponse
|
||||||
*/
|
*/
|
||||||
public sortInventory(pmcData: IPmcData, request: IInventorySortRequestData, sessionID: string): IItemEventRouterResponse
|
public sortInventory(
|
||||||
|
pmcData: IPmcData,
|
||||||
|
request: IInventorySortRequestData,
|
||||||
|
sessionID: string,
|
||||||
|
): IItemEventRouterResponse
|
||||||
{
|
{
|
||||||
|
|
||||||
for (const change of request.changedItems)
|
for (const change of request.changedItems)
|
||||||
{
|
{
|
||||||
const inventoryItem = pmcData.Inventory.items.find(x => x._id === change._id);
|
const inventoryItem = pmcData.Inventory.items.find((x) => x._id === change._id);
|
||||||
if (!inventoryItem)
|
if (!inventoryItem)
|
||||||
{
|
{
|
||||||
this.logger.error(`Unable to find inventory item: ${change._id} to auto-sort, YOU MUST RELOAD YOUR GAME`);
|
this.logger.error(
|
||||||
|
`Unable to find inventory item: ${change._id} to auto-sort, YOU MUST RELOAD YOUR GAME`,
|
||||||
|
);
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -707,13 +766,17 @@ export class InventoryController
|
|||||||
* @param sessionID Session id
|
* @param sessionID Session id
|
||||||
* @returns IItemEventRouterResponse
|
* @returns IItemEventRouterResponse
|
||||||
*/
|
*/
|
||||||
public createMapMarker(pmcData: IPmcData, request: IInventoryCreateMarkerRequestData, sessionID: string): IItemEventRouterResponse
|
public createMapMarker(
|
||||||
|
pmcData: IPmcData,
|
||||||
|
request: IInventoryCreateMarkerRequestData,
|
||||||
|
sessionID: string,
|
||||||
|
): IItemEventRouterResponse
|
||||||
{
|
{
|
||||||
// Get map from inventory
|
// Get map from inventory
|
||||||
const mapItem = pmcData.Inventory.items.find(i => i._id === request.item);
|
const mapItem = pmcData.Inventory.items.find((i) => i._id === request.item);
|
||||||
|
|
||||||
// add marker
|
// add marker
|
||||||
mapItem.upd.Map = mapItem.upd.Map || { Markers: [] };
|
mapItem.upd.Map = mapItem.upd.Map || {Markers: []};
|
||||||
request.mapMarker.Note = this.sanitiseMapMarkerText(request.mapMarker.Note);
|
request.mapMarker.Note = this.sanitiseMapMarkerText(request.mapMarker.Note);
|
||||||
mapItem.upd.Map.Markers.push(request.mapMarker);
|
mapItem.upd.Map.Markers.push(request.mapMarker);
|
||||||
|
|
||||||
@ -731,10 +794,14 @@ export class InventoryController
|
|||||||
* @param sessionID Session id
|
* @param sessionID Session id
|
||||||
* @returns IItemEventRouterResponse
|
* @returns IItemEventRouterResponse
|
||||||
*/
|
*/
|
||||||
public deleteMapMarker(pmcData: IPmcData, request: IInventoryDeleteMarkerRequestData, sessionID: string): IItemEventRouterResponse
|
public deleteMapMarker(
|
||||||
|
pmcData: IPmcData,
|
||||||
|
request: IInventoryDeleteMarkerRequestData,
|
||||||
|
sessionID: string,
|
||||||
|
): IItemEventRouterResponse
|
||||||
{
|
{
|
||||||
// Get map from inventory
|
// Get map from inventory
|
||||||
const mapItem = pmcData.Inventory.items.find(i => i._id === request.item);
|
const mapItem = pmcData.Inventory.items.find((i) => i._id === request.item);
|
||||||
|
|
||||||
// remove marker
|
// remove marker
|
||||||
const markers = mapItem.upd.Map.Markers.filter((marker) =>
|
const markers = mapItem.upd.Map.Markers.filter((marker) =>
|
||||||
@ -756,13 +823,17 @@ export class InventoryController
|
|||||||
* @param sessionID Session id
|
* @param sessionID Session id
|
||||||
* @returns IItemEventRouterResponse
|
* @returns IItemEventRouterResponse
|
||||||
*/
|
*/
|
||||||
public editMapMarker(pmcData: IPmcData, request: IInventoryEditMarkerRequestData, sessionID: string): IItemEventRouterResponse
|
public editMapMarker(
|
||||||
|
pmcData: IPmcData,
|
||||||
|
request: IInventoryEditMarkerRequestData,
|
||||||
|
sessionID: string,
|
||||||
|
): IItemEventRouterResponse
|
||||||
{
|
{
|
||||||
// Get map from inventory
|
// Get map from inventory
|
||||||
const mapItem = pmcData.Inventory.items.find(i => i._id === request.item);
|
const mapItem = pmcData.Inventory.items.find((i) => i._id === request.item);
|
||||||
|
|
||||||
// edit marker
|
// edit marker
|
||||||
const indexOfExistingNote = mapItem.upd.Map.Markers.findIndex(m => m.X === request.X && m.Y === request.Y);
|
const indexOfExistingNote = mapItem.upd.Map.Markers.findIndex((m) => m.X === request.X && m.Y === request.Y);
|
||||||
request.mapMarker.Note = this.sanitiseMapMarkerText(request.mapMarker.Note);
|
request.mapMarker.Note = this.sanitiseMapMarkerText(request.mapMarker.Note);
|
||||||
mapItem.upd.Map.Markers[indexOfExistingNote] = request.mapMarker;
|
mapItem.upd.Map.Markers[indexOfExistingNote] = request.mapMarker;
|
||||||
|
|
||||||
@ -791,15 +862,19 @@ export class InventoryController
|
|||||||
* @param sessionID Session id
|
* @param sessionID Session id
|
||||||
* @returns IItemEventRouterResponse
|
* @returns IItemEventRouterResponse
|
||||||
*/
|
*/
|
||||||
public openRandomLootContainer(pmcData: IPmcData, body: IOpenRandomLootContainerRequestData, sessionID: string): IItemEventRouterResponse
|
public openRandomLootContainer(
|
||||||
|
pmcData: IPmcData,
|
||||||
|
body: IOpenRandomLootContainerRequestData,
|
||||||
|
sessionID: string,
|
||||||
|
): IItemEventRouterResponse
|
||||||
{
|
{
|
||||||
const openedItem = pmcData.Inventory.items.find(x => x._id === body.item);
|
const openedItem = pmcData.Inventory.items.find((x) => x._id === body.item);
|
||||||
const containerDetails = this.itemHelper.getItem(openedItem._tpl);
|
const containerDetails = this.itemHelper.getItem(openedItem._tpl);
|
||||||
const isSealedWeaponBox = containerDetails[1]._name.includes("event_container_airdrop");
|
const isSealedWeaponBox = containerDetails[1]._name.includes("event_container_airdrop");
|
||||||
|
|
||||||
const newItemRequest: IAddItemRequestData = {
|
const newItemRequest: IAddItemRequestData = {
|
||||||
tid: "RandomLootContainer",
|
tid: "RandomLootContainer",
|
||||||
items: []
|
items: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
let foundInRaid = false;
|
let foundInRaid = false;
|
||||||
|
@ -30,7 +30,7 @@ export class LauncherController
|
|||||||
@inject("DatabaseServer") protected databaseServer: DatabaseServer,
|
@inject("DatabaseServer") protected databaseServer: DatabaseServer,
|
||||||
@inject("LocalisationService") protected localisationService: LocalisationService,
|
@inject("LocalisationService") protected localisationService: LocalisationService,
|
||||||
@inject("PreAkiModLoader") protected preAkiModLoader: PreAkiModLoader,
|
@inject("PreAkiModLoader") protected preAkiModLoader: PreAkiModLoader,
|
||||||
@inject("ConfigServer") protected configServer: ConfigServer
|
@inject("ConfigServer") protected configServer: ConfigServer,
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
this.coreConfig = this.configServer.getConfig(ConfigTypes.CORE);
|
this.coreConfig = this.configServer.getConfig(ConfigTypes.CORE);
|
||||||
@ -42,30 +42,26 @@ export class LauncherController
|
|||||||
backendUrl: this.httpServerHelper.getBackendUrl(),
|
backendUrl: this.httpServerHelper.getBackendUrl(),
|
||||||
name: this.coreConfig.serverName,
|
name: this.coreConfig.serverName,
|
||||||
editions: Object.keys(this.databaseServer.getTables().templates.profiles),
|
editions: Object.keys(this.databaseServer.getTables().templates.profiles),
|
||||||
profileDescriptions: this.getProfileDescriptions()
|
profileDescriptions: this.getProfileDescriptions(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get descriptive text for each of the profile edtions a player can choose
|
* Get descriptive text for each of the profile editions a player can choose
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
protected getProfileDescriptions(): Record<string, string>
|
protected getProfileDescriptions(): Record<string, string>
|
||||||
{
|
{
|
||||||
return {
|
return {
|
||||||
|
/* eslint-disable @typescript-eslint/naming-convention */
|
||||||
Standard: this.localisationService.getText("launcher-profile_standard"),
|
Standard: this.localisationService.getText("launcher-profile_standard"),
|
||||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
||||||
"Left Behind": this.localisationService.getText("launcher-profile_leftbehind"),
|
"Left Behind": this.localisationService.getText("launcher-profile_leftbehind"),
|
||||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
||||||
"Prepare To Escape": this.localisationService.getText("launcher-profile_preparetoescape"),
|
"Prepare To Escape": this.localisationService.getText("launcher-profile_preparetoescape"),
|
||||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
||||||
"Edge Of Darkness": this.localisationService.getText("launcher-edgeofdarkness"),
|
"Edge Of Darkness": this.localisationService.getText("launcher-edgeofdarkness"),
|
||||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
||||||
"SPT Easy start": this.localisationService.getText("launcher-profile_spteasystart"),
|
"SPT Easy start": this.localisationService.getText("launcher-profile_spteasystart"),
|
||||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
||||||
"SPT Zero to hero": this.localisationService.getText("launcher-profile_sptzerotohero"),
|
"SPT Zero to hero": this.localisationService.getText("launcher-profile_sptzerotohero"),
|
||||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
"SPT Developer": this.localisationService.getText("launcher-profile_sptdeveloper"),
|
||||||
"SPT Developer": this.localisationService.getText("launcher-profile_sptdeveloper")
|
/* eslint-enable @typescript-eslint/naming-convention */
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -115,12 +111,13 @@ export class LauncherController
|
|||||||
username: info.username,
|
username: info.username,
|
||||||
password: info.password,
|
password: info.password,
|
||||||
wipe: true,
|
wipe: true,
|
||||||
edition: info.edition
|
edition: info.edition,
|
||||||
};
|
};
|
||||||
this.saveServer.createProfile(newProfileDetails);
|
this.saveServer.createProfile(newProfileDetails);
|
||||||
|
|
||||||
this.saveServer.loadProfile(sessionID);
|
this.saveServer.loadProfile(sessionID);
|
||||||
this.saveServer.saveProfile(sessionID);
|
this.saveServer.saveProfile(sessionID);
|
||||||
|
|
||||||
return sessionID;
|
return sessionID;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,7 +41,7 @@ export class LocationController
|
|||||||
@inject("LootGenerator") protected lootGenerator: LootGenerator,
|
@inject("LootGenerator") protected lootGenerator: LootGenerator,
|
||||||
@inject("DatabaseServer") protected databaseServer: DatabaseServer,
|
@inject("DatabaseServer") protected databaseServer: DatabaseServer,
|
||||||
@inject("TimeUtil") protected timeUtil: TimeUtil,
|
@inject("TimeUtil") protected timeUtil: TimeUtil,
|
||||||
@inject("ConfigServer") protected configServer: ConfigServer
|
@inject("ConfigServer") protected configServer: ConfigServer,
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
this.airdropConfig = this.configServer.getConfig(ConfigTypes.AIRDROP);
|
this.airdropConfig = this.configServer.getConfig(ConfigTypes.AIRDROP);
|
||||||
@ -89,16 +89,22 @@ export class LocationController
|
|||||||
const staticLoot = this.locationGenerator.generateStaticContainers(location.base, staticAmmoDist);
|
const staticLoot = this.locationGenerator.generateStaticContainers(location.base, staticAmmoDist);
|
||||||
output.Loot.push(...staticLoot);
|
output.Loot.push(...staticLoot);
|
||||||
|
|
||||||
// Add dyanmic loot to output loot
|
// Add dynamic loot to output loot
|
||||||
const dynamicLootDist: ILooseLoot = this.jsonUtil.clone(location.looseLoot);
|
const dynamicLootDist: ILooseLoot = this.jsonUtil.clone(location.looseLoot);
|
||||||
const dynamicSpawnPoints: SpawnpointTemplate[] = this.locationGenerator.generateDynamicLoot(dynamicLootDist, staticAmmoDist, name);
|
const dynamicSpawnPoints: SpawnpointTemplate[] = this.locationGenerator.generateDynamicLoot(
|
||||||
|
dynamicLootDist,
|
||||||
|
staticAmmoDist,
|
||||||
|
name,
|
||||||
|
);
|
||||||
for (const spawnPoint of dynamicSpawnPoints)
|
for (const spawnPoint of dynamicSpawnPoints)
|
||||||
{
|
{
|
||||||
output.Loot.push(spawnPoint);
|
output.Loot.push(spawnPoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Done generating, log results
|
// Done generating, log results
|
||||||
this.logger.success(this.localisationService.getText("location-dynamic_items_spawned_success", dynamicSpawnPoints.length));
|
this.logger.success(
|
||||||
|
this.localisationService.getText("location-dynamic_items_spawned_success", dynamicSpawnPoints.length),
|
||||||
|
);
|
||||||
this.logger.success(this.localisationService.getText("location-generated_success", name));
|
this.logger.success(this.localisationService.getText("location-generated_success", name));
|
||||||
|
|
||||||
return output;
|
return output;
|
||||||
@ -110,7 +116,6 @@ export class LocationController
|
|||||||
* @param sessionId Players Id
|
* @param sessionId Players Id
|
||||||
* @returns ILocationsGenerateAllResponse
|
* @returns ILocationsGenerateAllResponse
|
||||||
*/
|
*/
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
||||||
public generateAll(sessionId: string): ILocationsGenerateAllResponse
|
public generateAll(sessionId: string): ILocationsGenerateAllResponse
|
||||||
{
|
{
|
||||||
const locationsFromDb = this.databaseServer.getTables().locations;
|
const locationsFromDb = this.databaseServer.getTables().locations;
|
||||||
@ -130,15 +135,15 @@ export class LocationController
|
|||||||
locations[mapBase._Id] = mapBase;
|
locations[mapBase._Id] = mapBase;
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
locations: locations,
|
locations: locations,
|
||||||
paths: locationsFromDb.base.paths
|
paths: locationsFromDb.base.paths,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle client/location/getAirdropLoot
|
* Handle client/location/getAirdropLoot
|
||||||
* Get loot for an airdop container
|
* Get loot for an airdrop container
|
||||||
* Generates it randomly based on config/airdrop.json values
|
* Generates it randomly based on config/airdrop.json values
|
||||||
* @returns Array of LootItem objects
|
* @returns Array of LootItem objects
|
||||||
*/
|
*/
|
||||||
@ -174,7 +179,9 @@ export class LocationController
|
|||||||
let lootSettingsByType = this.airdropConfig.loot[airdropType];
|
let lootSettingsByType = this.airdropConfig.loot[airdropType];
|
||||||
if (!lootSettingsByType)
|
if (!lootSettingsByType)
|
||||||
{
|
{
|
||||||
this.logger.error(this.localisationService.getText("location-unable_to_find_airdrop_drop_config_of_type", airdropType));
|
this.logger.error(
|
||||||
|
this.localisationService.getText("location-unable_to_find_airdrop_drop_config_of_type", airdropType),
|
||||||
|
);
|
||||||
lootSettingsByType = this.airdropConfig.loot[AirdropTypeEnum.MIXED];
|
lootSettingsByType = this.airdropConfig.loot[AirdropTypeEnum.MIXED];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -187,7 +194,7 @@ export class LocationController
|
|||||||
itemLimits: lootSettingsByType.itemLimits,
|
itemLimits: lootSettingsByType.itemLimits,
|
||||||
itemStackLimits: lootSettingsByType.itemStackLimits,
|
itemStackLimits: lootSettingsByType.itemStackLimits,
|
||||||
armorLevelWhitelist: lootSettingsByType.armorLevelWhitelist,
|
armorLevelWhitelist: lootSettingsByType.armorLevelWhitelist,
|
||||||
allowBossItems: lootSettingsByType.allowBossItems
|
allowBossItems: lootSettingsByType.allowBossItems,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -56,7 +56,7 @@ export class MatchController
|
|||||||
@inject("BotGenerationCacheService") protected botGenerationCacheService: BotGenerationCacheService,
|
@inject("BotGenerationCacheService") protected botGenerationCacheService: BotGenerationCacheService,
|
||||||
@inject("MailSendService") protected mailSendService: MailSendService,
|
@inject("MailSendService") protected mailSendService: MailSendService,
|
||||||
@inject("LootGenerator") protected lootGenerator: LootGenerator,
|
@inject("LootGenerator") protected lootGenerator: LootGenerator,
|
||||||
@inject("ApplicationContext") protected applicationContext: ApplicationContext
|
@inject("ApplicationContext") protected applicationContext: ApplicationContext,
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
this.matchConfig = this.configServer.getConfig(ConfigTypes.MATCH);
|
this.matchConfig = this.configServer.getConfig(ConfigTypes.MATCH);
|
||||||
@ -99,12 +99,11 @@ export class MatchController
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Handle match/group/start_game */
|
/** Handle match/group/start_game */
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
||||||
public joinMatch(info: IJoinMatchRequestData, sessionId: string): IJoinMatchResult
|
public joinMatch(info: IJoinMatchRequestData, sessionId: string): IJoinMatchResult
|
||||||
{
|
{
|
||||||
const output: IJoinMatchResult = {
|
const output: IJoinMatchResult = {
|
||||||
maxPveCountExceeded: false,
|
maxPveCountExceeded: false,
|
||||||
profiles: []
|
profiles: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
// get list of players joining into the match
|
// get list of players joining into the match
|
||||||
@ -120,20 +119,18 @@ export class MatchController
|
|||||||
raidMode: "Online",
|
raidMode: "Online",
|
||||||
mode: "deathmatch",
|
mode: "deathmatch",
|
||||||
shortid: null,
|
shortid: null,
|
||||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
additional_info: null,
|
||||||
additional_info: null
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Handle client/match/group/status */
|
/** Handle client/match/group/status */
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
||||||
public getGroupStatus(info: IGetGroupStatusRequestData): any
|
public getGroupStatus(info: IGetGroupStatusRequestData): any
|
||||||
{
|
{
|
||||||
return {
|
return {
|
||||||
players: [],
|
players: [],
|
||||||
maxPveCountExceeded: false
|
maxPveCountExceeded: false,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -147,12 +144,14 @@ export class MatchController
|
|||||||
// Store request data for access during bot generation
|
// Store request data for access during bot generation
|
||||||
this.applicationContext.addValue(ContextVariableType.RAID_CONFIGURATION, request);
|
this.applicationContext.addValue(ContextVariableType.RAID_CONFIGURATION, request);
|
||||||
|
|
||||||
//TODO: add code to strip PMC of equipment now they've started the raid
|
// TODO: add code to strip PMC of equipment now they've started the raid
|
||||||
|
|
||||||
// Set pmcs to difficulty set in pre-raid screen if override in bot config isnt enabled
|
// Set PMCs to difficulty set in pre-raid screen if override in bot config isn't enabled
|
||||||
if (!this.pmcConfig.useDifficultyOverride)
|
if (!this.pmcConfig.useDifficultyOverride)
|
||||||
{
|
{
|
||||||
this.pmcConfig.difficulty = this.convertDifficultyDropdownIntoBotDifficulty(request.wavesSettings.botDifficulty);
|
this.pmcConfig.difficulty = this.convertDifficultyDropdownIntoBotDifficulty(
|
||||||
|
request.wavesSettings.botDifficulty,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store the profile as-is for later use on the post-raid exp screen
|
// Store the profile as-is for later use on the post-raid exp screen
|
||||||
@ -235,9 +234,9 @@ export class MatchController
|
|||||||
parentId: parentId,
|
parentId: parentId,
|
||||||
upd: {
|
upd: {
|
||||||
StackObjectsCount: item.stackCount,
|
StackObjectsCount: item.stackCount,
|
||||||
SpawnedInSession: true
|
SpawnedInSession: true,
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -248,7 +247,7 @@ export class MatchController
|
|||||||
MessageType.MESSAGE_WITH_ITEMS,
|
MessageType.MESSAGE_WITH_ITEMS,
|
||||||
this.randomUtil.getArrayValue(this.traderConfig.fence.coopExtractGift.messageLocaleIds),
|
this.randomUtil.getArrayValue(this.traderConfig.fence.coopExtractGift.messageLocaleIds),
|
||||||
mailableLoot,
|
mailableLoot,
|
||||||
this.timeUtil.getHoursAsSeconds(this.traderConfig.fence.coopExtractGift.giftExpiryHours)
|
this.timeUtil.getHoursAsSeconds(this.traderConfig.fence.coopExtractGift.giftExpiryHours),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -296,7 +295,9 @@ export class MatchController
|
|||||||
this.traderHelper.lvlUp(fenceId, pmcData);
|
this.traderHelper.lvlUp(fenceId, pmcData);
|
||||||
pmcData.TradersInfo[fenceId].loyaltyLevel = Math.max(pmcData.TradersInfo[fenceId].loyaltyLevel, 1);
|
pmcData.TradersInfo[fenceId].loyaltyLevel = Math.max(pmcData.TradersInfo[fenceId].loyaltyLevel, 1);
|
||||||
|
|
||||||
this.logger.debug(`Car extract: ${extractName} used, total times taken: ${pmcData.CarExtractCounts[extractName]}`);
|
this.logger.debug(
|
||||||
|
`Car extract: ${extractName} used, total times taken: ${pmcData.CarExtractCounts[extractName]}`,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -309,8 +310,8 @@ export class MatchController
|
|||||||
{
|
{
|
||||||
let fenceStanding = Number(pmcData.TradersInfo[fenceId].standing);
|
let fenceStanding = Number(pmcData.TradersInfo[fenceId].standing);
|
||||||
|
|
||||||
// Not exact replica of Live behaviour
|
// Not exact replica of Live behaviour... Simplified for now. No real reason to do the whole (unconfirmed)
|
||||||
// Simplified for now, no real reason to do the whole (unconfirmed) extra 0.01 standing per day regeneration mechanic
|
// extra 0.01 standing per day regeneration mechanic.
|
||||||
const baseGain: number = this.inraidConfig.carExtractBaseStandingGain;
|
const baseGain: number = this.inraidConfig.carExtractBaseStandingGain;
|
||||||
const extractCount: number = pmcData.CarExtractCounts[extractName];
|
const extractCount: number = pmcData.CarExtractCounts[extractName];
|
||||||
|
|
||||||
|
@ -10,15 +10,15 @@ import { EventOutputHolder } from "@spt-aki/routers/EventOutputHolder";
|
|||||||
export class NoteController
|
export class NoteController
|
||||||
{
|
{
|
||||||
constructor(
|
constructor(
|
||||||
@inject("EventOutputHolder") protected eventOutputHolder: EventOutputHolder
|
@inject("EventOutputHolder") protected eventOutputHolder: EventOutputHolder,
|
||||||
)
|
)
|
||||||
{ }
|
{}
|
||||||
|
|
||||||
public addNote(pmcData: IPmcData, body: INoteActionData, sessionID: string): IItemEventRouterResponse
|
public addNote(pmcData: IPmcData, body: INoteActionData, sessionID: string): IItemEventRouterResponse
|
||||||
{
|
{
|
||||||
const newNote: Note = {
|
const newNote: Note = {
|
||||||
Time: body.note.Time,
|
Time: body.note.Time,
|
||||||
Text: body.note.Text
|
Text: body.note.Text,
|
||||||
};
|
};
|
||||||
pmcData.Notes.Notes.push(newNote);
|
pmcData.Notes.Notes.push(newNote);
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ export class NotifierController
|
|||||||
constructor(
|
constructor(
|
||||||
@inject("NotifierHelper") protected notifierHelper: NotifierHelper,
|
@inject("NotifierHelper") protected notifierHelper: NotifierHelper,
|
||||||
@inject("HttpServerHelper") protected httpServerHelper: HttpServerHelper,
|
@inject("HttpServerHelper") protected httpServerHelper: HttpServerHelper,
|
||||||
@inject("NotificationService") protected notificationService: NotificationService
|
@inject("NotificationService") protected notificationService: NotificationService,
|
||||||
)
|
)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
@ -82,11 +82,10 @@ export class NotifierController
|
|||||||
{
|
{
|
||||||
return {
|
return {
|
||||||
server: this.httpServerHelper.buildUrl(),
|
server: this.httpServerHelper.buildUrl(),
|
||||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
||||||
channel_id: sessionID,
|
channel_id: sessionID,
|
||||||
url: "",
|
url: "",
|
||||||
notifierServer: this.getServer(sessionID),
|
notifierServer: this.getServer(sessionID),
|
||||||
ws: this.notifierHelper.getWebSocketServer(sessionID)
|
ws: this.notifierHelper.getWebSocketServer(sessionID),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,9 +23,9 @@ export class PresetBuildController
|
|||||||
@inject("JsonUtil") protected jsonUtil: JsonUtil,
|
@inject("JsonUtil") protected jsonUtil: JsonUtil,
|
||||||
@inject("DatabaseServer") protected databaseServer: DatabaseServer,
|
@inject("DatabaseServer") protected databaseServer: DatabaseServer,
|
||||||
@inject("ItemHelper") protected itemHelper: ItemHelper,
|
@inject("ItemHelper") protected itemHelper: ItemHelper,
|
||||||
@inject("SaveServer") protected saveServer: SaveServer
|
@inject("SaveServer") protected saveServer: SaveServer,
|
||||||
)
|
)
|
||||||
{ }
|
{}
|
||||||
|
|
||||||
/** Handle client/handbook/builds/my/list */
|
/** Handle client/handbook/builds/my/list */
|
||||||
public getUserBuilds(sessionID: string): IUserBuilds
|
public getUserBuilds(sessionID: string): IUserBuilds
|
||||||
@ -35,21 +35,27 @@ export class PresetBuildController
|
|||||||
{
|
{
|
||||||
profile.userbuilds = {
|
profile.userbuilds = {
|
||||||
equipmentBuilds: [],
|
equipmentBuilds: [],
|
||||||
weaponBuilds: []
|
weaponBuilds: [],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure the secure container in the default presets match what the player has equipped
|
// Ensure the secure container in the default presets match what the player has equipped
|
||||||
const defaultEquipmentPresets = this.jsonUtil.clone(this.databaseServer.getTables().templates.defaultEquipmentPresets);
|
const defaultEquipmentPresets = this.jsonUtil.clone(
|
||||||
const playerSecureContainer = profile.characters.pmc.Inventory.items?.find(x => x.slotId === "SecuredContainer");
|
this.databaseServer.getTables().templates.defaultEquipmentPresets,
|
||||||
const firstDefaultItemsSecureContainer = defaultEquipmentPresets[0]?.items?.find(x => x.slotId === "SecuredContainer");
|
);
|
||||||
|
const playerSecureContainer = profile.characters.pmc.Inventory.items?.find((x) =>
|
||||||
|
x.slotId === "SecuredContainer"
|
||||||
|
);
|
||||||
|
const firstDefaultItemsSecureContainer = defaultEquipmentPresets[0]?.items?.find((x) =>
|
||||||
|
x.slotId === "SecuredContainer"
|
||||||
|
);
|
||||||
if (playerSecureContainer && playerSecureContainer?._tpl !== firstDefaultItemsSecureContainer?._tpl)
|
if (playerSecureContainer && playerSecureContainer?._tpl !== firstDefaultItemsSecureContainer?._tpl)
|
||||||
{
|
{
|
||||||
// Default equipment presets' secure container tpl doesnt match players secure container tpl
|
// Default equipment presets' secure container tpl doesn't match players secure container tpl
|
||||||
for (const defaultPreset of defaultEquipmentPresets)
|
for (const defaultPreset of defaultEquipmentPresets)
|
||||||
{
|
{
|
||||||
// Find presets secure container
|
// Find presets secure container
|
||||||
const secureContainer = defaultPreset.items.find(x => x.slotId === "SecuredContainer");
|
const secureContainer = defaultPreset.items.find((x) => x.slotId === "SecuredContainer");
|
||||||
if (secureContainer)
|
if (secureContainer)
|
||||||
{
|
{
|
||||||
secureContainer._tpl = playerSecureContainer._tpl;
|
secureContainer._tpl = playerSecureContainer._tpl;
|
||||||
@ -65,9 +71,13 @@ export class PresetBuildController
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Handle SaveWeaponBuild event */
|
/** Handle SaveWeaponBuild event */
|
||||||
public saveWeaponBuild(pmcData: IPmcData, body: IPresetBuildActionRequestData, sessionId: string): IItemEventRouterResponse
|
public saveWeaponBuild(
|
||||||
|
pmcData: IPmcData,
|
||||||
|
body: IPresetBuildActionRequestData,
|
||||||
|
sessionId: string,
|
||||||
|
): IItemEventRouterResponse
|
||||||
{
|
{
|
||||||
// TODO - could be merged into saveBuild, maybe
|
// TODO: Could be merged into saveBuild, maybe
|
||||||
const output = this.eventOutputHolder.getOutput(sessionId);
|
const output = this.eventOutputHolder.getOutput(sessionId);
|
||||||
|
|
||||||
// Replace duplicate Id's. The first item is the base item.
|
// Replace duplicate Id's. The first item is the base item.
|
||||||
@ -82,15 +92,19 @@ export class PresetBuildController
|
|||||||
name: body.name,
|
name: body.name,
|
||||||
root: body.root,
|
root: body.root,
|
||||||
items: body.items,
|
items: body.items,
|
||||||
type: "weapon"
|
type: "weapon",
|
||||||
};
|
};
|
||||||
|
|
||||||
const savedWeaponBuilds = this.saveServer.getProfile(sessionId).userbuilds.weaponBuilds;
|
const savedWeaponBuilds = this.saveServer.getProfile(sessionId).userbuilds.weaponBuilds;
|
||||||
const existingBuild = savedWeaponBuilds.find(x => x.id === body.id);
|
const existingBuild = savedWeaponBuilds.find((x) => x.id === body.id);
|
||||||
if (existingBuild)
|
if (existingBuild)
|
||||||
{
|
{
|
||||||
// exists, replace
|
// exists, replace
|
||||||
this.saveServer.getProfile(sessionId).userbuilds.weaponBuilds.splice(savedWeaponBuilds.indexOf(existingBuild), 1, newBuild);
|
this.saveServer.getProfile(sessionId).userbuilds.weaponBuilds.splice(
|
||||||
|
savedWeaponBuilds.indexOf(existingBuild),
|
||||||
|
1,
|
||||||
|
newBuild,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -105,12 +119,21 @@ export class PresetBuildController
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Handle SaveEquipmentBuild event */
|
/** Handle SaveEquipmentBuild event */
|
||||||
public saveEquipmentBuild(pmcData: IPmcData, body: IPresetBuildActionRequestData, sessionID: string): IItemEventRouterResponse
|
public saveEquipmentBuild(
|
||||||
|
pmcData: IPmcData,
|
||||||
|
body: IPresetBuildActionRequestData,
|
||||||
|
sessionID: string,
|
||||||
|
): IItemEventRouterResponse
|
||||||
{
|
{
|
||||||
return this.saveBuild(pmcData, body, sessionID, "equipmentBuilds");
|
return this.saveBuild(pmcData, body, sessionID, "equipmentBuilds");
|
||||||
}
|
}
|
||||||
|
|
||||||
protected saveBuild(pmcData: IPmcData, body: IPresetBuildActionRequestData, sessionID: string, buildType: string): IItemEventRouterResponse
|
protected saveBuild(
|
||||||
|
pmcData: IPmcData,
|
||||||
|
body: IPresetBuildActionRequestData,
|
||||||
|
sessionID: string,
|
||||||
|
buildType: string,
|
||||||
|
): IItemEventRouterResponse
|
||||||
{
|
{
|
||||||
const output = this.eventOutputHolder.getOutput(sessionID);
|
const output = this.eventOutputHolder.getOutput(sessionID);
|
||||||
const existingSavedBuilds: any[] = this.saveServer.getProfile(sessionID).userbuilds[buildType];
|
const existingSavedBuilds: any[] = this.saveServer.getProfile(sessionID).userbuilds[buildType];
|
||||||
@ -125,14 +148,18 @@ export class PresetBuildController
|
|||||||
buildType: "Custom",
|
buildType: "Custom",
|
||||||
root: body.root,
|
root: body.root,
|
||||||
fastPanel: [],
|
fastPanel: [],
|
||||||
items: body.items
|
items: body.items,
|
||||||
};
|
};
|
||||||
|
|
||||||
const existingBuild = existingSavedBuilds.find(x => x.name === body.name);
|
const existingBuild = existingSavedBuilds.find((x) => x.name === body.name);
|
||||||
if (existingBuild)
|
if (existingBuild)
|
||||||
{
|
{
|
||||||
// Already exists, replace
|
// Already exists, replace
|
||||||
this.saveServer.getProfile(sessionID).userbuilds[buildType].splice(existingSavedBuilds.indexOf(existingBuild), 1, newBuild);
|
this.saveServer.getProfile(sessionID).userbuilds[buildType].splice(
|
||||||
|
existingSavedBuilds.indexOf(existingBuild),
|
||||||
|
1,
|
||||||
|
newBuild,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -152,16 +179,24 @@ export class PresetBuildController
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Handle RemoveWeaponBuild event*/
|
/** Handle RemoveWeaponBuild event*/
|
||||||
public removeWeaponBuild(pmcData: IPmcData, body: IPresetBuildActionRequestData, sessionID: string): IItemEventRouterResponse
|
public removeWeaponBuild(
|
||||||
|
pmcData: IPmcData,
|
||||||
|
body: IPresetBuildActionRequestData,
|
||||||
|
sessionID: string,
|
||||||
|
): IItemEventRouterResponse
|
||||||
{
|
{
|
||||||
// todo - does this get called?
|
// TODO: Does this get called?
|
||||||
return this.removePlayerBuild(pmcData, body.id, sessionID);
|
return this.removePlayerBuild(pmcData, body.id, sessionID);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Handle RemoveEquipmentBuild event*/
|
/** Handle RemoveEquipmentBuild event*/
|
||||||
public removeEquipmentBuild(pmcData: IPmcData, body: IPresetBuildActionRequestData, sessionID: string): IItemEventRouterResponse
|
public removeEquipmentBuild(
|
||||||
|
pmcData: IPmcData,
|
||||||
|
body: IPresetBuildActionRequestData,
|
||||||
|
sessionID: string,
|
||||||
|
): IItemEventRouterResponse
|
||||||
{
|
{
|
||||||
// todo - does this get called?
|
// TODO: Does this get called?
|
||||||
return this.removePlayerBuild(pmcData, body.id, sessionID);
|
return this.removePlayerBuild(pmcData, body.id, sessionID);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -171,7 +206,7 @@ export class PresetBuildController
|
|||||||
const equipmentBuilds = this.saveServer.getProfile(sessionID).userbuilds.equipmentBuilds;
|
const equipmentBuilds = this.saveServer.getProfile(sessionID).userbuilds.equipmentBuilds;
|
||||||
|
|
||||||
// Check for id in weapon array first
|
// Check for id in weapon array first
|
||||||
const matchingWeaponBuild = weaponBuilds.find(x => x.id === id);
|
const matchingWeaponBuild = weaponBuilds.find((x) => x.id === id);
|
||||||
if (matchingWeaponBuild)
|
if (matchingWeaponBuild)
|
||||||
{
|
{
|
||||||
weaponBuilds.splice(weaponBuilds.indexOf(matchingWeaponBuild), 1);
|
weaponBuilds.splice(weaponBuilds.indexOf(matchingWeaponBuild), 1);
|
||||||
@ -180,7 +215,7 @@ export class PresetBuildController
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Id not found in weapons, try equipment
|
// Id not found in weapons, try equipment
|
||||||
const matchingEquipmentBuild = equipmentBuilds.find(x => x.id === id);
|
const matchingEquipmentBuild = equipmentBuilds.find((x) => x.id === id);
|
||||||
if (matchingEquipmentBuild)
|
if (matchingEquipmentBuild)
|
||||||
{
|
{
|
||||||
equipmentBuilds.splice(equipmentBuilds.indexOf(matchingEquipmentBuild), 1);
|
equipmentBuilds.splice(equipmentBuilds.indexOf(matchingEquipmentBuild), 1);
|
||||||
@ -192,7 +227,6 @@ export class PresetBuildController
|
|||||||
this.logger.error(`Unable to delete preset, cannot find ${id} in weapon or equipment presets`);
|
this.logger.error(`Unable to delete preset, cannot find ${id} in weapon or equipment presets`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return this.eventOutputHolder.getOutput(sessionID);
|
return this.eventOutputHolder.getOutput(sessionID);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -9,9 +9,9 @@ export class PresetController
|
|||||||
{
|
{
|
||||||
constructor(
|
constructor(
|
||||||
@inject("PresetHelper") protected presetHelper: PresetHelper,
|
@inject("PresetHelper") protected presetHelper: PresetHelper,
|
||||||
@inject("DatabaseServer") protected databaseServer: DatabaseServer
|
@inject("DatabaseServer") protected databaseServer: DatabaseServer,
|
||||||
)
|
)
|
||||||
{ }
|
{}
|
||||||
|
|
||||||
public initialize(): void
|
public initialize(): void
|
||||||
{
|
{
|
||||||
|
@ -47,9 +47,9 @@ export class ProfileController
|
|||||||
@inject("TraderHelper") protected traderHelper: TraderHelper,
|
@inject("TraderHelper") protected traderHelper: TraderHelper,
|
||||||
@inject("DialogueHelper") protected dialogueHelper: DialogueHelper,
|
@inject("DialogueHelper") protected dialogueHelper: DialogueHelper,
|
||||||
@inject("QuestHelper") protected questHelper: QuestHelper,
|
@inject("QuestHelper") protected questHelper: QuestHelper,
|
||||||
@inject("ProfileHelper") protected profileHelper: ProfileHelper
|
@inject("ProfileHelper") protected profileHelper: ProfileHelper,
|
||||||
)
|
)
|
||||||
{ }
|
{}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle /launcher/profiles
|
* Handle /launcher/profiles
|
||||||
@ -76,7 +76,7 @@ export class ProfileController
|
|||||||
const pmc = profile.characters.pmc;
|
const pmc = profile.characters.pmc;
|
||||||
|
|
||||||
// make sure character completed creation
|
// make sure character completed creation
|
||||||
if (!((pmc?.Info?.Level)))
|
if (!(pmc?.Info?.Level))
|
||||||
{
|
{
|
||||||
return {
|
return {
|
||||||
username: profile.info.username,
|
username: profile.info.username,
|
||||||
@ -87,7 +87,7 @@ export class ProfileController
|
|||||||
prevexp: 0,
|
prevexp: 0,
|
||||||
nextlvl: 0,
|
nextlvl: 0,
|
||||||
maxlvl: maxlvl,
|
maxlvl: maxlvl,
|
||||||
akiData: this.profileHelper.getDefaultAkiDataObject()
|
akiData: this.profileHelper.getDefaultAkiDataObject(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -102,7 +102,7 @@ export class ProfileController
|
|||||||
prevexp: (currlvl === 0) ? 0 : this.profileHelper.getExperience(currlvl),
|
prevexp: (currlvl === 0) ? 0 : this.profileHelper.getExperience(currlvl),
|
||||||
nextlvl: nextlvl,
|
nextlvl: nextlvl,
|
||||||
maxlvl: maxlvl,
|
maxlvl: maxlvl,
|
||||||
akiData: profile.aki
|
akiData: profile.aki,
|
||||||
};
|
};
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
@ -122,7 +122,8 @@ export class ProfileController
|
|||||||
public createProfile(info: IProfileCreateRequestData, sessionID: string): void
|
public createProfile(info: IProfileCreateRequestData, sessionID: string): void
|
||||||
{
|
{
|
||||||
const account = this.saveServer.getProfile(sessionID).info;
|
const account = this.saveServer.getProfile(sessionID).info;
|
||||||
const profile: TemplateSide = this.databaseServer.getTables().templates.profiles[account.edition][info.side.toLowerCase()];
|
const profile: TemplateSide =
|
||||||
|
this.databaseServer.getTables().templates.profiles[account.edition][info.side.toLowerCase()];
|
||||||
const pmcData = profile.character;
|
const pmcData = profile.character;
|
||||||
|
|
||||||
// Delete existing profile
|
// Delete existing profile
|
||||||
@ -148,11 +149,16 @@ export class ProfileController
|
|||||||
|
|
||||||
if (!pmcData.UnlockedInfo)
|
if (!pmcData.UnlockedInfo)
|
||||||
{
|
{
|
||||||
pmcData.UnlockedInfo = { unlockedProductionRecipe: [] };
|
pmcData.UnlockedInfo = {unlockedProductionRecipe: []};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Change item id's to be unique
|
// Change item id's to be unique
|
||||||
pmcData.Inventory.items = this.itemHelper.replaceIDs(pmcData, pmcData.Inventory.items, null, pmcData.Inventory.fastPanel);
|
pmcData.Inventory.items = this.itemHelper.replaceIDs(
|
||||||
|
pmcData,
|
||||||
|
pmcData.Inventory.items,
|
||||||
|
null,
|
||||||
|
pmcData.Inventory.fastPanel,
|
||||||
|
);
|
||||||
pmcData.Inventory.hideoutAreaStashes = {};
|
pmcData.Inventory.hideoutAreaStashes = {};
|
||||||
|
|
||||||
// Create profile
|
// Create profile
|
||||||
@ -160,7 +166,7 @@ export class ProfileController
|
|||||||
info: account,
|
info: account,
|
||||||
characters: {
|
characters: {
|
||||||
pmc: pmcData,
|
pmc: pmcData,
|
||||||
scav: {} as IPmcData
|
scav: {} as IPmcData,
|
||||||
},
|
},
|
||||||
suits: profile.suits,
|
suits: profile.suits,
|
||||||
userbuilds: profile.userbuilds,
|
userbuilds: profile.userbuilds,
|
||||||
@ -169,7 +175,7 @@ export class ProfileController
|
|||||||
vitality: {} as Vitality,
|
vitality: {} as Vitality,
|
||||||
inraid: {} as Inraid,
|
inraid: {} as Inraid,
|
||||||
insurance: [],
|
insurance: [],
|
||||||
traderPurchases: {}
|
traderPurchases: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
this.profileFixerService.checkForAndFixPmcProfileIssues(profileDetails.characters.pmc);
|
this.profileFixerService.checkForAndFixPmcProfileIssues(profileDetails.characters.pmc);
|
||||||
@ -185,7 +191,11 @@ export class ProfileController
|
|||||||
// Profile is flagged as wanting quests set to ready to hand in and collect rewards
|
// Profile is flagged as wanting quests set to ready to hand in and collect rewards
|
||||||
if (profile.trader.setQuestsAvailableForFinish)
|
if (profile.trader.setQuestsAvailableForFinish)
|
||||||
{
|
{
|
||||||
this.questHelper.addAllQuestsToProfile(profileDetails.characters.pmc, [QuestStatus.AvailableForStart, QuestStatus.Started, QuestStatus.AvailableForFinish]);
|
this.questHelper.addAllQuestsToProfile(profileDetails.characters.pmc, [
|
||||||
|
QuestStatus.AvailableForStart,
|
||||||
|
QuestStatus.Started,
|
||||||
|
QuestStatus.AvailableForFinish,
|
||||||
|
]);
|
||||||
|
|
||||||
// Make unused response so applyQuestReward works
|
// Make unused response so applyQuestReward works
|
||||||
const response = this.eventOutputHolder.getOutput(sessionID);
|
const response = this.eventOutputHolder.getOutput(sessionID);
|
||||||
@ -219,7 +229,9 @@ export class ProfileController
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
this.logger.warning(this.localisationService.getText("profile-unable_to_find_profile_by_id_cannot_delete", sessionID));
|
this.logger.warning(
|
||||||
|
this.localisationService.getText("profile-unable_to_find_profile_by_id_cannot_delete", sessionID),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -230,7 +242,11 @@ export class ProfileController
|
|||||||
* @param sessionID Session id
|
* @param sessionID Session id
|
||||||
* @param response Event router response
|
* @param response Event router response
|
||||||
*/
|
*/
|
||||||
protected givePlayerStartingQuestRewards(profileDetails: IAkiProfile, sessionID: string, response: IItemEventRouterResponse): void
|
protected givePlayerStartingQuestRewards(
|
||||||
|
profileDetails: IAkiProfile,
|
||||||
|
sessionID: string,
|
||||||
|
response: IItemEventRouterResponse,
|
||||||
|
): void
|
||||||
{
|
{
|
||||||
for (const quest of profileDetails.characters.pmc.Quests)
|
for (const quest of profileDetails.characters.pmc.Quests)
|
||||||
{
|
{
|
||||||
@ -238,8 +254,17 @@ export class ProfileController
|
|||||||
|
|
||||||
// Get messageId of text to send to player as text message in game
|
// Get messageId of text to send to player as text message in game
|
||||||
// Copy of code from QuestController.acceptQuest()
|
// Copy of code from QuestController.acceptQuest()
|
||||||
const messageId = this.questHelper.getMessageIdForQuestStart(questFromDb.startedMessageText, questFromDb.description);
|
const messageId = this.questHelper.getMessageIdForQuestStart(
|
||||||
const itemRewards = this.questHelper.applyQuestReward(profileDetails.characters.pmc, quest.qid, QuestStatus.Started, sessionID, response);
|
questFromDb.startedMessageText,
|
||||||
|
questFromDb.description,
|
||||||
|
);
|
||||||
|
const itemRewards = this.questHelper.applyQuestReward(
|
||||||
|
profileDetails.characters.pmc,
|
||||||
|
quest.qid,
|
||||||
|
QuestStatus.Started,
|
||||||
|
sessionID,
|
||||||
|
response,
|
||||||
|
);
|
||||||
|
|
||||||
this.mailSendService.sendLocalisedNpcMessageToPlayer(
|
this.mailSendService.sendLocalisedNpcMessageToPlayer(
|
||||||
sessionID,
|
sessionID,
|
||||||
@ -247,7 +272,8 @@ export class ProfileController
|
|||||||
MessageType.QUEST_START,
|
MessageType.QUEST_START,
|
||||||
messageId,
|
messageId,
|
||||||
itemRewards,
|
itemRewards,
|
||||||
this.timeUtil.getHoursAsSeconds(100));
|
this.timeUtil.getHoursAsSeconds(100),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -323,18 +349,15 @@ export class ProfileController
|
|||||||
/**
|
/**
|
||||||
* Handle client/game/profile/search
|
* Handle client/game/profile/search
|
||||||
*/
|
*/
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
||||||
public getFriends(info: ISearchFriendRequestData, sessionID: string): ISearchFriendResponse[]
|
public getFriends(info: ISearchFriendRequestData, sessionID: string): ISearchFriendResponse[]
|
||||||
{
|
{
|
||||||
return [
|
return [{
|
||||||
{
|
_id: this.hashUtil.generate(),
|
||||||
_id: this.hashUtil.generate(),
|
Info: {
|
||||||
Info: {
|
Level: 1,
|
||||||
Level: 1,
|
Side: "Bear",
|
||||||
Side: "Bear",
|
Nickname: info.nickname,
|
||||||
Nickname: info.nickname
|
},
|
||||||
}
|
}];
|
||||||
}
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -57,7 +57,7 @@ export class QuestController
|
|||||||
@inject("LocaleService") protected localeService: LocaleService,
|
@inject("LocaleService") protected localeService: LocaleService,
|
||||||
@inject("SeasonalEventService") protected seasonalEventService: SeasonalEventService,
|
@inject("SeasonalEventService") protected seasonalEventService: SeasonalEventService,
|
||||||
@inject("LocalisationService") protected localisationService: LocalisationService,
|
@inject("LocalisationService") protected localisationService: LocalisationService,
|
||||||
@inject("ConfigServer") protected configServer: ConfigServer
|
@inject("ConfigServer") protected configServer: ConfigServer,
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
this.questConfig = this.configServer.getConfig(ConfigTypes.QUEST);
|
this.questConfig = this.configServer.getConfig(ConfigTypes.QUEST);
|
||||||
@ -79,7 +79,7 @@ export class QuestController
|
|||||||
for (const quest of allQuests)
|
for (const quest of allQuests)
|
||||||
{
|
{
|
||||||
// Player already accepted the quest, show it regardless of status
|
// Player already accepted the quest, show it regardless of status
|
||||||
const questInProfile = profile.Quests.find(x => x.qid === quest._id);
|
const questInProfile = profile.Quests.find((x) => x.qid === quest._id);
|
||||||
if (questInProfile)
|
if (questInProfile)
|
||||||
{
|
{
|
||||||
quest.sptStatus = questInProfile.status;
|
quest.sptStatus = questInProfile.status;
|
||||||
@ -105,8 +105,12 @@ export class QuestController
|
|||||||
}
|
}
|
||||||
|
|
||||||
const questRequirements = this.questConditionHelper.getQuestConditions(quest.conditions.AvailableForStart);
|
const questRequirements = this.questConditionHelper.getQuestConditions(quest.conditions.AvailableForStart);
|
||||||
const loyaltyRequirements = this.questConditionHelper.getLoyaltyConditions(quest.conditions.AvailableForStart);
|
const loyaltyRequirements = this.questConditionHelper.getLoyaltyConditions(
|
||||||
const standingRequirements = this.questConditionHelper.getStandingConditions(quest.conditions.AvailableForStart);
|
quest.conditions.AvailableForStart,
|
||||||
|
);
|
||||||
|
const standingRequirements = this.questConditionHelper.getStandingConditions(
|
||||||
|
quest.conditions.AvailableForStart,
|
||||||
|
);
|
||||||
|
|
||||||
// Quest has no conditions, standing or loyalty conditions, add to visible quest list
|
// Quest has no conditions, standing or loyalty conditions, add to visible quest list
|
||||||
if (questRequirements.length === 0 && loyaltyRequirements.length === 0 && standingRequirements.length === 0)
|
if (questRequirements.length === 0 && loyaltyRequirements.length === 0 && standingRequirements.length === 0)
|
||||||
@ -122,14 +126,14 @@ export class QuestController
|
|||||||
for (const conditionToFulfil of questRequirements)
|
for (const conditionToFulfil of questRequirements)
|
||||||
{
|
{
|
||||||
// If the previous quest isn't in the user profile, it hasn't been completed or started
|
// If the previous quest isn't in the user profile, it hasn't been completed or started
|
||||||
const prerequisiteQuest = profile.Quests.find(pq => pq.qid === conditionToFulfil._props.target);
|
const prerequisiteQuest = profile.Quests.find((pq) => pq.qid === conditionToFulfil._props.target);
|
||||||
if (!prerequisiteQuest)
|
if (!prerequisiteQuest)
|
||||||
{
|
{
|
||||||
haveCompletedPreviousQuest = false;
|
haveCompletedPreviousQuest = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prereq does not have its status requirement fulfilled
|
// Prerequisite does not have its status requirement fulfilled
|
||||||
if (!conditionToFulfil._props.status.includes(prerequisiteQuest.status))
|
if (!conditionToFulfil._props.status.includes(prerequisiteQuest.status))
|
||||||
{
|
{
|
||||||
haveCompletedPreviousQuest = false;
|
haveCompletedPreviousQuest = false;
|
||||||
@ -144,7 +148,11 @@ export class QuestController
|
|||||||
const unlockTime = previousQuestCompleteTime + conditionToFulfil._props.availableAfter;
|
const unlockTime = previousQuestCompleteTime + conditionToFulfil._props.availableAfter;
|
||||||
if (unlockTime > this.timeUtil.getTimestamp())
|
if (unlockTime > this.timeUtil.getTimestamp())
|
||||||
{
|
{
|
||||||
this.logger.debug(`Quest ${quest.QuestName} is locked for another ${unlockTime - this.timeUtil.getTimestamp()} seconds`);
|
this.logger.debug(
|
||||||
|
`Quest ${quest.QuestName} is locked for another ${
|
||||||
|
unlockTime - this.timeUtil.getTimestamp()
|
||||||
|
} seconds`,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -221,19 +229,28 @@ export class QuestController
|
|||||||
const isHalloweenEventActive = this.seasonalEventService.halloweenEventEnabled();
|
const isHalloweenEventActive = this.seasonalEventService.halloweenEventEnabled();
|
||||||
|
|
||||||
// Not christmas + quest is for christmas
|
// Not christmas + quest is for christmas
|
||||||
if (!isChristmasEventActive && this.seasonalEventService.isQuestRelatedToEvent(questId, SeasonalEventType.CHRISTMAS))
|
if (
|
||||||
|
!isChristmasEventActive &&
|
||||||
|
this.seasonalEventService.isQuestRelatedToEvent(questId, SeasonalEventType.CHRISTMAS)
|
||||||
|
)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Not halloween + quest is for halloween
|
// Not halloween + quest is for halloween
|
||||||
if (!isHalloweenEventActive && this.seasonalEventService.isQuestRelatedToEvent(questId, SeasonalEventType.HALLOWEEN))
|
if (
|
||||||
|
!isHalloweenEventActive &&
|
||||||
|
this.seasonalEventService.isQuestRelatedToEvent(questId, SeasonalEventType.HALLOWEEN)
|
||||||
|
)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Should non-season event quests be shown to player
|
// Should non-season event quests be shown to player
|
||||||
if (!this.questConfig.showNonSeasonalEventQuests && this.seasonalEventService.isQuestRelatedToEvent(questId, SeasonalEventType.NONE))
|
if (
|
||||||
|
!this.questConfig.showNonSeasonalEventQuests &&
|
||||||
|
this.seasonalEventService.isQuestRelatedToEvent(questId, SeasonalEventType.NONE)
|
||||||
|
)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -274,7 +291,11 @@ export class QuestController
|
|||||||
* @param sessionID Session id
|
* @param sessionID Session id
|
||||||
* @returns client response
|
* @returns client response
|
||||||
*/
|
*/
|
||||||
public acceptQuest(pmcData: IPmcData, acceptedQuest: IAcceptQuestRequestData, sessionID: string): IItemEventRouterResponse
|
public acceptQuest(
|
||||||
|
pmcData: IPmcData,
|
||||||
|
acceptedQuest: IAcceptQuestRequestData,
|
||||||
|
sessionID: string,
|
||||||
|
): IItemEventRouterResponse
|
||||||
{
|
{
|
||||||
const acceptQuestResponse = this.eventOutputHolder.getOutput(sessionID);
|
const acceptQuestResponse = this.eventOutputHolder.getOutput(sessionID);
|
||||||
|
|
||||||
@ -282,7 +303,7 @@ export class QuestController
|
|||||||
const newQuest = this.questHelper.getQuestReadyForProfile(pmcData, startedState, acceptedQuest);
|
const newQuest = this.questHelper.getQuestReadyForProfile(pmcData, startedState, acceptedQuest);
|
||||||
|
|
||||||
// Does quest exist in profile
|
// Does quest exist in profile
|
||||||
if (pmcData.Quests.find(x => x.qid === acceptedQuest.qid))
|
if (pmcData.Quests.find((x) => x.qid === acceptedQuest.qid))
|
||||||
{
|
{
|
||||||
// Update existing
|
// Update existing
|
||||||
this.questHelper.updateQuestState(pmcData, QuestStatus.Started, acceptedQuest.qid);
|
this.questHelper.updateQuestState(pmcData, QuestStatus.Started, acceptedQuest.qid);
|
||||||
@ -297,8 +318,17 @@ export class QuestController
|
|||||||
// Note that for starting quests, the correct locale field is "description", not "startedMessageText".
|
// Note that for starting quests, the correct locale field is "description", not "startedMessageText".
|
||||||
const questFromDb = this.questHelper.getQuestFromDb(acceptedQuest.qid, pmcData);
|
const questFromDb = this.questHelper.getQuestFromDb(acceptedQuest.qid, pmcData);
|
||||||
// Get messageId of text to send to player as text message in game
|
// Get messageId of text to send to player as text message in game
|
||||||
const messageId = this.questHelper.getMessageIdForQuestStart(questFromDb.startedMessageText, questFromDb.description);
|
const messageId = this.questHelper.getMessageIdForQuestStart(
|
||||||
const startedQuestRewards = this.questHelper.applyQuestReward(pmcData, acceptedQuest.qid, QuestStatus.Started, sessionID, acceptQuestResponse);
|
questFromDb.startedMessageText,
|
||||||
|
questFromDb.description,
|
||||||
|
);
|
||||||
|
const startedQuestRewards = this.questHelper.applyQuestReward(
|
||||||
|
pmcData,
|
||||||
|
acceptedQuest.qid,
|
||||||
|
QuestStatus.Started,
|
||||||
|
sessionID,
|
||||||
|
acceptQuestResponse,
|
||||||
|
);
|
||||||
|
|
||||||
this.mailSendService.sendLocalisedNpcMessageToPlayer(
|
this.mailSendService.sendLocalisedNpcMessageToPlayer(
|
||||||
sessionID,
|
sessionID,
|
||||||
@ -306,9 +336,11 @@ export class QuestController
|
|||||||
MessageType.QUEST_START,
|
MessageType.QUEST_START,
|
||||||
messageId,
|
messageId,
|
||||||
startedQuestRewards,
|
startedQuestRewards,
|
||||||
this.timeUtil.getHoursAsSeconds(this.questConfig.redeemTime));
|
this.timeUtil.getHoursAsSeconds(this.questConfig.redeemTime),
|
||||||
|
);
|
||||||
|
|
||||||
acceptQuestResponse.profileChanges[sessionID].quests = this.questHelper.getNewlyAccessibleQuestsWhenStartingQuest(acceptedQuest.qid, sessionID);
|
acceptQuestResponse.profileChanges[sessionID].quests = this.questHelper
|
||||||
|
.getNewlyAccessibleQuestsWhenStartingQuest(acceptedQuest.qid, sessionID);
|
||||||
|
|
||||||
return acceptQuestResponse;
|
return acceptQuestResponse;
|
||||||
}
|
}
|
||||||
@ -322,7 +354,11 @@ export class QuestController
|
|||||||
* @param sessionID Session id
|
* @param sessionID Session id
|
||||||
* @returns IItemEventRouterResponse
|
* @returns IItemEventRouterResponse
|
||||||
*/
|
*/
|
||||||
public acceptRepeatableQuest(pmcData: IPmcData, acceptedQuest: IAcceptQuestRequestData, sessionID: string): IItemEventRouterResponse
|
public acceptRepeatableQuest(
|
||||||
|
pmcData: IPmcData,
|
||||||
|
acceptedQuest: IAcceptQuestRequestData,
|
||||||
|
sessionID: string,
|
||||||
|
): IItemEventRouterResponse
|
||||||
{
|
{
|
||||||
const acceptQuestResponse = this.eventOutputHolder.getOutput(sessionID);
|
const acceptQuestResponse = this.eventOutputHolder.getOutput(sessionID);
|
||||||
|
|
||||||
@ -333,13 +369,21 @@ export class QuestController
|
|||||||
const repeatableQuestProfile = this.getRepeatableQuestFromProfile(pmcData, acceptedQuest);
|
const repeatableQuestProfile = this.getRepeatableQuestFromProfile(pmcData, acceptedQuest);
|
||||||
if (!repeatableQuestProfile)
|
if (!repeatableQuestProfile)
|
||||||
{
|
{
|
||||||
this.logger.error(this.localisationService.getText("repeatable-accepted_repeatable_quest_not_found_in_active_quests", acceptedQuest.qid));
|
this.logger.error(
|
||||||
|
this.localisationService.getText(
|
||||||
|
"repeatable-accepted_repeatable_quest_not_found_in_active_quests",
|
||||||
|
acceptedQuest.qid,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
throw new Error(this.localisationService.getText("repeatable-unable_to_accept_quest_see_log"));
|
throw new Error(this.localisationService.getText("repeatable-unable_to_accept_quest_see_log"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Some scav quests need to be added to scav profile for them to show up in-raid
|
// Some scav quests need to be added to scav profile for them to show up in-raid
|
||||||
if (repeatableQuestProfile.side === "Scav" && ["PickUp", "Exploration", "Elimination"].includes(repeatableQuestProfile.type))
|
if (
|
||||||
|
repeatableQuestProfile.side === "Scav" &&
|
||||||
|
["PickUp", "Exploration", "Elimination"].includes(repeatableQuestProfile.type)
|
||||||
|
)
|
||||||
{
|
{
|
||||||
const fullProfile = this.profileHelper.getFullProfile(sessionID);
|
const fullProfile = this.profileHelper.getFullProfile(sessionID);
|
||||||
if (!fullProfile.characters.scav.Quests)
|
if (!fullProfile.characters.scav.Quests)
|
||||||
@ -351,46 +395,67 @@ export class QuestController
|
|||||||
}
|
}
|
||||||
|
|
||||||
const locale = this.localeService.getLocaleDb();
|
const locale = this.localeService.getLocaleDb();
|
||||||
const questStartedMessageKey = this.questHelper.getMessageIdForQuestStart(repeatableQuestProfile.startedMessageText, repeatableQuestProfile.description);
|
const questStartedMessageKey = this.questHelper.getMessageIdForQuestStart(
|
||||||
|
repeatableQuestProfile.startedMessageText,
|
||||||
|
repeatableQuestProfile.description,
|
||||||
|
);
|
||||||
|
|
||||||
// Can be started text or description text based on above function result
|
// Can be started text or description text based on above function result
|
||||||
let questStartedMessageText = locale[questStartedMessageKey];
|
let questStartedMessageText = locale[questStartedMessageKey];
|
||||||
// TODO: remove this whole if statement, possibly not required?
|
// TODO: Remove this whole if statement, possibly not required?
|
||||||
if (!questStartedMessageText)
|
if (!questStartedMessageText)
|
||||||
{
|
{
|
||||||
this.logger.debug(`Unable to accept quest ${acceptedQuest.qid}, cannot find the quest started message text with id ${questStartedMessageKey}. attempting to find it in en locale instead`);
|
this.logger.debug(
|
||||||
|
`Unable to accept quest ${acceptedQuest.qid}, cannot find the quest started message text with id ${questStartedMessageKey}. attempting to find it in en locale instead`,
|
||||||
|
);
|
||||||
|
|
||||||
// For some reason non-en locales dont have repeatable quest ids, fall back to en and grab it if possible
|
// For some reason non-en locales don't have repeatable quest ids, fall back to en and grab it if possible
|
||||||
const enLocale = this.databaseServer.getTables().locales.global["en"];
|
const enLocale = this.databaseServer.getTables().locales.global["en"];
|
||||||
questStartedMessageText = enLocale[repeatableQuestProfile.startedMessageText];
|
questStartedMessageText = enLocale[repeatableQuestProfile.startedMessageText];
|
||||||
|
|
||||||
if (!questStartedMessageText)
|
if (!questStartedMessageText)
|
||||||
{
|
{
|
||||||
this.logger.error(this.localisationService.getText("repeatable-unable_to_accept_quest_starting_message_not_found", {questId: acceptedQuest.qid, messageId: questStartedMessageKey}));
|
this.logger.error(
|
||||||
|
this.localisationService.getText("repeatable-unable_to_accept_quest_starting_message_not_found", {
|
||||||
|
questId: acceptedQuest.qid,
|
||||||
|
messageId: questStartedMessageKey,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
return this.httpResponseUtil.appendErrorToOutput(acceptQuestResponse, this.localisationService.getText("repeatable-unable_to_accept_quest_see_log"));
|
return this.httpResponseUtil.appendErrorToOutput(
|
||||||
|
acceptQuestResponse,
|
||||||
|
this.localisationService.getText("repeatable-unable_to_accept_quest_see_log"),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const questRewards = this.questHelper.getQuestRewardItems(<IQuest><unknown>repeatableQuestProfile, desiredQuestState);
|
const questRewards = this.questHelper.getQuestRewardItems(
|
||||||
|
<IQuest><unknown>repeatableQuestProfile,
|
||||||
|
desiredQuestState,
|
||||||
|
);
|
||||||
this.mailSendService.sendLocalisedNpcMessageToPlayer(
|
this.mailSendService.sendLocalisedNpcMessageToPlayer(
|
||||||
sessionID,
|
sessionID,
|
||||||
this.traderHelper.getTraderById(repeatableQuestProfile.traderId),
|
this.traderHelper.getTraderById(repeatableQuestProfile.traderId),
|
||||||
MessageType.QUEST_START,
|
MessageType.QUEST_START,
|
||||||
questStartedMessageKey,
|
questStartedMessageKey,
|
||||||
questRewards,
|
questRewards,
|
||||||
this.timeUtil.getHoursAsSeconds(this.questConfig.redeemTime));
|
this.timeUtil.getHoursAsSeconds(this.questConfig.redeemTime),
|
||||||
|
);
|
||||||
|
|
||||||
const repeatableSettings = pmcData.RepeatableQuests.find(x => x.name === repeatableQuestProfile.sptRepatableGroupName);
|
const repeatableSettings = pmcData.RepeatableQuests.find((x) =>
|
||||||
|
x.name === repeatableQuestProfile.sptRepatableGroupName
|
||||||
|
);
|
||||||
const change = {};
|
const change = {};
|
||||||
change[repeatableQuestProfile._id] = repeatableSettings.changeRequirement[repeatableQuestProfile._id];
|
change[repeatableQuestProfile._id] = repeatableSettings.changeRequirement[repeatableQuestProfile._id];
|
||||||
const responseData: IPmcDataRepeatableQuest = {
|
const responseData: IPmcDataRepeatableQuest = {
|
||||||
id: repeatableSettings.id ?? this.questConfig.repeatableQuests.find(x => x.name === repeatableQuestProfile.sptRepatableGroupName).id,
|
id: repeatableSettings.id ??
|
||||||
|
this.questConfig.repeatableQuests.find((x) => x.name === repeatableQuestProfile.sptRepatableGroupName)
|
||||||
|
.id,
|
||||||
name: repeatableSettings.name,
|
name: repeatableSettings.name,
|
||||||
endTime: repeatableSettings.endTime,
|
endTime: repeatableSettings.endTime,
|
||||||
changeRequirement: change,
|
changeRequirement: change,
|
||||||
activeQuests: [repeatableQuestProfile],
|
activeQuests: [repeatableQuestProfile],
|
||||||
inactiveQuests: []
|
inactiveQuests: [],
|
||||||
};
|
};
|
||||||
acceptQuestResponse.profileChanges[sessionID].repeatableQuests = [responseData];
|
acceptQuestResponse.profileChanges[sessionID].repeatableQuests = [responseData];
|
||||||
|
|
||||||
@ -407,7 +472,7 @@ export class QuestController
|
|||||||
{
|
{
|
||||||
for (const repeatableQuest of pmcData.RepeatableQuests)
|
for (const repeatableQuest of pmcData.RepeatableQuests)
|
||||||
{
|
{
|
||||||
const matchingQuest = repeatableQuest.activeQuests.find(x => x._id === acceptedQuest.qid);
|
const matchingQuest = repeatableQuest.activeQuests.find((x) => x._id === acceptedQuest.qid);
|
||||||
if (matchingQuest)
|
if (matchingQuest)
|
||||||
{
|
{
|
||||||
this.logger.debug(`Accepted repeatable quest ${acceptedQuest.qid} from ${repeatableQuest.name}`);
|
this.logger.debug(`Accepted repeatable quest ${acceptedQuest.qid} from ${repeatableQuest.name}`);
|
||||||
@ -430,7 +495,11 @@ export class QuestController
|
|||||||
* @param sessionID Session id
|
* @param sessionID Session id
|
||||||
* @returns ItemEvent client response
|
* @returns ItemEvent client response
|
||||||
*/
|
*/
|
||||||
public completeQuest(pmcData: IPmcData, body: ICompleteQuestRequestData, sessionID: string): IItemEventRouterResponse
|
public completeQuest(
|
||||||
|
pmcData: IPmcData,
|
||||||
|
body: ICompleteQuestRequestData,
|
||||||
|
sessionID: string,
|
||||||
|
): IItemEventRouterResponse
|
||||||
{
|
{
|
||||||
const completeQuestResponse = this.eventOutputHolder.getOutput(sessionID);
|
const completeQuestResponse = this.eventOutputHolder.getOutput(sessionID);
|
||||||
|
|
||||||
@ -442,7 +511,13 @@ export class QuestController
|
|||||||
|
|
||||||
const newQuestState = QuestStatus.Success;
|
const newQuestState = QuestStatus.Success;
|
||||||
this.questHelper.updateQuestState(pmcData, newQuestState, completedQuestId);
|
this.questHelper.updateQuestState(pmcData, newQuestState, completedQuestId);
|
||||||
const questRewards = this.questHelper.applyQuestReward(pmcData, body.qid, newQuestState, sessionID, completeQuestResponse);
|
const questRewards = this.questHelper.applyQuestReward(
|
||||||
|
pmcData,
|
||||||
|
body.qid,
|
||||||
|
newQuestState,
|
||||||
|
sessionID,
|
||||||
|
completeQuestResponse,
|
||||||
|
);
|
||||||
|
|
||||||
// Check for linked failed + unrestartable quests
|
// Check for linked failed + unrestartable quests
|
||||||
const questsToFail = this.getQuestsFailedByCompletingQuest(completedQuestId);
|
const questsToFail = this.getQuestsFailedByCompletingQuest(completedQuestId);
|
||||||
@ -457,7 +532,7 @@ export class QuestController
|
|||||||
// Add diff of quests before completion vs after for client response
|
// Add diff of quests before completion vs after for client response
|
||||||
const questDelta = this.questHelper.getDeltaQuests(beforeQuests, this.getClientQuests(sessionID));
|
const questDelta = this.questHelper.getDeltaQuests(beforeQuests, this.getClientQuests(sessionID));
|
||||||
|
|
||||||
// Check newly available + failed quests for timegates and add them to profile
|
// Check newly available + failed quests for time gates and add them to profile
|
||||||
this.addTimeLockedQuestsToProfile(pmcData, [...questDelta, ...questsToFail], body.qid);
|
this.addTimeLockedQuestsToProfile(pmcData, [...questDelta, ...questsToFail], body.qid);
|
||||||
|
|
||||||
// Inform client of quest changes
|
// Inform client of quest changes
|
||||||
@ -466,10 +541,12 @@ export class QuestController
|
|||||||
// Check if it's a repeatable quest. If so, remove from Quests and repeatable.activeQuests list + move to repeatable.inactiveQuests
|
// Check if it's a repeatable quest. If so, remove from Quests and repeatable.activeQuests list + move to repeatable.inactiveQuests
|
||||||
for (const currentRepeatable of pmcData.RepeatableQuests)
|
for (const currentRepeatable of pmcData.RepeatableQuests)
|
||||||
{
|
{
|
||||||
const repeatableQuest = currentRepeatable.activeQuests.find(x => x._id === completedQuestId);
|
const repeatableQuest = currentRepeatable.activeQuests.find((x) => x._id === completedQuestId);
|
||||||
if (repeatableQuest)
|
if (repeatableQuest)
|
||||||
{
|
{
|
||||||
currentRepeatable.activeQuests = currentRepeatable.activeQuests.filter(x => x._id !== completedQuestId);
|
currentRepeatable.activeQuests = currentRepeatable.activeQuests.filter((x) =>
|
||||||
|
x._id !== completedQuestId
|
||||||
|
);
|
||||||
currentRepeatable.inactiveQuests.push(repeatableQuest);
|
currentRepeatable.inactiveQuests.push(repeatableQuest);
|
||||||
|
|
||||||
// Need to remove redundant scav quest object as its no longer necessary, is tracked in pmc profile
|
// Need to remove redundant scav quest object as its no longer necessary, is tracked in pmc profile
|
||||||
@ -498,31 +575,39 @@ export class QuestController
|
|||||||
protected removeQuestFromScavProfile(sessionId: string, questIdToRemove: string): void
|
protected removeQuestFromScavProfile(sessionId: string, questIdToRemove: string): void
|
||||||
{
|
{
|
||||||
const fullProfile = this.profileHelper.getFullProfile(sessionId);
|
const fullProfile = this.profileHelper.getFullProfile(sessionId);
|
||||||
const repeatableInScavProfile = fullProfile.characters.scav.Quests.find(x => x.qid === questIdToRemove);
|
const repeatableInScavProfile = fullProfile.characters.scav.Quests.find((x) => x.qid === questIdToRemove);
|
||||||
if (!repeatableInScavProfile)
|
if (!repeatableInScavProfile)
|
||||||
{
|
{
|
||||||
this.logger.warning(`Unable to remove quest: ${questIdToRemove} from profile as scav profile cannot be found`);
|
this.logger.warning(
|
||||||
|
`Unable to remove quest: ${questIdToRemove} from profile as scav profile cannot be found`,
|
||||||
|
);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
fullProfile.characters.scav.Quests.splice(fullProfile.characters.scav.Quests.indexOf(repeatableInScavProfile), 1);
|
fullProfile.characters.scav.Quests.splice(
|
||||||
|
fullProfile.characters.scav.Quests.indexOf(repeatableInScavProfile),
|
||||||
|
1,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return quests that have different statuses
|
* Return quests that have different statuses
|
||||||
* @param preQuestStatusus Quests before
|
* @param preQuestStatuses Quests before
|
||||||
* @param postQuestStatuses Quests after
|
* @param postQuestStatuses Quests after
|
||||||
* @returns QuestStatusChange array
|
* @returns QuestStatusChange array
|
||||||
*/
|
*/
|
||||||
protected getQuestsWithDifferentStatuses(preQuestStatusus: IQuestStatus[], postQuestStatuses: IQuestStatus[]): IQuestStatus[]
|
protected getQuestsWithDifferentStatuses(
|
||||||
|
preQuestStatuses: IQuestStatus[],
|
||||||
|
postQuestStatuses: IQuestStatus[],
|
||||||
|
): IQuestStatus[]
|
||||||
{
|
{
|
||||||
const result: IQuestStatus[] = [];
|
const result: IQuestStatus[] = [];
|
||||||
|
|
||||||
for (const quest of postQuestStatuses)
|
for (const quest of postQuestStatuses)
|
||||||
{
|
{
|
||||||
// Add quest if status differs or quest not found
|
// Add quest if status differs or quest not found
|
||||||
const preQuest = preQuestStatusus.find(x => x.qid === quest.qid);
|
const preQuest = preQuestStatuses.find((x) => x.qid === quest.qid);
|
||||||
if (!preQuest || preQuest.status !== quest.status)
|
if (!preQuest || preQuest.status !== quest.status)
|
||||||
{
|
{
|
||||||
result.push(quest);
|
result.push(quest);
|
||||||
@ -544,7 +629,12 @@ export class QuestController
|
|||||||
* @param completedQuestId Completed quest id
|
* @param completedQuestId Completed quest id
|
||||||
* @param questRewards Rewards given to player
|
* @param questRewards Rewards given to player
|
||||||
*/
|
*/
|
||||||
protected sendSuccessDialogMessageOnQuestComplete(sessionID: string, pmcData: IPmcData, completedQuestId: string, questRewards: Reward[]): void
|
protected sendSuccessDialogMessageOnQuestComplete(
|
||||||
|
sessionID: string,
|
||||||
|
pmcData: IPmcData,
|
||||||
|
completedQuestId: string,
|
||||||
|
questRewards: Reward[],
|
||||||
|
): void
|
||||||
{
|
{
|
||||||
const quest = this.questHelper.getQuestFromDb(completedQuestId, pmcData);
|
const quest = this.questHelper.getQuestFromDb(completedQuestId, pmcData);
|
||||||
|
|
||||||
@ -554,7 +644,8 @@ export class QuestController
|
|||||||
MessageType.QUEST_SUCCESS,
|
MessageType.QUEST_SUCCESS,
|
||||||
quest.successMessageText,
|
quest.successMessageText,
|
||||||
questRewards,
|
questRewards,
|
||||||
this.timeUtil.getHoursAsSeconds(this.questConfig.redeemTime));
|
this.timeUtil.getHoursAsSeconds(this.questConfig.redeemTime),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -568,15 +659,18 @@ export class QuestController
|
|||||||
// Iterate over quests, look for quests with right criteria
|
// Iterate over quests, look for quests with right criteria
|
||||||
for (const quest of quests)
|
for (const quest of quests)
|
||||||
{
|
{
|
||||||
// If quest has prereq of completed quest + availableAfter value > 0 (quest has wait time)
|
// If quest has prerequisite of completed quest + availableAfter value > 0 (quest has wait time)
|
||||||
const nextQuestWaitCondition = quest.conditions.AvailableForStart.find(x => x._props.target === completedQuestId && x._props.availableAfter > 0);
|
const nextQuestWaitCondition = quest.conditions.AvailableForStart.find((x) =>
|
||||||
|
x._props.target === completedQuestId && x._props.availableAfter > 0
|
||||||
|
);
|
||||||
if (nextQuestWaitCondition)
|
if (nextQuestWaitCondition)
|
||||||
{
|
{
|
||||||
// Now + wait time
|
// Now + wait time
|
||||||
const availableAfterTimestamp = this.timeUtil.getTimestamp() + nextQuestWaitCondition._props.availableAfter;
|
const availableAfterTimestamp = this.timeUtil.getTimestamp() +
|
||||||
|
nextQuestWaitCondition._props.availableAfter;
|
||||||
|
|
||||||
// Update quest in profile with status of AvailableAfter
|
// Update quest in profile with status of AvailableAfter
|
||||||
const existingQuestInProfile = pmcData.Quests.find(x => x.qid === quest._id);
|
const existingQuestInProfile = pmcData.Quests.find((x) => x.qid === quest._id);
|
||||||
if (existingQuestInProfile)
|
if (existingQuestInProfile)
|
||||||
{
|
{
|
||||||
existingQuestInProfile.availableAfter = availableAfterTimestamp;
|
existingQuestInProfile.availableAfter = availableAfterTimestamp;
|
||||||
@ -592,10 +686,9 @@ export class QuestController
|
|||||||
startTime: 0,
|
startTime: 0,
|
||||||
status: QuestStatus.AvailableAfter,
|
status: QuestStatus.AvailableAfter,
|
||||||
statusTimers: {
|
statusTimers: {
|
||||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
"9": this.timeUtil.getTimestamp(), // eslint-disable-line @typescript-eslint/naming-convention
|
||||||
"9": this.timeUtil.getTimestamp()
|
|
||||||
},
|
},
|
||||||
availableAfter: availableAfterTimestamp
|
availableAfter: availableAfterTimestamp,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -617,7 +710,7 @@ export class QuestController
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return x.conditions.Fail.some(y => y._props.target === completedQuestId);
|
return x.conditions.Fail.some((y) => y._props.target === completedQuestId);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -629,23 +722,28 @@ export class QuestController
|
|||||||
* @param questsToFail quests to fail
|
* @param questsToFail quests to fail
|
||||||
* @param output Client output
|
* @param output Client output
|
||||||
*/
|
*/
|
||||||
protected failQuests(sessionID: string, pmcData: IPmcData, questsToFail: IQuest[], output: IItemEventRouterResponse): void
|
protected failQuests(
|
||||||
|
sessionID: string,
|
||||||
|
pmcData: IPmcData,
|
||||||
|
questsToFail: IQuest[],
|
||||||
|
output: IItemEventRouterResponse,
|
||||||
|
): void
|
||||||
{
|
{
|
||||||
for (const questToFail of questsToFail)
|
for (const questToFail of questsToFail)
|
||||||
{
|
{
|
||||||
// Skip failing a quest that has a fail status of something other than success
|
// Skip failing a quest that has a fail status of something other than success
|
||||||
if (questToFail.conditions.Fail?.some(x => x._props.status?.some(y => y !== QuestStatus.Success)))
|
if (questToFail.conditions.Fail?.some((x) => x._props.status?.some((y) => y !== QuestStatus.Success)))
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const isActiveQuestInPlayerProfile = pmcData.Quests.find(y => y.qid === questToFail._id);
|
const isActiveQuestInPlayerProfile = pmcData.Quests.find((y) => y.qid === questToFail._id);
|
||||||
if (isActiveQuestInPlayerProfile)
|
if (isActiveQuestInPlayerProfile)
|
||||||
{
|
{
|
||||||
const failBody: IFailQuestRequestData = {
|
const failBody: IFailQuestRequestData = {
|
||||||
Action: "QuestComplete",
|
Action: "QuestComplete",
|
||||||
qid: questToFail._id,
|
qid: questToFail._id,
|
||||||
removeExcessItems: true
|
removeExcessItems: true,
|
||||||
};
|
};
|
||||||
this.questHelper.failQuest(pmcData, failBody, sessionID, output);
|
this.questHelper.failQuest(pmcData, failBody, sessionID, output);
|
||||||
}
|
}
|
||||||
@ -657,7 +755,7 @@ export class QuestController
|
|||||||
qid: questToFail._id,
|
qid: questToFail._id,
|
||||||
startTime: this.timeUtil.getTimestamp(),
|
startTime: this.timeUtil.getTimestamp(),
|
||||||
statusTimers: statusTimers,
|
statusTimers: statusTimers,
|
||||||
status: QuestStatus.Fail
|
status: QuestStatus.Fail,
|
||||||
};
|
};
|
||||||
pmcData.Quests.push(questData);
|
pmcData.Quests.push(questData);
|
||||||
}
|
}
|
||||||
@ -671,7 +769,11 @@ export class QuestController
|
|||||||
* @param sessionID Session id
|
* @param sessionID Session id
|
||||||
* @returns IItemEventRouterResponse
|
* @returns IItemEventRouterResponse
|
||||||
*/
|
*/
|
||||||
public handoverQuest(pmcData: IPmcData, handoverQuestRequest: IHandoverQuestRequestData, sessionID: string): IItemEventRouterResponse
|
public handoverQuest(
|
||||||
|
pmcData: IPmcData,
|
||||||
|
handoverQuestRequest: IHandoverQuestRequestData,
|
||||||
|
sessionID: string,
|
||||||
|
): IItemEventRouterResponse
|
||||||
{
|
{
|
||||||
const quest = this.questHelper.getQuestFromDb(handoverQuestRequest.qid, pmcData);
|
const quest = this.questHelper.getQuestFromDb(handoverQuestRequest.qid, pmcData);
|
||||||
const handoverQuestTypes = ["HandoverItem", "WeaponAssembly"];
|
const handoverQuestTypes = ["HandoverItem", "WeaponAssembly"];
|
||||||
@ -684,20 +786,33 @@ export class QuestController
|
|||||||
let handoverRequirements: AvailableForConditions;
|
let handoverRequirements: AvailableForConditions;
|
||||||
for (const condition of quest.conditions.AvailableForFinish)
|
for (const condition of quest.conditions.AvailableForFinish)
|
||||||
{
|
{
|
||||||
if (condition._props.id === handoverQuestRequest.conditionId && handoverQuestTypes.includes(condition._parent))
|
if (
|
||||||
|
condition._props.id === handoverQuestRequest.conditionId &&
|
||||||
|
handoverQuestTypes.includes(condition._parent)
|
||||||
|
)
|
||||||
{
|
{
|
||||||
handedInCount = Number.parseInt(<string>condition._props.value);
|
handedInCount = Number.parseInt(<string>condition._props.value);
|
||||||
isItemHandoverQuest = condition._parent === handoverQuestTypes[0];
|
isItemHandoverQuest = condition._parent === handoverQuestTypes[0];
|
||||||
handoverRequirements = condition;
|
handoverRequirements = condition;
|
||||||
|
|
||||||
const profileCounter = (handoverQuestRequest.conditionId in pmcData.BackendCounters)
|
const profileCounter = (handoverQuestRequest.conditionId in pmcData.BackendCounters) ?
|
||||||
? pmcData.BackendCounters[handoverQuestRequest.conditionId].value
|
pmcData.BackendCounters[handoverQuestRequest.conditionId].value :
|
||||||
: 0;
|
0;
|
||||||
handedInCount -= profileCounter;
|
handedInCount -= profileCounter;
|
||||||
|
|
||||||
if (handedInCount <= 0)
|
if (handedInCount <= 0)
|
||||||
{
|
{
|
||||||
this.logger.error(this.localisationService.getText("repeatable-quest_handover_failed_condition_already_satisfied", {questId: handoverQuestRequest.qid, conditionId: handoverQuestRequest.conditionId, profileCounter: profileCounter, value: handedInCount}));
|
this.logger.error(
|
||||||
|
this.localisationService.getText(
|
||||||
|
"repeatable-quest_handover_failed_condition_already_satisfied",
|
||||||
|
{
|
||||||
|
questId: handoverQuestRequest.qid,
|
||||||
|
conditionId: handoverQuestRequest.conditionId,
|
||||||
|
profileCounter: profileCounter,
|
||||||
|
value: handedInCount,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
@ -714,11 +829,16 @@ export class QuestController
|
|||||||
let totalItemCountToRemove = 0;
|
let totalItemCountToRemove = 0;
|
||||||
for (const itemHandover of handoverQuestRequest.items)
|
for (const itemHandover of handoverQuestRequest.items)
|
||||||
{
|
{
|
||||||
const matchingItemInProfile = pmcData.Inventory.items.find(x => x._id === itemHandover.id);
|
const matchingItemInProfile = pmcData.Inventory.items.find((x) => x._id === itemHandover.id);
|
||||||
if (!handoverRequirements._props.target.includes(matchingItemInProfile._tpl))
|
if (!handoverRequirements._props.target.includes(matchingItemInProfile._tpl))
|
||||||
{
|
{
|
||||||
// Item handed in by player doesnt match what was requested
|
// Item handed in by player doesn't match what was requested
|
||||||
return this.showQuestItemHandoverMatchError(handoverQuestRequest, matchingItemInProfile, handoverRequirements, output);
|
return this.showQuestItemHandoverMatchError(
|
||||||
|
handoverQuestRequest,
|
||||||
|
matchingItemInProfile,
|
||||||
|
handoverRequirements,
|
||||||
|
output,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove the right quantity of given items
|
// Remove the right quantity of given items
|
||||||
@ -727,7 +847,13 @@ export class QuestController
|
|||||||
if (itemHandover.count - itemCountToRemove > 0)
|
if (itemHandover.count - itemCountToRemove > 0)
|
||||||
{
|
{
|
||||||
// Remove single item with no children
|
// Remove single item with no children
|
||||||
this.questHelper.changeItemStack(pmcData, itemHandover.id, itemHandover.count - itemCountToRemove, sessionID, output);
|
this.questHelper.changeItemStack(
|
||||||
|
pmcData,
|
||||||
|
itemHandover.id,
|
||||||
|
itemHandover.count - itemCountToRemove,
|
||||||
|
sessionID,
|
||||||
|
output,
|
||||||
|
);
|
||||||
if (totalItemCountToRemove === handedInCount)
|
if (totalItemCountToRemove === handedInCount)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
@ -740,7 +866,7 @@ export class QuestController
|
|||||||
let index = pmcData.Inventory.items.length;
|
let index = pmcData.Inventory.items.length;
|
||||||
|
|
||||||
// Important: don't tell the client to remove the attachments, it will handle it
|
// Important: don't tell the client to remove the attachments, it will handle it
|
||||||
output.profileChanges[sessionID].items.del.push({ _id: itemHandover.id });
|
output.profileChanges[sessionID].items.del.push({_id: itemHandover.id});
|
||||||
|
|
||||||
// Important: loop backward when removing items from the array we're looping on
|
// Important: loop backward when removing items from the array we're looping on
|
||||||
while (index-- > 0)
|
while (index-- > 0)
|
||||||
@ -753,7 +879,12 @@ export class QuestController
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.updateProfileBackendCounterValue(pmcData, handoverQuestRequest.conditionId, handoverQuestRequest.qid, totalItemCountToRemove);
|
this.updateProfileBackendCounterValue(
|
||||||
|
pmcData,
|
||||||
|
handoverQuestRequest.conditionId,
|
||||||
|
handoverQuestRequest.qid,
|
||||||
|
totalItemCountToRemove,
|
||||||
|
);
|
||||||
|
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
@ -764,9 +895,15 @@ export class QuestController
|
|||||||
* @param output Response to send to user
|
* @param output Response to send to user
|
||||||
* @returns IItemEventRouterResponse
|
* @returns IItemEventRouterResponse
|
||||||
*/
|
*/
|
||||||
protected showRepeatableQuestInvalidConditionError(handoverQuestRequest: IHandoverQuestRequestData, output: IItemEventRouterResponse): IItemEventRouterResponse
|
protected showRepeatableQuestInvalidConditionError(
|
||||||
|
handoverQuestRequest: IHandoverQuestRequestData,
|
||||||
|
output: IItemEventRouterResponse,
|
||||||
|
): IItemEventRouterResponse
|
||||||
{
|
{
|
||||||
const errorMessage = this.localisationService.getText("repeatable-quest_handover_failed_condition_invalid", { questId: handoverQuestRequest.qid, conditionId: handoverQuestRequest.conditionId });
|
const errorMessage = this.localisationService.getText("repeatable-quest_handover_failed_condition_invalid", {
|
||||||
|
questId: handoverQuestRequest.qid,
|
||||||
|
conditionId: handoverQuestRequest.conditionId,
|
||||||
|
});
|
||||||
this.logger.error(errorMessage);
|
this.logger.error(errorMessage);
|
||||||
|
|
||||||
return this.httpResponseUtil.appendErrorToOutput(output, errorMessage);
|
return this.httpResponseUtil.appendErrorToOutput(output, errorMessage);
|
||||||
@ -780,9 +917,18 @@ export class QuestController
|
|||||||
* @param output Response to send to user
|
* @param output Response to send to user
|
||||||
* @returns IItemEventRouterResponse
|
* @returns IItemEventRouterResponse
|
||||||
*/
|
*/
|
||||||
protected showQuestItemHandoverMatchError(handoverQuestRequest: IHandoverQuestRequestData, itemHandedOver: Item, handoverRequirements: AvailableForConditions, output: IItemEventRouterResponse): IItemEventRouterResponse
|
protected showQuestItemHandoverMatchError(
|
||||||
|
handoverQuestRequest: IHandoverQuestRequestData,
|
||||||
|
itemHandedOver: Item,
|
||||||
|
handoverRequirements: AvailableForConditions,
|
||||||
|
output: IItemEventRouterResponse,
|
||||||
|
): IItemEventRouterResponse
|
||||||
{
|
{
|
||||||
const errorMessage = this.localisationService.getText("quest-handover_wrong_item", { questId: handoverQuestRequest.qid, handedInTpl: itemHandedOver._tpl, requiredTpl: handoverRequirements._props.target[0] });
|
const errorMessage = this.localisationService.getText("quest-handover_wrong_item", {
|
||||||
|
questId: handoverQuestRequest.qid,
|
||||||
|
handedInTpl: itemHandedOver._tpl,
|
||||||
|
requiredTpl: handoverRequirements._props.target[0],
|
||||||
|
});
|
||||||
this.logger.error(errorMessage);
|
this.logger.error(errorMessage);
|
||||||
|
|
||||||
return this.httpResponseUtil.appendErrorToOutput(output, errorMessage);
|
return this.httpResponseUtil.appendErrorToOutput(output, errorMessage);
|
||||||
@ -796,7 +942,12 @@ export class QuestController
|
|||||||
* @param questId quest id counter is associated with
|
* @param questId quest id counter is associated with
|
||||||
* @param counterValue value to increment the backend counter with
|
* @param counterValue value to increment the backend counter with
|
||||||
*/
|
*/
|
||||||
protected updateProfileBackendCounterValue(pmcData: IPmcData, conditionId: string, questId: string, counterValue: number): void
|
protected updateProfileBackendCounterValue(
|
||||||
|
pmcData: IPmcData,
|
||||||
|
conditionId: string,
|
||||||
|
questId: string,
|
||||||
|
counterValue: number,
|
||||||
|
): void
|
||||||
{
|
{
|
||||||
if (pmcData.BackendCounters[conditionId] !== undefined)
|
if (pmcData.BackendCounters[conditionId] !== undefined)
|
||||||
{
|
{
|
||||||
@ -807,7 +958,7 @@ export class QuestController
|
|||||||
pmcData.BackendCounters[conditionId] = {
|
pmcData.BackendCounters[conditionId] = {
|
||||||
id: conditionId,
|
id: conditionId,
|
||||||
qid: questId,
|
qid: questId,
|
||||||
value: counterValue
|
value: counterValue,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -76,7 +76,7 @@ export class RagfairController
|
|||||||
@inject("RagfairRequiredItemsService") protected ragfairRequiredItemsService: RagfairRequiredItemsService,
|
@inject("RagfairRequiredItemsService") protected ragfairRequiredItemsService: RagfairRequiredItemsService,
|
||||||
@inject("RagfairOfferGenerator") protected ragfairOfferGenerator: RagfairOfferGenerator,
|
@inject("RagfairOfferGenerator") protected ragfairOfferGenerator: RagfairOfferGenerator,
|
||||||
@inject("LocalisationService") protected localisationService: LocalisationService,
|
@inject("LocalisationService") protected localisationService: LocalisationService,
|
||||||
@inject("ConfigServer") protected configServer: ConfigServer
|
@inject("ConfigServer") protected configServer: ConfigServer,
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
this.ragfairConfig = this.configServer.getConfig(ConfigTypes.RAGFAIR);
|
this.ragfairConfig = this.configServer.getConfig(ConfigTypes.RAGFAIR);
|
||||||
@ -89,7 +89,7 @@ export class RagfairController
|
|||||||
const result: IGetOffersResult = {
|
const result: IGetOffersResult = {
|
||||||
offers: [],
|
offers: [],
|
||||||
offersCount: searchRequest.limit,
|
offersCount: searchRequest.limit,
|
||||||
selectedCategory: searchRequest.handbookId
|
selectedCategory: searchRequest.handbookId,
|
||||||
};
|
};
|
||||||
|
|
||||||
const pmcProfile = this.profileHelper.getPmcProfile(sessionID);
|
const pmcProfile = this.profileHelper.getPmcProfile(sessionID);
|
||||||
@ -106,7 +106,11 @@ export class RagfairController
|
|||||||
this.addIndexValueToOffers(result.offers);
|
this.addIndexValueToOffers(result.offers);
|
||||||
|
|
||||||
// Sort offers
|
// Sort offers
|
||||||
result.offers = this.ragfairSortHelper.sortOffers(result.offers, searchRequest.sortType, searchRequest.sortDirection);
|
result.offers = this.ragfairSortHelper.sortOffers(
|
||||||
|
result.offers,
|
||||||
|
searchRequest.sortType,
|
||||||
|
searchRequest.sortDirection,
|
||||||
|
);
|
||||||
|
|
||||||
// Match offers with quests and lock unfinished quests
|
// Match offers with quests and lock unfinished quests
|
||||||
const profile = this.profileHelper.getFullProfile(sessionID);
|
const profile = this.profileHelper.getFullProfile(sessionID);
|
||||||
@ -114,8 +118,8 @@ export class RagfairController
|
|||||||
{
|
{
|
||||||
if (offer.user.memberType === MemberCategory.TRADER)
|
if (offer.user.memberType === MemberCategory.TRADER)
|
||||||
{
|
{
|
||||||
// for the items, check the barter schemes. The method getDisplayableAssorts sets a flag sptQuestLocked to true if the quest
|
// for the items, check the barter schemes. The method getDisplayableAssorts sets a flag sptQuestLocked
|
||||||
// is not completed yet
|
// to true if the quest is not completed yet
|
||||||
if (this.ragfairOfferHelper.traderOfferItemQuestLocked(offer, traderAssorts))
|
if (this.ragfairOfferHelper.traderOfferItemQuestLocked(offer, traderAssorts))
|
||||||
{
|
{
|
||||||
offer.locked = true;
|
offer.locked = true;
|
||||||
@ -135,7 +139,7 @@ export class RagfairController
|
|||||||
if (searchRequest.buildCount === 0)
|
if (searchRequest.buildCount === 0)
|
||||||
{
|
{
|
||||||
const start = searchRequest.page * searchRequest.limit;
|
const start = searchRequest.page * searchRequest.limit;
|
||||||
const end = Math.min(((searchRequest.page + 1) * searchRequest.limit), result.offers.length);
|
const end = Math.min((searchRequest.page + 1) * searchRequest.limit, result.offers.length);
|
||||||
result.offers = result.offers.slice(start, end);
|
result.offers = result.offers.slice(start, end);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
@ -149,7 +153,12 @@ export class RagfairController
|
|||||||
* @param pmcProfile Player profile
|
* @param pmcProfile Player profile
|
||||||
* @returns array of offers
|
* @returns array of offers
|
||||||
*/
|
*/
|
||||||
protected getOffersForSearchType(searchRequest: ISearchRequestData, itemsToAdd: string[], traderAssorts: Record<string, ITraderAssort>, pmcProfile: IPmcData): IRagfairOffer[]
|
protected getOffersForSearchType(
|
||||||
|
searchRequest: ISearchRequestData,
|
||||||
|
itemsToAdd: string[],
|
||||||
|
traderAssorts: Record<string, ITraderAssort>,
|
||||||
|
pmcProfile: IPmcData,
|
||||||
|
): IRagfairOffer[]
|
||||||
{
|
{
|
||||||
// Searching for items in preset menu
|
// Searching for items in preset menu
|
||||||
if (searchRequest.buildCount)
|
if (searchRequest.buildCount)
|
||||||
@ -165,7 +174,7 @@ export class RagfairController
|
|||||||
* Get categories for the type of search being performed, linked/required/all
|
* Get categories for the type of search being performed, linked/required/all
|
||||||
* @param searchRequest Client search request data
|
* @param searchRequest Client search request data
|
||||||
* @param offers ragfair offers to get categories for
|
* @param offers ragfair offers to get categories for
|
||||||
* @returns record with tpls + counts
|
* @returns record with templates + counts
|
||||||
*/
|
*/
|
||||||
protected getSpecificCategories(searchRequest: ISearchRequestData, offers: IRagfairOffer[]): Record<string, number>
|
protected getSpecificCategories(searchRequest: ISearchRequestData, offers: IRagfairOffer[]): Record<string, number>
|
||||||
{
|
{
|
||||||
@ -191,7 +200,12 @@ export class RagfairController
|
|||||||
* @param pmcProfile Player profile
|
* @param pmcProfile Player profile
|
||||||
* @param result Result object being sent back to client
|
* @param result Result object being sent back to client
|
||||||
*/
|
*/
|
||||||
protected addRequiredOffersToResult(searchRequest: ISearchRequestData, assorts: Record<string, ITraderAssort>, pmcProfile: IPmcData, result: IGetOffersResult): void
|
protected addRequiredOffersToResult(
|
||||||
|
searchRequest: ISearchRequestData,
|
||||||
|
assorts: Record<string, ITraderAssort>,
|
||||||
|
pmcProfile: IPmcData,
|
||||||
|
result: IGetOffersResult,
|
||||||
|
): void
|
||||||
{
|
{
|
||||||
const requiredOffers = this.ragfairRequiredItemsService.getRequiredItemsById(searchRequest.neededSearchId);
|
const requiredOffers = this.ragfairRequiredItemsService.getRequiredItemsById(searchRequest.neededSearchId);
|
||||||
for (const requiredOffer of requiredOffers)
|
for (const requiredOffer of requiredOffers)
|
||||||
@ -214,7 +228,7 @@ export class RagfairController
|
|||||||
for (const offer of offers)
|
for (const offer of offers)
|
||||||
{
|
{
|
||||||
offer.intId = ++counter;
|
offer.intId = ++counter;
|
||||||
offer.items[0].parentId = ""; //without this it causes error: "Item deserialization error: No parent with id hideout found for item x"
|
offer.items[0].parentId = ""; // Without this it causes error: "Item deserialization error: No parent with id hideout found for item x"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -239,12 +253,12 @@ export class RagfairController
|
|||||||
|
|
||||||
const traderAssorts = this.traderHelper.getTraderAssortsByTraderId(offer.user.id).items;
|
const traderAssorts = this.traderHelper.getTraderAssortsByTraderId(offer.user.id).items;
|
||||||
const assortId = offer.items[0]._id;
|
const assortId = offer.items[0]._id;
|
||||||
const assortData = traderAssorts.find(x => x._id === assortId);
|
const assortData = traderAssorts.find((x) => x._id === assortId);
|
||||||
|
|
||||||
// Use value stored in profile, otherwise use value directly from in-memory trader assort data
|
// Use value stored in profile, otherwise use value directly from in-memory trader assort data
|
||||||
offer.buyRestrictionCurrent = profile.traderPurchases[offer.user.id][assortId]
|
offer.buyRestrictionCurrent = profile.traderPurchases[offer.user.id][assortId] ?
|
||||||
? profile.traderPurchases[offer.user.id][assortId].count
|
profile.traderPurchases[offer.user.id][assortId].count :
|
||||||
: assortData.upd.BuyRestrictionCurrent;
|
assortData.upd.BuyRestrictionCurrent;
|
||||||
|
|
||||||
offer.buyRestrictionMax = assortData.upd.BuyRestrictionMax;
|
offer.buyRestrictionMax = assortData.upd.BuyRestrictionMax;
|
||||||
}
|
}
|
||||||
@ -258,10 +272,15 @@ export class RagfairController
|
|||||||
const firstItem = offer.items[0];
|
const firstItem = offer.items[0];
|
||||||
const traderAssorts = this.traderHelper.getTraderAssortsByTraderId(offer.user.id).items;
|
const traderAssorts = this.traderHelper.getTraderAssortsByTraderId(offer.user.id).items;
|
||||||
|
|
||||||
const assortPurchased = traderAssorts.find(x => x._id === offer.items[0]._id);
|
const assortPurchased = traderAssorts.find((x) => x._id === offer.items[0]._id);
|
||||||
if (!assortPurchased)
|
if (!assortPurchased)
|
||||||
{
|
{
|
||||||
this.logger.warning(this.localisationService.getText("ragfair-unable_to_adjust_stack_count_assort_not_found", {offerId: offer.items[0]._id, traderId: offer.user.id}));
|
this.logger.warning(
|
||||||
|
this.localisationService.getText("ragfair-unable_to_adjust_stack_count_assort_not_found", {
|
||||||
|
offerId: offer.items[0]._id,
|
||||||
|
traderId: offer.user.id,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -301,7 +320,7 @@ export class RagfairController
|
|||||||
let offers = this.ragfairOfferService.getOffersOfType(getPriceRequest.templateId);
|
let offers = this.ragfairOfferService.getOffersOfType(getPriceRequest.templateId);
|
||||||
|
|
||||||
// Offers exist for item, get averages of what's listed
|
// Offers exist for item, get averages of what's listed
|
||||||
if (typeof(offers) === "object" && offers.length > 0)
|
if (typeof offers === "object" && offers.length > 0)
|
||||||
{
|
{
|
||||||
offers = this.ragfairSortHelper.sortOffers(offers, RagfairSort.PRICE);
|
offers = this.ragfairSortHelper.sortOffers(offers, RagfairSort.PRICE);
|
||||||
const min = offers[0].requirementsCost; // Get first item from array as its pre-sorted
|
const min = offers[0].requirementsCost; // Get first item from array as its pre-sorted
|
||||||
@ -310,10 +329,11 @@ export class RagfairController
|
|||||||
return {
|
return {
|
||||||
avg: (min + max) / 2,
|
avg: (min + max) / 2,
|
||||||
min: min,
|
min: min,
|
||||||
max: max
|
max: max,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
else // No offers listed, get price from live ragfair price list prices.json
|
// No offers listed, get price from live ragfair price list prices.json
|
||||||
|
else
|
||||||
{
|
{
|
||||||
const templatesDb = this.databaseServer.getTables().templates;
|
const templatesDb = this.databaseServer.getTables().templates;
|
||||||
|
|
||||||
@ -327,7 +347,7 @@ export class RagfairController
|
|||||||
return {
|
return {
|
||||||
avg: tplPrice,
|
avg: tplPrice,
|
||||||
min: tplPrice,
|
min: tplPrice,
|
||||||
max: tplPrice
|
max: tplPrice,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -339,7 +359,11 @@ export class RagfairController
|
|||||||
* @param sessionID Session id
|
* @param sessionID Session id
|
||||||
* @returns IItemEventRouterResponse
|
* @returns IItemEventRouterResponse
|
||||||
*/
|
*/
|
||||||
public addPlayerOffer(pmcData: IPmcData, offerRequest: IAddOfferRequestData, sessionID: string): IItemEventRouterResponse
|
public addPlayerOffer(
|
||||||
|
pmcData: IPmcData,
|
||||||
|
offerRequest: IAddOfferRequestData,
|
||||||
|
sessionID: string,
|
||||||
|
): IItemEventRouterResponse
|
||||||
{
|
{
|
||||||
const output = this.eventOutputHolder.getOutput(sessionID);
|
const output = this.eventOutputHolder.getOutput(sessionID);
|
||||||
|
|
||||||
@ -351,7 +375,11 @@ export class RagfairController
|
|||||||
|
|
||||||
// Get an array of items from player inventory to list on flea
|
// Get an array of items from player inventory to list on flea
|
||||||
const getItemsFromInventoryErrorMessage = "";
|
const getItemsFromInventoryErrorMessage = "";
|
||||||
const itemsInInventoryToList = this.getItemsToListOnFleaFromInventory(pmcData, offerRequest.items, getItemsFromInventoryErrorMessage);
|
const itemsInInventoryToList = this.getItemsToListOnFleaFromInventory(
|
||||||
|
pmcData,
|
||||||
|
offerRequest.items,
|
||||||
|
getItemsFromInventoryErrorMessage,
|
||||||
|
);
|
||||||
if (!itemsInInventoryToList)
|
if (!itemsInInventoryToList)
|
||||||
{
|
{
|
||||||
this.httpResponse.appendErrorToOutput(output, getItemsFromInventoryErrorMessage);
|
this.httpResponse.appendErrorToOutput(output, getItemsFromInventoryErrorMessage);
|
||||||
@ -360,32 +388,52 @@ export class RagfairController
|
|||||||
// Checks are done, create the offer
|
// Checks are done, create the offer
|
||||||
const playerListedPriceInRub = this.calculateRequirementsPriceInRub(offerRequest.requirements);
|
const playerListedPriceInRub = this.calculateRequirementsPriceInRub(offerRequest.requirements);
|
||||||
const fullProfile = this.saveServer.getProfile(sessionID);
|
const fullProfile = this.saveServer.getProfile(sessionID);
|
||||||
const offer = this.createPlayerOffer(fullProfile, offerRequest.requirements, this.ragfairHelper.mergeStackable(itemsInInventoryToList), offerRequest.sellInOnePiece, playerListedPriceInRub);
|
const offer = this.createPlayerOffer(
|
||||||
|
fullProfile,
|
||||||
|
offerRequest.requirements,
|
||||||
|
this.ragfairHelper.mergeStackable(itemsInInventoryToList),
|
||||||
|
offerRequest.sellInOnePiece,
|
||||||
|
playerListedPriceInRub,
|
||||||
|
);
|
||||||
const rootItem = offer.items[0];
|
const rootItem = offer.items[0];
|
||||||
const qualityMultiplier = this.itemHelper.getItemQualityModifier(rootItem);
|
const qualityMultiplier = this.itemHelper.getItemQualityModifier(rootItem);
|
||||||
const averageOfferPrice = this.ragfairPriceService.getFleaPriceForItem(rootItem._tpl) * rootItem.upd.StackObjectsCount * qualityMultiplier;
|
const averageOfferPrice = this.ragfairPriceService.getFleaPriceForItem(rootItem._tpl) *
|
||||||
const itemStackCount = (offerRequest.sellInOnePiece)
|
rootItem.upd.StackObjectsCount * qualityMultiplier;
|
||||||
? 1
|
const itemStackCount = (offerRequest.sellInOnePiece) ?
|
||||||
: rootItem.upd.StackObjectsCount;
|
1 :
|
||||||
|
rootItem.upd.StackObjectsCount;
|
||||||
|
|
||||||
// Get averaged price of a single item being listed
|
// Get averaged price of a single item being listed
|
||||||
const averageSingleItemPrice = (offerRequest.sellInOnePiece)
|
const averageSingleItemPrice = (offerRequest.sellInOnePiece) ?
|
||||||
? averageOfferPrice / rootItem.upd.StackObjectsCount // Packs are a single offer made of many items
|
averageOfferPrice / rootItem.upd.StackObjectsCount // Packs are a single offer made of many items
|
||||||
: averageOfferPrice / itemStackCount;
|
:
|
||||||
|
averageOfferPrice / itemStackCount;
|
||||||
|
|
||||||
// Get averaged price of listing
|
// Get averaged price of listing
|
||||||
const averagePlayerListedPriceInRub = (offerRequest.sellInOnePiece)
|
const averagePlayerListedPriceInRub = (offerRequest.sellInOnePiece) ?
|
||||||
? playerListedPriceInRub / rootItem.upd.StackObjectsCount
|
playerListedPriceInRub / rootItem.upd.StackObjectsCount :
|
||||||
: playerListedPriceInRub;
|
playerListedPriceInRub;
|
||||||
|
|
||||||
// Packs are reduced to the average price of a single item in the pack vs the averaged single price of an item
|
// Packs are reduced to the average price of a single item in the pack vs the averaged single price of an item
|
||||||
const sellChancePercent = this.ragfairSellHelper.calculateSellChance(averageSingleItemPrice, averagePlayerListedPriceInRub, qualityMultiplier);
|
const sellChancePercent = this.ragfairSellHelper.calculateSellChance(
|
||||||
|
averageSingleItemPrice,
|
||||||
|
averagePlayerListedPriceInRub,
|
||||||
|
qualityMultiplier,
|
||||||
|
);
|
||||||
offer.sellResult = this.ragfairSellHelper.rollForSale(sellChancePercent, itemStackCount);
|
offer.sellResult = this.ragfairSellHelper.rollForSale(sellChancePercent, itemStackCount);
|
||||||
|
|
||||||
// Subtract flea market fee from stash
|
// Subtract flea market fee from stash
|
||||||
if (this.ragfairConfig.sell.fees)
|
if (this.ragfairConfig.sell.fees)
|
||||||
{
|
{
|
||||||
const taxFeeChargeFailed = this.chargePlayerTaxFee(sessionID, rootItem, pmcData, playerListedPriceInRub, itemStackCount, offerRequest, output);
|
const taxFeeChargeFailed = this.chargePlayerTaxFee(
|
||||||
|
sessionID,
|
||||||
|
rootItem,
|
||||||
|
pmcData,
|
||||||
|
playerListedPriceInRub,
|
||||||
|
itemStackCount,
|
||||||
|
offerRequest,
|
||||||
|
output,
|
||||||
|
);
|
||||||
if (taxFeeChargeFailed)
|
if (taxFeeChargeFailed)
|
||||||
{
|
{
|
||||||
return output;
|
return output;
|
||||||
@ -415,24 +463,41 @@ export class RagfairController
|
|||||||
* @param output IItemEventRouterResponse
|
* @param output IItemEventRouterResponse
|
||||||
* @returns True if charging tax to player failed
|
* @returns True if charging tax to player failed
|
||||||
*/
|
*/
|
||||||
protected chargePlayerTaxFee(sessionID: string, rootItem: Item, pmcData: IPmcData, requirementsPriceInRub: number, itemStackCount: number, offerRequest: IAddOfferRequestData, output: IItemEventRouterResponse): boolean
|
protected chargePlayerTaxFee(
|
||||||
|
sessionID: string,
|
||||||
|
rootItem: Item,
|
||||||
|
pmcData: IPmcData,
|
||||||
|
requirementsPriceInRub: number,
|
||||||
|
itemStackCount: number,
|
||||||
|
offerRequest: IAddOfferRequestData,
|
||||||
|
output: IItemEventRouterResponse,
|
||||||
|
): boolean
|
||||||
{
|
{
|
||||||
// Get tax from cache hydrated earlier by client, if that's missing fall back to server calculation (inaccurate)
|
// Get tax from cache hydrated earlier by client, if that's missing fall back to server calculation (inaccurate)
|
||||||
const storedClientTaxValue = this.ragfairTaxService.getStoredClientOfferTaxValueById(offerRequest.items[0]);
|
const storedClientTaxValue = this.ragfairTaxService.getStoredClientOfferTaxValueById(offerRequest.items[0]);
|
||||||
const tax = storedClientTaxValue
|
const tax = storedClientTaxValue ?
|
||||||
? storedClientTaxValue.fee
|
storedClientTaxValue.fee :
|
||||||
: this.ragfairTaxService.calculateTax(rootItem, pmcData, requirementsPriceInRub, itemStackCount, offerRequest.sellInOnePiece);
|
this.ragfairTaxService.calculateTax(
|
||||||
|
rootItem,
|
||||||
|
pmcData,
|
||||||
|
requirementsPriceInRub,
|
||||||
|
itemStackCount,
|
||||||
|
offerRequest.sellInOnePiece,
|
||||||
|
);
|
||||||
|
|
||||||
this.logger.debug(`Offer tax to charge: ${tax}, pulled from client: ${(!!storedClientTaxValue)}`);
|
this.logger.debug(`Offer tax to charge: ${tax}, pulled from client: ${(!!storedClientTaxValue)}`);
|
||||||
|
|
||||||
//cleanup of cache now we've used the tax value from it
|
// cleanup of cache now we've used the tax value from it
|
||||||
this.ragfairTaxService.clearStoredOfferTaxById(offerRequest.items[0]);
|
this.ragfairTaxService.clearStoredOfferTaxById(offerRequest.items[0]);
|
||||||
|
|
||||||
const buyTradeRequest = this.createBuyTradeRequestObject("RUB", tax);
|
const buyTradeRequest = this.createBuyTradeRequestObject("RUB", tax);
|
||||||
output = this.paymentService.payMoney(pmcData, buyTradeRequest, sessionID, output);
|
output = this.paymentService.payMoney(pmcData, buyTradeRequest, sessionID, output);
|
||||||
if (output.warnings.length > 0)
|
if (output.warnings.length > 0)
|
||||||
{
|
{
|
||||||
output = this.httpResponse.appendErrorToOutput(output, this.localisationService.getText("ragfair-unable_to_pay_commission_fee", tax));
|
output = this.httpResponse.appendErrorToOutput(
|
||||||
|
output,
|
||||||
|
this.localisationService.getText("ragfair-unable_to_pay_commission_fee", tax),
|
||||||
|
);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -484,7 +549,8 @@ export class RagfairController
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
requirementsPriceInRub += this.ragfairPriceService.getDynamicPriceForItem(requestedItemTpl) * item.count;
|
requirementsPriceInRub += this.ragfairPriceService.getDynamicPriceForItem(requestedItemTpl) *
|
||||||
|
item.count;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -492,22 +558,28 @@ export class RagfairController
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Using item ids from flea offer request, find corrispnding items from player inventory and return as array
|
* Using item ids from flea offer request, find corresponding items from player inventory and return as array
|
||||||
* @param pmcData Player profile
|
* @param pmcData Player profile
|
||||||
* @param itemIdsFromFleaOfferRequest Ids from request
|
* @param itemIdsFromFleaOfferRequest Ids from request
|
||||||
* @param errorMessage if item is not found, add error message to this parameter
|
* @param errorMessage if item is not found, add error message to this parameter
|
||||||
* @returns Array of items from player inventory
|
* @returns Array of items from player inventory
|
||||||
*/
|
*/
|
||||||
protected getItemsToListOnFleaFromInventory(pmcData: IPmcData, itemIdsFromFleaOfferRequest: string[], errorMessage: string): Item[]
|
protected getItemsToListOnFleaFromInventory(
|
||||||
|
pmcData: IPmcData,
|
||||||
|
itemIdsFromFleaOfferRequest: string[],
|
||||||
|
errorMessage: string,
|
||||||
|
): Item[]
|
||||||
{
|
{
|
||||||
const itemsToReturn = [];
|
const itemsToReturn = [];
|
||||||
// Count how many items are being sold and multiply the requested amount accordingly
|
// Count how many items are being sold and multiply the requested amount accordingly
|
||||||
for (const itemId of itemIdsFromFleaOfferRequest)
|
for (const itemId of itemIdsFromFleaOfferRequest)
|
||||||
{
|
{
|
||||||
let item = pmcData.Inventory.items.find(i => i._id === itemId);
|
let item = pmcData.Inventory.items.find((i) => i._id === itemId);
|
||||||
if (!item)
|
if (!item)
|
||||||
{
|
{
|
||||||
errorMessage = this.localisationService.getText("ragfair-unable_to_find_item_in_inventory", {id: itemId});
|
errorMessage = this.localisationService.getText("ragfair-unable_to_find_item_in_inventory", {
|
||||||
|
id: itemId,
|
||||||
|
});
|
||||||
this.logger.error(errorMessage);
|
this.logger.error(errorMessage);
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
@ -528,28 +600,34 @@ export class RagfairController
|
|||||||
return itemsToReturn;
|
return itemsToReturn;
|
||||||
}
|
}
|
||||||
|
|
||||||
public createPlayerOffer(profile: IAkiProfile, requirements: Requirement[], items: Item[], sellInOnePiece: boolean, amountToSend: number): IRagfairOffer
|
public createPlayerOffer(
|
||||||
|
profile: IAkiProfile,
|
||||||
|
requirements: Requirement[],
|
||||||
|
items: Item[],
|
||||||
|
sellInOnePiece: boolean,
|
||||||
|
amountToSend: number,
|
||||||
|
): IRagfairOffer
|
||||||
{
|
{
|
||||||
const loyalLevel = 1;
|
const loyalLevel = 1;
|
||||||
const formattedItems: Item[] = items.map(item =>
|
const formattedItems: Item[] = items.map((item) =>
|
||||||
{
|
{
|
||||||
const isChild = items.find(it => it._id === item.parentId);
|
const isChild = items.find((it) => it._id === item.parentId);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
_id: item._id,
|
_id: item._id,
|
||||||
_tpl: item._tpl,
|
_tpl: item._tpl,
|
||||||
parentId: (isChild) ? item.parentId : "hideout",
|
parentId: isChild ? item.parentId : "hideout",
|
||||||
slotId: (isChild) ? item.slotId : "hideout",
|
slotId: isChild ? item.slotId : "hideout",
|
||||||
upd: item.upd
|
upd: item.upd,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
const formattedRequirements: IBarterScheme[] = requirements.map(item =>
|
const formattedRequirements: IBarterScheme[] = requirements.map((item) =>
|
||||||
{
|
{
|
||||||
return {
|
return {
|
||||||
_tpl: item._tpl,
|
_tpl: item._tpl,
|
||||||
count: item.count,
|
count: item.count,
|
||||||
onlyFunctional: item.onlyFunctional
|
onlyFunctional: item.onlyFunctional,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -559,7 +637,7 @@ export class RagfairController
|
|||||||
formattedItems,
|
formattedItems,
|
||||||
formattedRequirements,
|
formattedRequirements,
|
||||||
loyalLevel,
|
loyalLevel,
|
||||||
sellInOnePiece
|
sellInOnePiece,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -586,21 +664,32 @@ export class RagfairController
|
|||||||
const offers = pmcData.RagfairInfo.offers;
|
const offers = pmcData.RagfairInfo.offers;
|
||||||
if (!offers)
|
if (!offers)
|
||||||
{
|
{
|
||||||
this.logger.warning(this.localisationService.getText("ragfair-unable_to_remove_offer_not_found_in_profile", {profileId: sessionID, offerId: offerId}));
|
this.logger.warning(
|
||||||
|
this.localisationService.getText("ragfair-unable_to_remove_offer_not_found_in_profile", {
|
||||||
|
profileId: sessionID,
|
||||||
|
offerId: offerId,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
pmcData.RagfairInfo.offers = [];
|
pmcData.RagfairInfo.offers = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
const index = offers.findIndex(offer => offer._id === offerId);
|
const index = offers.findIndex((offer) => offer._id === offerId);
|
||||||
if (index === -1)
|
if (index === -1)
|
||||||
{
|
{
|
||||||
this.logger.error(this.localisationService.getText("ragfair-offer_not_found_in_profile", {offerId: offerId}));
|
this.logger.error(
|
||||||
return this.httpResponse.appendErrorToOutput(this.eventOutputHolder.getOutput(sessionID), this.localisationService.getText("ragfair-offer_not_found_in_profile_short"));
|
this.localisationService.getText("ragfair-offer_not_found_in_profile", {offerId: offerId}),
|
||||||
|
);
|
||||||
|
return this.httpResponse.appendErrorToOutput(
|
||||||
|
this.eventOutputHolder.getOutput(sessionID),
|
||||||
|
this.localisationService.getText("ragfair-offer_not_found_in_profile_short"),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const differenceInSeconds = (offers[index].endTime - this.timeUtil.getTimestamp());
|
const differenceInSeconds = offers[index].endTime - this.timeUtil.getTimestamp();
|
||||||
if (differenceInSeconds > this.ragfairConfig.sell.expireSeconds)// expireSeconds Default is 71 seconds
|
if (differenceInSeconds > this.ragfairConfig.sell.expireSeconds)
|
||||||
{
|
{
|
||||||
|
// expireSeconds Default is 71 seconds
|
||||||
const newEndTime = this.ragfairConfig.sell.expireSeconds + this.timeUtil.getTimestamp();
|
const newEndTime = this.ragfairConfig.sell.expireSeconds + this.timeUtil.getTimestamp();
|
||||||
offers[index].endTime = Math.round(newEndTime);
|
offers[index].endTime = Math.round(newEndTime);
|
||||||
}
|
}
|
||||||
@ -613,26 +702,42 @@ export class RagfairController
|
|||||||
let output = this.eventOutputHolder.getOutput(sessionID);
|
let output = this.eventOutputHolder.getOutput(sessionID);
|
||||||
const pmcData = this.saveServer.getProfile(sessionID).characters.pmc;
|
const pmcData = this.saveServer.getProfile(sessionID).characters.pmc;
|
||||||
const offers = pmcData.RagfairInfo.offers;
|
const offers = pmcData.RagfairInfo.offers;
|
||||||
const index = offers.findIndex(offer => offer._id === info.offerId);
|
const index = offers.findIndex((offer) => offer._id === info.offerId);
|
||||||
const secondsToAdd = info.renewalTime * TimeUtil.oneHourAsSeconds;
|
const secondsToAdd = info.renewalTime * TimeUtil.oneHourAsSeconds;
|
||||||
|
|
||||||
if (index === -1)
|
if (index === -1)
|
||||||
{
|
{
|
||||||
this.logger.warning(this.localisationService.getText("ragfair-offer_not_found_in_profile", {offerId: info.offerId}));
|
this.logger.warning(
|
||||||
return this.httpResponse.appendErrorToOutput(this.eventOutputHolder.getOutput(sessionID), this.localisationService.getText("ragfair-offer_not_found_in_profile_short"));
|
this.localisationService.getText("ragfair-offer_not_found_in_profile", {offerId: info.offerId}),
|
||||||
|
);
|
||||||
|
return this.httpResponse.appendErrorToOutput(
|
||||||
|
this.eventOutputHolder.getOutput(sessionID),
|
||||||
|
this.localisationService.getText("ragfair-offer_not_found_in_profile_short"),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// MOD: Pay flea market fee
|
// MOD: Pay flea market fee
|
||||||
if (this.ragfairConfig.sell.fees)
|
if (this.ragfairConfig.sell.fees)
|
||||||
{
|
{
|
||||||
const count = offers[index].sellInOnePiece ? 1 : offers[index].items.reduce((sum, item) => sum += item.upd.StackObjectsCount, 0);
|
const count = offers[index].sellInOnePiece ?
|
||||||
const tax = this.ragfairTaxService.calculateTax(offers[index].items[0], this.profileHelper.getPmcProfile(sessionID), offers[index].requirementsCost, count, offers[index].sellInOnePiece);
|
1 :
|
||||||
|
offers[index].items.reduce((sum, item) => sum += item.upd.StackObjectsCount, 0);
|
||||||
|
const tax = this.ragfairTaxService.calculateTax(
|
||||||
|
offers[index].items[0],
|
||||||
|
this.profileHelper.getPmcProfile(sessionID),
|
||||||
|
offers[index].requirementsCost,
|
||||||
|
count,
|
||||||
|
offers[index].sellInOnePiece,
|
||||||
|
);
|
||||||
|
|
||||||
const request = this.createBuyTradeRequestObject("RUB", tax);
|
const request = this.createBuyTradeRequestObject("RUB", tax);
|
||||||
output = this.paymentService.payMoney(pmcData, request, sessionID, output);
|
output = this.paymentService.payMoney(pmcData, request, sessionID, output);
|
||||||
if (output.warnings.length > 0)
|
if (output.warnings.length > 0)
|
||||||
{
|
{
|
||||||
return this.httpResponse.appendErrorToOutput(output, this.localisationService.getText("ragfair-unable_to_pay_commission_fee"));
|
return this.httpResponse.appendErrorToOutput(
|
||||||
|
output,
|
||||||
|
this.localisationService.getText("ragfair-unable_to_pay_commission_fee"),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -652,19 +757,16 @@ export class RagfairController
|
|||||||
return {
|
return {
|
||||||
tid: "ragfair",
|
tid: "ragfair",
|
||||||
Action: "TradingConfirm",
|
Action: "TradingConfirm",
|
||||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
||||||
scheme_items: [
|
scheme_items: [
|
||||||
{
|
{
|
||||||
id: this.paymentHelper.getCurrency(currency),
|
id: this.paymentHelper.getCurrency(currency),
|
||||||
count: Math.round(value)
|
count: Math.round(value),
|
||||||
}
|
},
|
||||||
],
|
],
|
||||||
type: "",
|
type: "",
|
||||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
||||||
item_id: "",
|
item_id: "",
|
||||||
count: 0,
|
count: 0,
|
||||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
scheme_id: 0,
|
||||||
scheme_id: 0
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -27,9 +27,9 @@ export class RepairController
|
|||||||
@inject("TraderHelper") protected traderHelper: TraderHelper,
|
@inject("TraderHelper") protected traderHelper: TraderHelper,
|
||||||
@inject("PaymentService") protected paymentService: PaymentService,
|
@inject("PaymentService") protected paymentService: PaymentService,
|
||||||
@inject("RepairHelper") protected repairHelper: RepairHelper,
|
@inject("RepairHelper") protected repairHelper: RepairHelper,
|
||||||
@inject("RepairService") protected repairService: RepairService
|
@inject("RepairService") protected repairService: RepairService,
|
||||||
)
|
)
|
||||||
{ }
|
{}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle TraderRepair event
|
* Handle TraderRepair event
|
||||||
@ -39,7 +39,11 @@ export class RepairController
|
|||||||
* @param pmcData player profile
|
* @param pmcData player profile
|
||||||
* @returns item event router action
|
* @returns item event router action
|
||||||
*/
|
*/
|
||||||
public traderRepair(sessionID: string, body: ITraderRepairActionDataRequest, pmcData: IPmcData): IItemEventRouterResponse
|
public traderRepair(
|
||||||
|
sessionID: string,
|
||||||
|
body: ITraderRepairActionDataRequest,
|
||||||
|
pmcData: IPmcData,
|
||||||
|
): IItemEventRouterResponse
|
||||||
{
|
{
|
||||||
const output = this.eventOutputHolder.getOutput(sessionID);
|
const output = this.eventOutputHolder.getOutput(sessionID);
|
||||||
|
|
||||||
@ -48,7 +52,14 @@ export class RepairController
|
|||||||
{
|
{
|
||||||
const repairDetails = this.repairService.repairItemByTrader(sessionID, pmcData, repairItem, body.tid);
|
const repairDetails = this.repairService.repairItemByTrader(sessionID, pmcData, repairItem, body.tid);
|
||||||
|
|
||||||
this.repairService.payForRepair(sessionID, pmcData, repairItem._id, repairDetails.repairCost, body.tid, output);
|
this.repairService.payForRepair(
|
||||||
|
sessionID,
|
||||||
|
pmcData,
|
||||||
|
repairItem._id,
|
||||||
|
repairDetails.repairCost,
|
||||||
|
body.tid,
|
||||||
|
output,
|
||||||
|
);
|
||||||
|
|
||||||
if (output.warnings.length > 0)
|
if (output.warnings.length > 0)
|
||||||
{
|
{
|
||||||
@ -78,7 +89,13 @@ export class RepairController
|
|||||||
const output = this.eventOutputHolder.getOutput(sessionID);
|
const output = this.eventOutputHolder.getOutput(sessionID);
|
||||||
|
|
||||||
// repair item
|
// repair item
|
||||||
const repairDetails = this.repairService.repairItemByKit(sessionID, pmcData, body.repairKitsInfo, body.target, output);
|
const repairDetails = this.repairService.repairItemByKit(
|
||||||
|
sessionID,
|
||||||
|
pmcData,
|
||||||
|
body.repairKitsInfo,
|
||||||
|
body.target,
|
||||||
|
output,
|
||||||
|
);
|
||||||
|
|
||||||
this.repairService.addBuffToItem(repairDetails, pmcData);
|
this.repairService.addBuffToItem(repairDetails, pmcData);
|
||||||
|
|
||||||
|
@ -7,7 +7,11 @@ import { RagfairServerHelper } from "@spt-aki/helpers/RagfairServerHelper";
|
|||||||
import { RepeatableQuestHelper } from "@spt-aki/helpers/RepeatableQuestHelper";
|
import { RepeatableQuestHelper } from "@spt-aki/helpers/RepeatableQuestHelper";
|
||||||
import { IEmptyRequestData } from "@spt-aki/models/eft/common/IEmptyRequestData";
|
import { IEmptyRequestData } from "@spt-aki/models/eft/common/IEmptyRequestData";
|
||||||
import { IPmcData } from "@spt-aki/models/eft/common/IPmcData";
|
import { IPmcData } from "@spt-aki/models/eft/common/IPmcData";
|
||||||
import { IChangeRequirement, IPmcDataRepeatableQuest, IRepeatableQuest } from "@spt-aki/models/eft/common/tables/IRepeatableQuests";
|
import {
|
||||||
|
IChangeRequirement,
|
||||||
|
IPmcDataRepeatableQuest,
|
||||||
|
IRepeatableQuest,
|
||||||
|
} from "@spt-aki/models/eft/common/tables/IRepeatableQuests";
|
||||||
import { IItemEventRouterResponse } from "@spt-aki/models/eft/itemEvent/IItemEventRouterResponse";
|
import { IItemEventRouterResponse } from "@spt-aki/models/eft/itemEvent/IItemEventRouterResponse";
|
||||||
import { IRepeatableQuestChangeRequest } from "@spt-aki/models/eft/quests/IRepeatableQuestChangeRequest";
|
import { IRepeatableQuestChangeRequest } from "@spt-aki/models/eft/quests/IRepeatableQuestChangeRequest";
|
||||||
import { ConfigTypes } from "@spt-aki/models/enums/ConfigTypes";
|
import { ConfigTypes } from "@spt-aki/models/enums/ConfigTypes";
|
||||||
@ -50,13 +54,12 @@ export class RepeatableQuestController
|
|||||||
@inject("RepeatableQuestGenerator") protected repeatableQuestGenerator: RepeatableQuestGenerator,
|
@inject("RepeatableQuestGenerator") protected repeatableQuestGenerator: RepeatableQuestGenerator,
|
||||||
@inject("RepeatableQuestHelper") protected repeatableQuestHelper: RepeatableQuestHelper,
|
@inject("RepeatableQuestHelper") protected repeatableQuestHelper: RepeatableQuestHelper,
|
||||||
@inject("QuestHelper") protected questHelper: QuestHelper,
|
@inject("QuestHelper") protected questHelper: QuestHelper,
|
||||||
@inject("ConfigServer") protected configServer: ConfigServer
|
@inject("ConfigServer") protected configServer: ConfigServer,
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
this.questConfig = this.configServer.getConfig(ConfigTypes.QUEST);
|
this.questConfig = this.configServer.getConfig(ConfigTypes.QUEST);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle client/repeatalbeQuests/activityPeriods
|
* Handle client/repeatalbeQuests/activityPeriods
|
||||||
* Returns an array of objects in the format of repeatable quests to the client.
|
* Returns an array of objects in the format of repeatable quests to the client.
|
||||||
@ -69,8 +72,8 @@ export class RepeatableQuestController
|
|||||||
* }
|
* }
|
||||||
*
|
*
|
||||||
* The method checks if the player level requirement for repeatable quests (e.g. daily lvl5, weekly lvl15) is met and if the previously active quests
|
* The method checks if the player level requirement for repeatable quests (e.g. daily lvl5, weekly lvl15) is met and if the previously active quests
|
||||||
* are still valid. This ischecked by endTime persisted in profile accordning to the resetTime configured for each repeatable kind (daily, weekly)
|
* are still valid. This is checked by endTime persisted in profile according to the resetTime configured for each repeatable kind (daily, weekly)
|
||||||
* in QuestCondig.js
|
* in QuestConfig.js
|
||||||
*
|
*
|
||||||
* If the condition is met, new repeatableQuests are created, old quests (which are persisted in the profile.RepeatableQuests[i].activeQuests) are
|
* If the condition is met, new repeatableQuests are created, old quests (which are persisted in the profile.RepeatableQuests[i].activeQuests) are
|
||||||
* moved to profile.RepeatableQuests[i].inactiveQuests. This memory is required to get rid of old repeatable quest data in the profile, otherwise
|
* moved to profile.RepeatableQuests[i].inactiveQuests. This memory is required to get rid of old repeatable quest data in the profile, otherwise
|
||||||
@ -78,16 +81,16 @@ export class RepeatableQuestController
|
|||||||
* (if the are on "Succeed" but not "Completed" we keep them, to allow the player to complete them and get the rewards)
|
* (if the are on "Succeed" but not "Completed" we keep them, to allow the player to complete them and get the rewards)
|
||||||
* The new quests generated are again persisted in profile.RepeatableQuests
|
* The new quests generated are again persisted in profile.RepeatableQuests
|
||||||
*
|
*
|
||||||
*
|
|
||||||
* @param {string} sessionId Player's session id
|
* @param {string} sessionId Player's session id
|
||||||
* @returns {array} array of "repeatableQuestObjects" as descibed above
|
* @returns {array} array of "repeatableQuestObjects" as described above
|
||||||
*/
|
*/
|
||||||
public getClientRepeatableQuests(_info: IEmptyRequestData, sessionID: string): IPmcDataRepeatableQuest[]
|
public getClientRepeatableQuests(_info: IEmptyRequestData, sessionID: string): IPmcDataRepeatableQuest[]
|
||||||
{
|
{
|
||||||
const returnData: Array<IPmcDataRepeatableQuest> = [];
|
const returnData: Array<IPmcDataRepeatableQuest> = [];
|
||||||
const pmcData = this.profileHelper.getPmcProfile(sessionID);
|
const pmcData = this.profileHelper.getPmcProfile(sessionID);
|
||||||
const time = this.timeUtil.getTimestamp();
|
const time = this.timeUtil.getTimestamp();
|
||||||
const scavQuestUnlocked = pmcData?.Hideout?.Areas?.find(hideoutArea => hideoutArea.type === HideoutAreas.INTEL_CENTER)?.level >= 1;
|
const scavQuestUnlocked =
|
||||||
|
pmcData?.Hideout?.Areas?.find((hideoutArea) => hideoutArea.type === HideoutAreas.INTEL_CENTER)?.level >= 1;
|
||||||
|
|
||||||
// Daily / weekly / Daily_Savage
|
// Daily / weekly / Daily_Savage
|
||||||
for (const repeatableConfig of this.questConfig.repeatableQuests)
|
for (const repeatableConfig of this.questConfig.repeatableQuests)
|
||||||
@ -95,9 +98,11 @@ export class RepeatableQuestController
|
|||||||
// get daily/weekly data from profile, add empty object if missing
|
// get daily/weekly data from profile, add empty object if missing
|
||||||
const currentRepeatableQuestType = this.getRepeatableQuestSubTypeFromProfile(repeatableConfig, pmcData);
|
const currentRepeatableQuestType = this.getRepeatableQuestSubTypeFromProfile(repeatableConfig, pmcData);
|
||||||
|
|
||||||
if (repeatableConfig.side === "Pmc"
|
if (
|
||||||
&& pmcData.Info.Level >= repeatableConfig.minPlayerLevel
|
repeatableConfig.side === "Pmc" &&
|
||||||
|| repeatableConfig.side === "Scav" && scavQuestUnlocked)
|
pmcData.Info.Level >= repeatableConfig.minPlayerLevel ||
|
||||||
|
repeatableConfig.side === "Scav" && scavQuestUnlocked
|
||||||
|
)
|
||||||
{
|
{
|
||||||
if (time > currentRepeatableQuestType.endTime - 1)
|
if (time > currentRepeatableQuestType.endTime - 1)
|
||||||
{
|
{
|
||||||
@ -110,22 +115,24 @@ export class RepeatableQuestController
|
|||||||
// after a raid (the client seems to keep quests internally and we want to get rid of old repeatable quests)
|
// after a raid (the client seems to keep quests internally and we want to get rid of old repeatable quests)
|
||||||
// and remove them from the PMC's Quests and RepeatableQuests[i].activeQuests
|
// and remove them from the PMC's Quests and RepeatableQuests[i].activeQuests
|
||||||
const questsToKeep = [];
|
const questsToKeep = [];
|
||||||
//for (let i = 0; i < currentRepeatable.activeQuests.length; i++)
|
// for (let i = 0; i < currentRepeatable.activeQuests.length; i++)
|
||||||
for (const activeQuest of currentRepeatableQuestType.activeQuests)
|
for (const activeQuest of currentRepeatableQuestType.activeQuests)
|
||||||
{
|
{
|
||||||
// check if the quest is ready to be completed, if so, don't remove it
|
// check if the quest is ready to be completed, if so, don't remove it
|
||||||
const quest = pmcData.Quests.filter(q => q.qid === activeQuest._id);
|
const quest = pmcData.Quests.filter((q) => q.qid === activeQuest._id);
|
||||||
if (quest.length > 0)
|
if (quest.length > 0)
|
||||||
{
|
{
|
||||||
if (quest[0].status === QuestStatus.AvailableForFinish)
|
if (quest[0].status === QuestStatus.AvailableForFinish)
|
||||||
{
|
{
|
||||||
questsToKeep.push(activeQuest);
|
questsToKeep.push(activeQuest);
|
||||||
this.logger.debug(`Keeping repeatable quest ${activeQuest._id} in activeQuests since it is available to AvailableForFinish`);
|
this.logger.debug(
|
||||||
|
`Keeping repeatable quest ${activeQuest._id} in activeQuests since it is available to AvailableForFinish`,
|
||||||
|
);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.profileFixerService.removeDanglingConditionCounters(pmcData);
|
this.profileFixerService.removeDanglingConditionCounters(pmcData);
|
||||||
pmcData.Quests = pmcData.Quests.filter(q => q.qid !== activeQuest._id);
|
pmcData.Quests = pmcData.Quests.filter((q) => q.qid !== activeQuest._id);
|
||||||
currentRepeatableQuestType.inactiveQuests.push(activeQuest);
|
currentRepeatableQuestType.inactiveQuests.push(activeQuest);
|
||||||
}
|
}
|
||||||
currentRepeatableQuestType.activeQuests = questsToKeep;
|
currentRepeatableQuestType.activeQuests = questsToKeep;
|
||||||
@ -144,12 +151,14 @@ export class RepeatableQuestController
|
|||||||
pmcData.Info.Level,
|
pmcData.Info.Level,
|
||||||
pmcData.TradersInfo,
|
pmcData.TradersInfo,
|
||||||
questTypePool,
|
questTypePool,
|
||||||
repeatableConfig
|
repeatableConfig,
|
||||||
);
|
);
|
||||||
lifeline++;
|
lifeline++;
|
||||||
if (lifeline > 10)
|
if (lifeline > 10)
|
||||||
{
|
{
|
||||||
this.logger.debug("We were stuck in repeatable quest generation. This should never happen. Please report");
|
this.logger.debug(
|
||||||
|
"We were stuck in repeatable quest generation. This should never happen. Please report",
|
||||||
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -174,7 +183,7 @@ export class RepeatableQuestController
|
|||||||
{
|
{
|
||||||
currentRepeatableQuestType.changeRequirement[quest._id] = {
|
currentRepeatableQuestType.changeRequirement[quest._id] = {
|
||||||
changeCost: quest.changeCost,
|
changeCost: quest.changeCost,
|
||||||
changeStandingCost: this.randomUtil.getArrayValue([0, 0.01])
|
changeStandingCost: this.randomUtil.getArrayValue([0, 0.01]),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -184,7 +193,7 @@ export class RepeatableQuestController
|
|||||||
endTime: currentRepeatableQuestType.endTime,
|
endTime: currentRepeatableQuestType.endTime,
|
||||||
activeQuests: currentRepeatableQuestType.activeQuests,
|
activeQuests: currentRepeatableQuestType.activeQuests,
|
||||||
inactiveQuests: currentRepeatableQuestType.inactiveQuests,
|
inactiveQuests: currentRepeatableQuestType.inactiveQuests,
|
||||||
changeRequirement: currentRepeatableQuestType.changeRequirement
|
changeRequirement: currentRepeatableQuestType.changeRequirement,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -199,10 +208,15 @@ export class RepeatableQuestController
|
|||||||
*/
|
*/
|
||||||
protected getQuestCount(repeatableConfig: IRepeatableQuestConfig, pmcData: IPmcData): number
|
protected getQuestCount(repeatableConfig: IRepeatableQuestConfig, pmcData: IPmcData): number
|
||||||
{
|
{
|
||||||
if (repeatableConfig.name.toLowerCase() === "daily" && this.profileHelper.hasEliteSkillLevel(SkillTypes.CHARISMA, pmcData))
|
if (
|
||||||
|
repeatableConfig.name.toLowerCase() === "daily" &&
|
||||||
|
this.profileHelper.hasEliteSkillLevel(SkillTypes.CHARISMA, pmcData)
|
||||||
|
)
|
||||||
{
|
{
|
||||||
// Elite charisma skill gives extra daily quest(s)
|
// Elite charisma skill gives extra daily quest(s)
|
||||||
return repeatableConfig.numQuests + this.databaseServer.getTables().globals.config.SkillsSettings.Charisma.BonusSettings.EliteBonusSettings.RepeatableQuestExtraCount;
|
return repeatableConfig.numQuests +
|
||||||
|
this.databaseServer.getTables().globals.config.SkillsSettings.Charisma.BonusSettings.EliteBonusSettings
|
||||||
|
.RepeatableQuestExtraCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
return repeatableConfig.numQuests;
|
return repeatableConfig.numQuests;
|
||||||
@ -214,10 +228,13 @@ export class RepeatableQuestController
|
|||||||
* @param pmcData Profile to search
|
* @param pmcData Profile to search
|
||||||
* @returns IPmcDataRepeatableQuest
|
* @returns IPmcDataRepeatableQuest
|
||||||
*/
|
*/
|
||||||
protected getRepeatableQuestSubTypeFromProfile(repeatableConfig: IRepeatableQuestConfig, pmcData: IPmcData): IPmcDataRepeatableQuest
|
protected getRepeatableQuestSubTypeFromProfile(
|
||||||
|
repeatableConfig: IRepeatableQuestConfig,
|
||||||
|
pmcData: IPmcData,
|
||||||
|
): IPmcDataRepeatableQuest
|
||||||
{
|
{
|
||||||
// Get from profile, add if missing
|
// Get from profile, add if missing
|
||||||
let repeatableQuestDetails = pmcData.RepeatableQuests.find(x => x.name === repeatableConfig.name);
|
let repeatableQuestDetails = pmcData.RepeatableQuests.find((x) => x.name === repeatableConfig.name);
|
||||||
if (!repeatableQuestDetails)
|
if (!repeatableQuestDetails)
|
||||||
{
|
{
|
||||||
repeatableQuestDetails = {
|
repeatableQuestDetails = {
|
||||||
@ -226,7 +243,7 @@ export class RepeatableQuestController
|
|||||||
activeQuests: [],
|
activeQuests: [],
|
||||||
inactiveQuests: [],
|
inactiveQuests: [],
|
||||||
endTime: 0,
|
endTime: 0,
|
||||||
changeRequirement: {}
|
changeRequirement: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
// Add base object that holds repeatable data to profile
|
// Add base object that holds repeatable data to profile
|
||||||
@ -297,16 +314,16 @@ export class RepeatableQuestController
|
|||||||
// Target is boss
|
// Target is boss
|
||||||
if (probabilityObject.data.isBoss)
|
if (probabilityObject.data.isBoss)
|
||||||
{
|
{
|
||||||
questPool.pool.Elimination.targets[probabilityObject.key] = { locations: ["any"] };
|
questPool.pool.Elimination.targets[probabilityObject.key] = {locations: ["any"]};
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
const possibleLocations = Object.keys(repeatableConfig.locations);
|
const possibleLocations = Object.keys(repeatableConfig.locations);
|
||||||
|
|
||||||
// Set possible locations for elimination task, ift arget is savage, exclude labs from locations
|
// Set possible locations for elimination task, if target is savage, exclude labs from locations
|
||||||
questPool.pool.Elimination.targets[probabilityObject.key] = (probabilityObject.key === "Savage")
|
questPool.pool.Elimination.targets[probabilityObject.key] = (probabilityObject.key === "Savage") ?
|
||||||
? { locations: possibleLocations.filter(x => x !== "laboratory")}
|
{locations: possibleLocations.filter((x) => x !== "laboratory")} :
|
||||||
: { locations: possibleLocations };
|
{locations: possibleLocations};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -319,15 +336,15 @@ export class RepeatableQuestController
|
|||||||
types: repeatableConfig.types.slice(),
|
types: repeatableConfig.types.slice(),
|
||||||
pool: {
|
pool: {
|
||||||
Exploration: {
|
Exploration: {
|
||||||
locations: {}
|
locations: {},
|
||||||
},
|
},
|
||||||
Elimination: {
|
Elimination: {
|
||||||
targets: {}
|
targets: {},
|
||||||
},
|
},
|
||||||
Pickup: {
|
Pickup: {
|
||||||
locations: {}
|
locations: {},
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -355,7 +372,11 @@ export class RepeatableQuestController
|
|||||||
/**
|
/**
|
||||||
* Handle RepeatableQuestChange event
|
* Handle RepeatableQuestChange event
|
||||||
*/
|
*/
|
||||||
public changeRepeatableQuest(pmcData: IPmcData, changeRequest: IRepeatableQuestChangeRequest, sessionID: string): IItemEventRouterResponse
|
public changeRepeatableQuest(
|
||||||
|
pmcData: IPmcData,
|
||||||
|
changeRequest: IRepeatableQuestChangeRequest,
|
||||||
|
sessionID: string,
|
||||||
|
): IItemEventRouterResponse
|
||||||
{
|
{
|
||||||
let repeatableToChange: IPmcDataRepeatableQuest;
|
let repeatableToChange: IPmcDataRepeatableQuest;
|
||||||
let changeRequirement: IChangeRequirement;
|
let changeRequirement: IChangeRequirement;
|
||||||
@ -367,7 +388,7 @@ export class RepeatableQuestController
|
|||||||
for (const currentRepeatablePool of pmcData.RepeatableQuests)
|
for (const currentRepeatablePool of pmcData.RepeatableQuests)
|
||||||
{
|
{
|
||||||
// Check for existing quest in (daily/weekly/scav arrays)
|
// Check for existing quest in (daily/weekly/scav arrays)
|
||||||
const questToReplace = currentRepeatablePool.activeQuests.find(x => x._id === changeRequest.qid);
|
const questToReplace = currentRepeatablePool.activeQuests.find((x) => x._id === changeRequest.qid);
|
||||||
if (!questToReplace)
|
if (!questToReplace)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
@ -377,14 +398,19 @@ export class RepeatableQuestController
|
|||||||
replacedQuestTraderId = questToReplace.traderId;
|
replacedQuestTraderId = questToReplace.traderId;
|
||||||
|
|
||||||
// Update active quests to exclude the quest we're replacing
|
// Update active quests to exclude the quest we're replacing
|
||||||
currentRepeatablePool.activeQuests = currentRepeatablePool.activeQuests.filter(x => x._id !== changeRequest.qid);
|
currentRepeatablePool.activeQuests = currentRepeatablePool.activeQuests.filter((x) =>
|
||||||
|
x._id !== changeRequest.qid
|
||||||
|
);
|
||||||
|
|
||||||
// Get cost to replace existing quest
|
// Get cost to replace existing quest
|
||||||
changeRequirement = this.jsonUtil.clone(currentRepeatablePool.changeRequirement[changeRequest.qid]);
|
changeRequirement = this.jsonUtil.clone(currentRepeatablePool.changeRequirement[changeRequest.qid]);
|
||||||
delete currentRepeatablePool.changeRequirement[changeRequest.qid];
|
delete currentRepeatablePool.changeRequirement[changeRequest.qid];
|
||||||
// TODO: somehow we need to reduce the questPool by the currently active quests (for all repeatables)
|
|
||||||
|
|
||||||
const repeatableConfig = this.questConfig.repeatableQuests.find(x => x.name === currentRepeatablePool.name);
|
// TODO: somehow we need to reduce the questPool by the currently active quests (for all repeatable)
|
||||||
|
|
||||||
|
const repeatableConfig = this.questConfig.repeatableQuests.find((x) =>
|
||||||
|
x.name === currentRepeatablePool.name
|
||||||
|
);
|
||||||
const questTypePool = this.generateQuestPool(repeatableConfig, pmcData.Info.Level);
|
const questTypePool = this.generateQuestPool(repeatableConfig, pmcData.Info.Level);
|
||||||
const newRepeatableQuest = this.attemptToGenerateRepeatableQuest(pmcData, questTypePool, repeatableConfig);
|
const newRepeatableQuest = this.attemptToGenerateRepeatableQuest(pmcData, questTypePool, repeatableConfig);
|
||||||
if (newRepeatableQuest)
|
if (newRepeatableQuest)
|
||||||
@ -394,7 +420,7 @@ export class RepeatableQuestController
|
|||||||
currentRepeatablePool.activeQuests.push(newRepeatableQuest);
|
currentRepeatablePool.activeQuests.push(newRepeatableQuest);
|
||||||
currentRepeatablePool.changeRequirement[newRepeatableQuest._id] = {
|
currentRepeatablePool.changeRequirement[newRepeatableQuest._id] = {
|
||||||
changeCost: newRepeatableQuest.changeCost,
|
changeCost: newRepeatableQuest.changeCost,
|
||||||
changeStandingCost: this.randomUtil.getArrayValue([0, 0.01])
|
changeStandingCost: this.randomUtil.getArrayValue([0, 0.01]),
|
||||||
};
|
};
|
||||||
|
|
||||||
const fullProfile = this.profileHelper.getFullProfile(sessionID);
|
const fullProfile = this.profileHelper.getFullProfile(sessionID);
|
||||||
@ -403,7 +429,10 @@ export class RepeatableQuestController
|
|||||||
this.questHelper.findAndRemoveQuestFromArrayIfExists(questToReplace._id, pmcData.Quests);
|
this.questHelper.findAndRemoveQuestFromArrayIfExists(questToReplace._id, pmcData.Quests);
|
||||||
|
|
||||||
// Find quest we're replacing in scav profile quests array and remove it
|
// Find quest we're replacing in scav profile quests array and remove it
|
||||||
this.questHelper.findAndRemoveQuestFromArrayIfExists(questToReplace._id, fullProfile.characters.scav?.Quests ?? []);
|
this.questHelper.findAndRemoveQuestFromArrayIfExists(
|
||||||
|
questToReplace._id,
|
||||||
|
fullProfile.characters.scav?.Quests ?? [],
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Found and replaced the quest in current repeatable
|
// Found and replaced the quest in current repeatable
|
||||||
@ -442,7 +471,11 @@ export class RepeatableQuestController
|
|||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected attemptToGenerateRepeatableQuest(pmcData: IPmcData, questTypePool: IQuestTypePool, repeatableConfig: IRepeatableQuestConfig): IRepeatableQuest
|
protected attemptToGenerateRepeatableQuest(
|
||||||
|
pmcData: IPmcData,
|
||||||
|
questTypePool: IQuestTypePool,
|
||||||
|
repeatableConfig: IRepeatableQuestConfig,
|
||||||
|
): IRepeatableQuest
|
||||||
{
|
{
|
||||||
let newRepeatableQuest: IRepeatableQuest = null;
|
let newRepeatableQuest: IRepeatableQuest = null;
|
||||||
let attemptsToGenerateQuest = 0;
|
let attemptsToGenerateQuest = 0;
|
||||||
@ -452,16 +485,17 @@ export class RepeatableQuestController
|
|||||||
pmcData.Info.Level,
|
pmcData.Info.Level,
|
||||||
pmcData.TradersInfo,
|
pmcData.TradersInfo,
|
||||||
questTypePool,
|
questTypePool,
|
||||||
repeatableConfig
|
repeatableConfig,
|
||||||
);
|
);
|
||||||
attemptsToGenerateQuest++;
|
attemptsToGenerateQuest++;
|
||||||
if (attemptsToGenerateQuest > 10)
|
if (attemptsToGenerateQuest > 10)
|
||||||
{
|
{
|
||||||
this.logger.debug("We were stuck in repeatable quest generation. This should never happen. Please report");
|
this.logger.debug(
|
||||||
|
"We were stuck in repeatable quest generation. This should never happen. Please report",
|
||||||
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return newRepeatableQuest;
|
return newRepeatableQuest;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -29,7 +29,7 @@ import { HttpResponseUtil } from "@spt-aki/utils/HttpResponseUtil";
|
|||||||
import { JsonUtil } from "@spt-aki/utils/JsonUtil";
|
import { JsonUtil } from "@spt-aki/utils/JsonUtil";
|
||||||
|
|
||||||
@injectable()
|
@injectable()
|
||||||
class TradeController
|
export class TradeController
|
||||||
{
|
{
|
||||||
protected ragfairConfig: IRagfairConfig;
|
protected ragfairConfig: IRagfairConfig;
|
||||||
protected traderConfig: ITraderConfig;
|
protected traderConfig: ITraderConfig;
|
||||||
@ -46,7 +46,7 @@ class TradeController
|
|||||||
@inject("HttpResponseUtil") protected httpResponse: HttpResponseUtil,
|
@inject("HttpResponseUtil") protected httpResponse: HttpResponseUtil,
|
||||||
@inject("LocalisationService") protected localisationService: LocalisationService,
|
@inject("LocalisationService") protected localisationService: LocalisationService,
|
||||||
@inject("RagfairPriceService") protected ragfairPriceService: RagfairPriceService,
|
@inject("RagfairPriceService") protected ragfairPriceService: RagfairPriceService,
|
||||||
@inject("ConfigServer") protected configServer: ConfigServer
|
@inject("ConfigServer") protected configServer: ConfigServer,
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
this.ragfairConfig = this.configServer.getConfig(ConfigTypes.RAGFAIR);
|
this.ragfairConfig = this.configServer.getConfig(ConfigTypes.RAGFAIR);
|
||||||
@ -54,13 +54,21 @@ class TradeController
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Handle TradingConfirm event */
|
/** Handle TradingConfirm event */
|
||||||
public confirmTrading(pmcData: IPmcData, request: IProcessBaseTradeRequestData, sessionID: string): IItemEventRouterResponse
|
public confirmTrading(
|
||||||
|
pmcData: IPmcData,
|
||||||
|
request: IProcessBaseTradeRequestData,
|
||||||
|
sessionID: string,
|
||||||
|
): IItemEventRouterResponse
|
||||||
{
|
{
|
||||||
return this.confirmTradingInternal(pmcData, request, sessionID, this.traderConfig.purchasesAreFoundInRaid);
|
return this.confirmTradingInternal(pmcData, request, sessionID, this.traderConfig.purchasesAreFoundInRaid);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Handle RagFairBuyOffer event */
|
/** Handle RagFairBuyOffer event */
|
||||||
public confirmRagfairTrading(pmcData: IPmcData, body: IProcessRagfairTradeRequestData, sessionID: string): IItemEventRouterResponse
|
public confirmRagfairTrading(
|
||||||
|
pmcData: IPmcData,
|
||||||
|
body: IProcessRagfairTradeRequestData,
|
||||||
|
sessionID: string,
|
||||||
|
): IItemEventRouterResponse
|
||||||
{
|
{
|
||||||
let output = this.eventOutputHolder.getOutput(sessionID);
|
let output = this.eventOutputHolder.getOutput(sessionID);
|
||||||
|
|
||||||
@ -69,19 +77,33 @@ class TradeController
|
|||||||
const fleaOffer = this.ragfairServer.getOffer(offer.id);
|
const fleaOffer = this.ragfairServer.getOffer(offer.id);
|
||||||
if (!fleaOffer)
|
if (!fleaOffer)
|
||||||
{
|
{
|
||||||
return this.httpResponse.appendErrorToOutput(output, `Offer with ID ${offer.id} not found`, BackendErrorCodes.OFFERNOTFOUND);
|
return this.httpResponse.appendErrorToOutput(
|
||||||
|
output,
|
||||||
|
`Offer with ID ${offer.id} not found`,
|
||||||
|
BackendErrorCodes.OFFERNOTFOUND,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (offer.count === 0)
|
if (offer.count === 0)
|
||||||
{
|
{
|
||||||
const errorMessage = this.localisationService.getText("ragfair-unable_to_purchase_0_count_item", this.itemHelper.getItem(fleaOffer.items[0]._tpl)[1]._name);
|
const errorMessage = this.localisationService.getText(
|
||||||
|
"ragfair-unable_to_purchase_0_count_item",
|
||||||
|
this.itemHelper.getItem(fleaOffer.items[0]._tpl)[1]._name,
|
||||||
|
);
|
||||||
return this.httpResponse.appendErrorToOutput(output, errorMessage, BackendErrorCodes.OFFEROUTOFSTOCK);
|
return this.httpResponse.appendErrorToOutput(output, errorMessage, BackendErrorCodes.OFFEROUTOFSTOCK);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Skip buying items when player doesn't have necessary loyalty
|
// Skip buying items when player doesn't have necessary loyalty
|
||||||
if (fleaOffer.user.memberType === MemberCategory.TRADER && fleaOffer.loyaltyLevel > pmcData.TradersInfo[fleaOffer.user.id].loyaltyLevel)
|
if (
|
||||||
|
fleaOffer.user.memberType === MemberCategory.TRADER &&
|
||||||
|
fleaOffer.loyaltyLevel > pmcData.TradersInfo[fleaOffer.user.id].loyaltyLevel
|
||||||
|
)
|
||||||
{
|
{
|
||||||
this.logger.debug(`Unable to buy item: ${fleaOffer.items[0]._tpl} from trader: ${fleaOffer.user.id} as loyalty level too low, skipping`);
|
this.logger.debug(
|
||||||
|
`Unable to buy item: ${
|
||||||
|
fleaOffer.items[0]._tpl
|
||||||
|
} from trader: ${fleaOffer.user.id} as loyalty level too low, skipping`,
|
||||||
|
);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -91,17 +113,21 @@ class TradeController
|
|||||||
Action: "TradingConfirm",
|
Action: "TradingConfirm",
|
||||||
type: "buy_from_trader",
|
type: "buy_from_trader",
|
||||||
tid: (fleaOffer.user.memberType !== MemberCategory.TRADER) ? "ragfair" : fleaOffer.user.id,
|
tid: (fleaOffer.user.memberType !== MemberCategory.TRADER) ? "ragfair" : fleaOffer.user.id,
|
||||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
||||||
item_id: fleaOffer.root,
|
item_id: fleaOffer.root,
|
||||||
count: offer.count,
|
count: offer.count,
|
||||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
||||||
scheme_id: 0,
|
scheme_id: 0,
|
||||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
scheme_items: offer.items,
|
||||||
scheme_items: offer.items
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// confirmTrading() must occur prior to removing the offer stack, otherwise item inside offer doesn't exist for confirmTrading() to use
|
// confirmTrading() must occur prior to removing the offer stack, otherwise item inside offer doesn't exist
|
||||||
output = this.confirmTradingInternal(pmcData, buyData, sessionID, this.ragfairConfig.dynamic.purchasesAreFoundInRaid, fleaOffer.items[0].upd);
|
// for confirmTrading() to use
|
||||||
|
output = this.confirmTradingInternal(
|
||||||
|
pmcData,
|
||||||
|
buyData,
|
||||||
|
sessionID,
|
||||||
|
this.ragfairConfig.dynamic.purchasesAreFoundInRaid,
|
||||||
|
fleaOffer.items[0].upd,
|
||||||
|
);
|
||||||
if (fleaOffer.user.memberType !== MemberCategory.TRADER)
|
if (fleaOffer.user.memberType !== MemberCategory.TRADER)
|
||||||
{
|
{
|
||||||
// remove player item offer stack
|
// remove player item offer stack
|
||||||
@ -113,19 +139,26 @@ class TradeController
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Handle SellAllFromSavage event */
|
/** Handle SellAllFromSavage event */
|
||||||
public sellScavItemsToFence(pmcData: IPmcData, body: ISellScavItemsToFenceRequestData, sessionId: string): IItemEventRouterResponse
|
public sellScavItemsToFence(
|
||||||
|
pmcData: IPmcData,
|
||||||
|
body: ISellScavItemsToFenceRequestData,
|
||||||
|
sessionId: string,
|
||||||
|
): IItemEventRouterResponse
|
||||||
{
|
{
|
||||||
const scavProfile = this.profileHelper.getFullProfile(sessionId)?.characters?.scav;
|
const scavProfile = this.profileHelper.getFullProfile(sessionId)?.characters?.scav;
|
||||||
if (!scavProfile)
|
if (!scavProfile)
|
||||||
{
|
{
|
||||||
return this.httpResponse.appendErrorToOutput(this.eventOutputHolder.getOutput(sessionId), `Profile ${body.fromOwner.id} has no scav account`);
|
return this.httpResponse.appendErrorToOutput(
|
||||||
|
this.eventOutputHolder.getOutput(sessionId),
|
||||||
|
`Profile ${body.fromOwner.id} has no scav account`,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.sellInventoryToTrader(sessionId, scavProfile, pmcData, Traders.FENCE);
|
return this.sellInventoryToTrader(sessionId, scavProfile, pmcData, Traders.FENCE);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sell all sellable items to a trader from inventory
|
* Sell all items (that can be sold) to a trader from inventory
|
||||||
* WILL DELETE ITEMS FROM INVENTORY + CHILDREN OF ITEMS SOLD
|
* WILL DELETE ITEMS FROM INVENTORY + CHILDREN OF ITEMS SOLD
|
||||||
* @param sessionId Session id
|
* @param sessionId Session id
|
||||||
* @param profileWithItemsToSell Profile with items to be sold to trader
|
* @param profileWithItemsToSell Profile with items to be sold to trader
|
||||||
@ -133,10 +166,15 @@ class TradeController
|
|||||||
* @param trader Trader to sell items to
|
* @param trader Trader to sell items to
|
||||||
* @returns IItemEventRouterResponse
|
* @returns IItemEventRouterResponse
|
||||||
*/
|
*/
|
||||||
protected sellInventoryToTrader(sessionId: string, profileWithItemsToSell: IPmcData, profileThatGetsMoney: IPmcData, trader: Traders): IItemEventRouterResponse
|
protected sellInventoryToTrader(
|
||||||
|
sessionId: string,
|
||||||
|
profileWithItemsToSell: IPmcData,
|
||||||
|
profileThatGetsMoney: IPmcData,
|
||||||
|
trader: Traders,
|
||||||
|
): IItemEventRouterResponse
|
||||||
{
|
{
|
||||||
const handbookPrices = this.ragfairPriceService.getAllStaticPrices();
|
const handbookPrices = this.ragfairPriceService.getAllStaticPrices();
|
||||||
// TODO, apply trader sell bonuses?
|
// TODO: apply trader sell bonuses?
|
||||||
const traderDetails = this.traderHelper.getTrader(trader, sessionId);
|
const traderDetails = this.traderHelper.getTrader(trader, sessionId);
|
||||||
|
|
||||||
// Prep request object
|
// Prep request object
|
||||||
@ -145,16 +183,23 @@ class TradeController
|
|||||||
type: "sell_to_trader",
|
type: "sell_to_trader",
|
||||||
tid: trader,
|
tid: trader,
|
||||||
price: 0,
|
price: 0,
|
||||||
items: []
|
items: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
// Get all base items that scav has (primaryweapon/backpack/pockets etc)
|
// Get all base items that scav has (primaryweapon/backpack/pockets etc)
|
||||||
// Add items that trader will buy (only sell items that have the container as parent) to request object
|
// Add items that trader will buy (only sell items that have the container as parent) to request object
|
||||||
const containerAndEquipmentItems = profileWithItemsToSell.Inventory.items.filter(x => x.parentId === profileWithItemsToSell.Inventory.equipment);
|
const containerAndEquipmentItems = profileWithItemsToSell.Inventory.items.filter((x) =>
|
||||||
|
x.parentId === profileWithItemsToSell.Inventory.equipment
|
||||||
|
);
|
||||||
for (const itemToSell of containerAndEquipmentItems)
|
for (const itemToSell of containerAndEquipmentItems)
|
||||||
{
|
{
|
||||||
// Increment sell price in request
|
// Increment sell price in request
|
||||||
sellRequest.price += this.getPriceOfItemAndChildren(itemToSell._id, profileWithItemsToSell.Inventory.items, handbookPrices, traderDetails);
|
sellRequest.price += this.getPriceOfItemAndChildren(
|
||||||
|
itemToSell._id,
|
||||||
|
profileWithItemsToSell.Inventory.items,
|
||||||
|
handbookPrices,
|
||||||
|
traderDetails,
|
||||||
|
);
|
||||||
|
|
||||||
// Add item details to request
|
// Add item details to request
|
||||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||||
@ -172,7 +217,12 @@ class TradeController
|
|||||||
* @param traderDetails Trader being sold to to perform buy category check against
|
* @param traderDetails Trader being sold to to perform buy category check against
|
||||||
* @returns Rouble price
|
* @returns Rouble price
|
||||||
*/
|
*/
|
||||||
protected getPriceOfItemAndChildren(parentItemId: string, items: Item[], handbookPrices: Record<string, number>, traderDetails: ITraderBase): number
|
protected getPriceOfItemAndChildren(
|
||||||
|
parentItemId: string,
|
||||||
|
items: Item[],
|
||||||
|
handbookPrices: Record<string, number>,
|
||||||
|
traderDetails: ITraderBase,
|
||||||
|
): number
|
||||||
{
|
{
|
||||||
const itemWithChildren = this.itemHelper.findAndReturnChildrenAsItems(items, parentItemId);
|
const itemWithChildren = this.itemHelper.findAndReturnChildrenAsItems(items, parentItemId);
|
||||||
|
|
||||||
@ -180,9 +230,12 @@ class TradeController
|
|||||||
for (const itemToSell of itemWithChildren)
|
for (const itemToSell of itemWithChildren)
|
||||||
{
|
{
|
||||||
const itemDetails = this.itemHelper.getItem(itemToSell._tpl);
|
const itemDetails = this.itemHelper.getItem(itemToSell._tpl);
|
||||||
if (!(itemDetails[0] && this.itemHelper.isOfBaseclasses(itemDetails[1]._id, traderDetails.items_buy.category)))
|
if (
|
||||||
|
!(itemDetails[0] &&
|
||||||
|
this.itemHelper.isOfBaseclasses(itemDetails[1]._id, traderDetails.items_buy.category))
|
||||||
|
)
|
||||||
{
|
{
|
||||||
// Skip if tpl isnt item OR item doesn't fulfill match traders buy categories
|
// Skip if tpl isn't item OR item doesn't fulfill match traders buy categories
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -193,7 +246,13 @@ class TradeController
|
|||||||
return totalPrice;
|
return totalPrice;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected confirmTradingInternal(pmcData: IPmcData, body: IProcessBaseTradeRequestData, sessionID: string, foundInRaid = false, upd: Upd = null): IItemEventRouterResponse
|
protected confirmTradingInternal(
|
||||||
|
pmcData: IPmcData,
|
||||||
|
body: IProcessBaseTradeRequestData,
|
||||||
|
sessionID: string,
|
||||||
|
foundInRaid = false,
|
||||||
|
upd: Upd = null,
|
||||||
|
): IItemEventRouterResponse
|
||||||
{
|
{
|
||||||
// buying
|
// buying
|
||||||
if (body.type === "buy_from_trader")
|
if (body.type === "buy_from_trader")
|
||||||
@ -212,6 +271,3 @@ class TradeController
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export { TradeController };
|
|
||||||
|
|
||||||
|
@ -23,12 +23,13 @@ export class TraderController
|
|||||||
@inject("ProfileHelper") protected profileHelper: ProfileHelper,
|
@inject("ProfileHelper") protected profileHelper: ProfileHelper,
|
||||||
@inject("TraderHelper") protected traderHelper: TraderHelper,
|
@inject("TraderHelper") protected traderHelper: TraderHelper,
|
||||||
@inject("TraderAssortService") protected traderAssortService: TraderAssortService,
|
@inject("TraderAssortService") protected traderAssortService: TraderAssortService,
|
||||||
@inject("TraderPurchasePersisterService") protected traderPurchasePersisterService: TraderPurchasePersisterService,
|
@inject("TraderPurchasePersisterService") protected traderPurchasePersisterService:
|
||||||
|
TraderPurchasePersisterService,
|
||||||
@inject("FenceService") protected fenceService: FenceService,
|
@inject("FenceService") protected fenceService: FenceService,
|
||||||
@inject("FenceBaseAssortGenerator") protected fenceBaseAssortGenerator: FenceBaseAssortGenerator,
|
@inject("FenceBaseAssortGenerator") protected fenceBaseAssortGenerator: FenceBaseAssortGenerator,
|
||||||
@inject("JsonUtil") protected jsonUtil: JsonUtil
|
@inject("JsonUtil") protected jsonUtil: JsonUtil,
|
||||||
)
|
)
|
||||||
{ }
|
{}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Runs when onLoad event is fired
|
* Runs when onLoad event is fired
|
||||||
@ -132,7 +133,7 @@ export class TraderController
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return traders.sort((a, b) => this.sortByTraderId(a,b));
|
return traders.sort((a, b) => this.sortByTraderId(a, b));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -15,7 +15,7 @@ export class WeatherController
|
|||||||
constructor(
|
constructor(
|
||||||
@inject("WeatherGenerator") protected weatherGenerator: WeatherGenerator,
|
@inject("WeatherGenerator") protected weatherGenerator: WeatherGenerator,
|
||||||
@inject("WinstonLogger") protected logger: ILogger,
|
@inject("WinstonLogger") protected logger: ILogger,
|
||||||
@inject("ConfigServer") protected configServer: ConfigServer
|
@inject("ConfigServer") protected configServer: ConfigServer,
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
this.weatherConfig = this.configServer.getConfig(ConfigTypes.WEATHER);
|
this.weatherConfig = this.configServer.getConfig(ConfigTypes.WEATHER);
|
||||||
@ -28,7 +28,7 @@ export class WeatherController
|
|||||||
acceleration: 0,
|
acceleration: 0,
|
||||||
time: "",
|
time: "",
|
||||||
date: "",
|
date: "",
|
||||||
weather: null
|
weather: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
result = this.weatherGenerator.calculateGameTime(result);
|
result = this.weatherGenerator.calculateGameTime(result);
|
||||||
|
@ -9,16 +9,16 @@ import { EventOutputHolder } from "@spt-aki/routers/EventOutputHolder";
|
|||||||
export class WishlistController
|
export class WishlistController
|
||||||
{
|
{
|
||||||
constructor(
|
constructor(
|
||||||
@inject("EventOutputHolder") protected eventOutputHolder: EventOutputHolder
|
@inject("EventOutputHolder") protected eventOutputHolder: EventOutputHolder,
|
||||||
)
|
)
|
||||||
{ }
|
{}
|
||||||
|
|
||||||
/** Handle AddToWishList */
|
/** Handle AddToWishList */
|
||||||
public addToWishList(pmcData: IPmcData, body: IWishlistActionData, sessionID: string): IItemEventRouterResponse
|
public addToWishList(pmcData: IPmcData, body: IWishlistActionData, sessionID: string): IItemEventRouterResponse
|
||||||
{
|
{
|
||||||
for (const item in pmcData.WishList)
|
for (const item in pmcData.WishList)
|
||||||
{
|
{
|
||||||
// don't add the item
|
// Don't add the item
|
||||||
if (pmcData.WishList[item] === body.templateId)
|
if (pmcData.WishList[item] === body.templateId)
|
||||||
{
|
{
|
||||||
return this.eventOutputHolder.getOutput(sessionID);
|
return this.eventOutputHolder.getOutput(sessionID);
|
||||||
|
Loading…
Reference in New Issue
Block a user