Formatting for helper classes.
This commit is contained in:
parent
d3e5418fc8
commit
8586447d21
@ -12,25 +12,30 @@ import { LocalisationService } from "@spt-aki/services/LocalisationService";
|
||||
@injectable()
|
||||
export class AssortHelper
|
||||
{
|
||||
|
||||
constructor(
|
||||
@inject("WinstonLogger") protected logger: ILogger,
|
||||
@inject("ItemHelper") protected itemHelper: ItemHelper,
|
||||
@inject("DatabaseServer") protected databaseServer: DatabaseServer,
|
||||
@inject("LocalisationService") protected localisationService: LocalisationService,
|
||||
@inject("QuestHelper") protected questHelper: QuestHelper
|
||||
@inject("QuestHelper") protected questHelper: QuestHelper,
|
||||
)
|
||||
{}
|
||||
|
||||
/**
|
||||
* Remove assorts from a trader that have not been unlocked yet (via player completing corrisponding quest)
|
||||
* Remove assorts from a trader that have not been unlocked yet (via player completing corresponding quest)
|
||||
* @param pmcProfile Player profile
|
||||
* @param traderId Traders id the assort belongs to
|
||||
* @param traderAssorts All assort items from same trader
|
||||
* @param mergedQuestAssorts Dict of quest assort to quest id unlocks for all traders (key = started/failed/complete)
|
||||
* @returns Assort items minus locked quest assorts
|
||||
*/
|
||||
public stripLockedQuestAssort(pmcProfile: IPmcData, traderId: string, traderAssorts: ITraderAssort, mergedQuestAssorts: Record<string, Record<string, string>>, flea = false): ITraderAssort
|
||||
public stripLockedQuestAssort(
|
||||
pmcProfile: IPmcData,
|
||||
traderId: string,
|
||||
traderAssorts: ITraderAssort,
|
||||
mergedQuestAssorts: Record<string, Record<string, string>>,
|
||||
flea = false,
|
||||
): ITraderAssort
|
||||
{
|
||||
// Trader assort does not always contain loyal_level_items
|
||||
if (!traderAssorts.loyal_level_items)
|
||||
@ -67,12 +72,18 @@ export class AssortHelper
|
||||
* @param assortId Assort to look for linked quest id
|
||||
* @returns quest id + array of quest status the assort should show for
|
||||
*/
|
||||
protected getQuestIdAndStatusThatShowAssort(mergedQuestAssorts: Record<string, Record<string, string>>, assortId: string): { questId: string; status: QuestStatus[]; }
|
||||
protected getQuestIdAndStatusThatShowAssort(
|
||||
mergedQuestAssorts: Record<string, Record<string, string>>,
|
||||
assortId: string,
|
||||
): {questId: string; status: QuestStatus[];}
|
||||
{
|
||||
if (assortId in mergedQuestAssorts.started)
|
||||
{
|
||||
// Assort unlocked by starting quest, assort is visible to player when : started or ready to hand in + handed in
|
||||
return { questId: mergedQuestAssorts.started[assortId], status: [QuestStatus.Started, QuestStatus.AvailableForFinish, QuestStatus.Success]};
|
||||
return {
|
||||
questId: mergedQuestAssorts.started[assortId],
|
||||
status: [QuestStatus.Started, QuestStatus.AvailableForFinish, QuestStatus.Success],
|
||||
};
|
||||
}
|
||||
else if (assortId in mergedQuestAssorts.success)
|
||||
{
|
||||
|
@ -23,22 +23,27 @@ export class BotDifficultyHelper
|
||||
@inject("RandomUtil") protected randomUtil: RandomUtil,
|
||||
@inject("LocalisationService") protected localisationService: LocalisationService,
|
||||
@inject("BotHelper") protected botHelper: BotHelper,
|
||||
@inject("ConfigServer") protected configServer: ConfigServer
|
||||
@inject("ConfigServer") protected configServer: ConfigServer,
|
||||
)
|
||||
{
|
||||
this.pmcConfig = this.configServer.getConfig(ConfigTypes.PMC);
|
||||
}
|
||||
|
||||
public getPmcDifficultySettings(pmcType: "bear"|"usec", difficulty: string, usecType: string, bearType: string): Difficulty
|
||||
public getPmcDifficultySettings(
|
||||
pmcType: "bear" | "usec",
|
||||
difficulty: string,
|
||||
usecType: string,
|
||||
bearType: string,
|
||||
): Difficulty
|
||||
{
|
||||
const difficultySettings = this.getDifficultySettings(pmcType, difficulty);
|
||||
|
||||
const friendlyType = pmcType === "bear"
|
||||
? bearType
|
||||
: usecType;
|
||||
const enemyType = pmcType === "bear"
|
||||
? usecType
|
||||
: bearType;
|
||||
const friendlyType = pmcType === "bear" ?
|
||||
bearType :
|
||||
usecType;
|
||||
const enemyType = pmcType === "bear" ?
|
||||
usecType :
|
||||
bearType;
|
||||
|
||||
this.botHelper.addBotToEnemyList(difficultySettings, this.pmcConfig.enemyTypes, friendlyType); // Add generic bot types to enemy list
|
||||
this.botHelper.addBotToEnemyList(difficultySettings, [enemyType, friendlyType], ""); // add same/opposite side to enemy list
|
||||
@ -61,14 +66,23 @@ export class BotDifficultyHelper
|
||||
{
|
||||
// get fallback
|
||||
this.logger.warning(this.localisationService.getText("bot-unable_to_get_bot_fallback_to_assault", type));
|
||||
this.databaseServer.getTables().bots.types[type] = this.jsonUtil.clone(this.databaseServer.getTables().bots.types.assault);
|
||||
this.databaseServer.getTables().bots.types[type] = this.jsonUtil.clone(
|
||||
this.databaseServer.getTables().bots.types.assault,
|
||||
);
|
||||
}
|
||||
|
||||
const difficultySettings = this.botHelper.getBotTemplate(type).difficulty[difficulty];
|
||||
if (!difficultySettings)
|
||||
{
|
||||
this.logger.warning(this.localisationService.getText("bot-unable_to_get_bot_difficulty_fallback_to_assault", {botType: type, difficulty: difficulty}));
|
||||
this.databaseServer.getTables().bots.types[type].difficulty[difficulty] = this.jsonUtil.clone(this.databaseServer.getTables().bots.types.assault.difficulty[difficulty]);
|
||||
this.logger.warning(
|
||||
this.localisationService.getText("bot-unable_to_get_bot_difficulty_fallback_to_assault", {
|
||||
botType: type,
|
||||
difficulty: difficulty,
|
||||
}),
|
||||
);
|
||||
this.databaseServer.getTables().bots.types[type].difficulty[difficulty] = this.jsonUtil.clone(
|
||||
this.databaseServer.getTables().bots.types.assault.difficulty[difficulty],
|
||||
);
|
||||
}
|
||||
|
||||
return this.jsonUtil.clone(difficultySettings);
|
||||
@ -82,9 +96,9 @@ export class BotDifficultyHelper
|
||||
*/
|
||||
protected getDifficultySettings(type: string, difficulty: string): Difficulty
|
||||
{
|
||||
let difficultySetting = this.pmcConfig.difficulty.toLowerCase() === "asonline"
|
||||
? difficulty
|
||||
: this.pmcConfig.difficulty.toLowerCase();
|
||||
let difficultySetting = this.pmcConfig.difficulty.toLowerCase() === "asonline" ?
|
||||
difficulty :
|
||||
this.pmcConfig.difficulty.toLowerCase();
|
||||
|
||||
difficultySetting = this.convertBotDifficultyDropdownToBotDifficulty(difficultySetting);
|
||||
|
||||
|
@ -32,7 +32,7 @@ export class BotGeneratorHelper
|
||||
@inject("ItemHelper") protected itemHelper: ItemHelper,
|
||||
@inject("ApplicationContext") protected applicationContext: ApplicationContext,
|
||||
@inject("LocalisationService") protected localisationService: LocalisationService,
|
||||
@inject("ConfigServer") protected configServer: ConfigServer
|
||||
@inject("ConfigServer") protected configServer: ConfigServer,
|
||||
)
|
||||
{
|
||||
this.botConfig = this.configServer.getConfig(ConfigTypes.BOT);
|
||||
@ -46,22 +46,24 @@ export class BotGeneratorHelper
|
||||
* @param botRole Used by weapons to randomize the durability values. Null for non-equipped items
|
||||
* @returns Item Upd object with extra properties
|
||||
*/
|
||||
public generateExtraPropertiesForItem(itemTemplate: ITemplateItem, botRole?: string): { upd?: Upd }
|
||||
public generateExtraPropertiesForItem(itemTemplate: ITemplateItem, botRole?: string): {upd?: Upd;}
|
||||
{
|
||||
// Get raid settings, if no raid, default to day
|
||||
const raidSettings = this.applicationContext.getLatestValue(ContextVariableType.RAID_CONFIGURATION)?.getValue<IGetRaidConfigurationRequestData>();
|
||||
const raidSettings = this.applicationContext.getLatestValue(ContextVariableType.RAID_CONFIGURATION)?.getValue<
|
||||
IGetRaidConfigurationRequestData
|
||||
>();
|
||||
const raidIsNight = raidSettings?.timeVariant === "PAST";
|
||||
|
||||
const itemProperties: Upd = {};
|
||||
|
||||
if (itemTemplate._props.MaxDurability)
|
||||
{
|
||||
if (itemTemplate._props.weapClass) // Is weapon
|
||||
{
|
||||
if (itemTemplate._props.weapClass)
|
||||
{ // Is weapon
|
||||
itemProperties.Repairable = this.generateWeaponRepairableProperties(itemTemplate, botRole);
|
||||
}
|
||||
else if (itemTemplate._props.armorClass) // Is armor
|
||||
{
|
||||
else if (itemTemplate._props.armorClass)
|
||||
{ // Is armor
|
||||
itemProperties.Repairable = this.generateArmorRepairableProperties(itemTemplate, botRole);
|
||||
}
|
||||
}
|
||||
@ -90,35 +92,49 @@ export class BotGeneratorHelper
|
||||
|
||||
if (itemTemplate._props.MaxHpResource)
|
||||
{
|
||||
itemProperties.MedKit = { HpResource: this.getRandomizedResourceValue(itemTemplate._props.MaxHpResource, this.botConfig.lootItemResourceRandomization[botRole]?.meds) };
|
||||
itemProperties.MedKit = {
|
||||
HpResource: this.getRandomizedResourceValue(
|
||||
itemTemplate._props.MaxHpResource,
|
||||
this.botConfig.lootItemResourceRandomization[botRole]?.meds,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
if (itemTemplate._props.MaxResource && itemTemplate._props.foodUseTime)
|
||||
{
|
||||
itemProperties.FoodDrink = { HpPercent: this.getRandomizedResourceValue(itemTemplate._props.MaxResource, this.botConfig.lootItemResourceRandomization[botRole]?.food) };
|
||||
itemProperties.FoodDrink = {
|
||||
HpPercent: this.getRandomizedResourceValue(
|
||||
itemTemplate._props.MaxResource,
|
||||
this.botConfig.lootItemResourceRandomization[botRole]?.food,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
if (itemTemplate._parent === BaseClasses.FLASHLIGHT)
|
||||
{
|
||||
// Get chance from botconfig for bot type
|
||||
const lightLaserActiveChance = raidIsNight
|
||||
? this.getBotEquipmentSettingFromConfig(botRole, "lightIsActiveNightChancePercent", 50)
|
||||
: this.getBotEquipmentSettingFromConfig(botRole, "lightIsActiveDayChancePercent", 25);
|
||||
const lightLaserActiveChance = raidIsNight ?
|
||||
this.getBotEquipmentSettingFromConfig(botRole, "lightIsActiveNightChancePercent", 50) :
|
||||
this.getBotEquipmentSettingFromConfig(botRole, "lightIsActiveDayChancePercent", 25);
|
||||
itemProperties.Light = {IsActive: (this.randomUtil.getChance100(lightLaserActiveChance)), SelectedMode: 0};
|
||||
}
|
||||
else if (itemTemplate._parent === BaseClasses.TACTICAL_COMBO)
|
||||
{
|
||||
// Get chance from botconfig for bot type, use 50% if no value found
|
||||
const lightLaserActiveChance = this.getBotEquipmentSettingFromConfig(botRole, "laserIsActiveChancePercent", 50);
|
||||
const lightLaserActiveChance = this.getBotEquipmentSettingFromConfig(
|
||||
botRole,
|
||||
"laserIsActiveChancePercent",
|
||||
50,
|
||||
);
|
||||
itemProperties.Light = {IsActive: (this.randomUtil.getChance100(lightLaserActiveChance)), SelectedMode: 0};
|
||||
}
|
||||
|
||||
if (itemTemplate._parent === BaseClasses.NIGHTVISION)
|
||||
{
|
||||
// Get chance from botconfig for bot type
|
||||
const nvgActiveChance = raidIsNight
|
||||
? this.getBotEquipmentSettingFromConfig(botRole, "nvgIsActiveChanceNightPercent", 90)
|
||||
: this.getBotEquipmentSettingFromConfig(botRole, "nvgIsActiveChanceDayPercent", 15);
|
||||
const nvgActiveChance = raidIsNight ?
|
||||
this.getBotEquipmentSettingFromConfig(botRole, "nvgIsActiveChanceNightPercent", 90) :
|
||||
this.getBotEquipmentSettingFromConfig(botRole, "nvgIsActiveChanceDayPercent", 15);
|
||||
itemProperties.Togglable = {On: (this.randomUtil.getChance100(nvgActiveChance))};
|
||||
}
|
||||
|
||||
@ -126,13 +142,17 @@ export class BotGeneratorHelper
|
||||
if (itemTemplate._props.HasHinge && itemTemplate._props.FaceShieldComponent)
|
||||
{
|
||||
// Get chance from botconfig for bot type, use 75% if no value found
|
||||
const faceShieldActiveChance = this.getBotEquipmentSettingFromConfig(botRole, "faceShieldIsActiveChancePercent", 75);
|
||||
const faceShieldActiveChance = this.getBotEquipmentSettingFromConfig(
|
||||
botRole,
|
||||
"faceShieldIsActiveChancePercent",
|
||||
75,
|
||||
);
|
||||
itemProperties.Togglable = {On: (this.randomUtil.getChance100(faceShieldActiveChance))};
|
||||
}
|
||||
|
||||
return Object.keys(itemProperties).length
|
||||
? { upd: itemProperties }
|
||||
: {};
|
||||
return Object.keys(itemProperties).length ?
|
||||
{upd: itemProperties} :
|
||||
{};
|
||||
}
|
||||
|
||||
/**
|
||||
@ -153,8 +173,10 @@ export class BotGeneratorHelper
|
||||
return maxResource;
|
||||
}
|
||||
|
||||
return this.randomUtil.getInt(this.randomUtil.getPercentOfValue(randomizationValues.resourcePercent, maxResource, 0), maxResource);
|
||||
|
||||
return this.randomUtil.getInt(
|
||||
this.randomUtil.getPercentOfValue(randomizationValues.resourcePercent, maxResource, 0),
|
||||
maxResource,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -164,7 +186,11 @@ export class BotGeneratorHelper
|
||||
* @param defaultValue default value for the chance of activation if the botrole or bot equipment role is null
|
||||
* @returns Percent chance to be active
|
||||
*/
|
||||
protected getBotEquipmentSettingFromConfig(botRole: string, setting: keyof EquipmentFilters, defaultValue: number): number
|
||||
protected getBotEquipmentSettingFromConfig(
|
||||
botRole: string,
|
||||
setting: keyof EquipmentFilters,
|
||||
defaultValue: number,
|
||||
): number
|
||||
{
|
||||
if (!botRole)
|
||||
{
|
||||
@ -173,13 +199,25 @@ export class BotGeneratorHelper
|
||||
const botEquipmentSettings = this.botConfig.equipment[this.getBotEquipmentRole(botRole)];
|
||||
if (!botEquipmentSettings)
|
||||
{
|
||||
this.logger.warning(this.localisationService.getText("bot-missing_equipment_settings", {botRole: botRole, setting: setting, defaultValue: defaultValue}));
|
||||
this.logger.warning(
|
||||
this.localisationService.getText("bot-missing_equipment_settings", {
|
||||
botRole: botRole,
|
||||
setting: setting,
|
||||
defaultValue: defaultValue,
|
||||
}),
|
||||
);
|
||||
|
||||
return defaultValue;
|
||||
}
|
||||
if (botEquipmentSettings[setting] === undefined || typeof botEquipmentSettings[setting] !== "number")
|
||||
{
|
||||
this.logger.warning(this.localisationService.getText("bot-missing_equipment_settings_property", {botRole: botRole, setting: setting, defaultValue: defaultValue}));
|
||||
this.logger.warning(
|
||||
this.localisationService.getText("bot-missing_equipment_settings_property", {
|
||||
botRole: botRole,
|
||||
setting: setting,
|
||||
defaultValue: defaultValue,
|
||||
}),
|
||||
);
|
||||
|
||||
return defaultValue;
|
||||
}
|
||||
@ -196,11 +234,15 @@ export class BotGeneratorHelper
|
||||
protected generateWeaponRepairableProperties(itemTemplate: ITemplateItem, botRole: string): Repairable
|
||||
{
|
||||
const maxDurability = this.durabilityLimitsHelper.getRandomizedMaxWeaponDurability(itemTemplate, botRole);
|
||||
const currentDurability = this.durabilityLimitsHelper.getRandomizedWeaponDurability(itemTemplate, botRole, maxDurability);
|
||||
const currentDurability = this.durabilityLimitsHelper.getRandomizedWeaponDurability(
|
||||
itemTemplate,
|
||||
botRole,
|
||||
maxDurability,
|
||||
);
|
||||
|
||||
return {
|
||||
Durability: currentDurability,
|
||||
MaxDurability: maxDurability
|
||||
MaxDurability: maxDurability,
|
||||
};
|
||||
}
|
||||
|
||||
@ -222,12 +264,16 @@ export class BotGeneratorHelper
|
||||
else
|
||||
{
|
||||
maxDurability = this.durabilityLimitsHelper.getRandomizedMaxArmorDurability(itemTemplate, botRole);
|
||||
currentDurability = this.durabilityLimitsHelper.getRandomizedArmorDurability(itemTemplate, botRole, maxDurability);
|
||||
currentDurability = this.durabilityLimitsHelper.getRandomizedArmorDurability(
|
||||
itemTemplate,
|
||||
botRole,
|
||||
maxDurability,
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
Durability: currentDurability,
|
||||
MaxDurability: maxDurability
|
||||
MaxDurability: maxDurability,
|
||||
};
|
||||
}
|
||||
|
||||
@ -238,7 +284,11 @@ export class BotGeneratorHelper
|
||||
* @param equipmentSlot Slot the item will be placed into
|
||||
* @returns false if no incompatibilities, also has incompatibility reason
|
||||
*/
|
||||
public isItemIncompatibleWithCurrentItems(items: Item[], tplToCheck: string, equipmentSlot: string): { incompatible: boolean, reason: string }
|
||||
public isItemIncompatibleWithCurrentItems(
|
||||
items: Item[],
|
||||
tplToCheck: string,
|
||||
equipmentSlot: string,
|
||||
): {incompatible: boolean; reason: string;}
|
||||
{
|
||||
// Skip slots that have no incompatibilities
|
||||
if (["Scabbard", "Backpack", "SecureContainer", "Holster", "ArmBand"].includes(equipmentSlot))
|
||||
@ -247,42 +297,65 @@ export class BotGeneratorHelper
|
||||
}
|
||||
|
||||
// TODO: Can probably be optimized to cache itemTemplates as items are added to inventory
|
||||
const equippedItems = items.map(i => this.databaseServer.getTables().templates.items[i._tpl]);
|
||||
const equippedItems = items.map((i) => this.databaseServer.getTables().templates.items[i._tpl]);
|
||||
const item = this.itemHelper.getItem(tplToCheck);
|
||||
const itemToCheck = item[1];
|
||||
|
||||
if (!item[0])
|
||||
{
|
||||
this.logger.warning(this.localisationService.getText("bot-invalid_item_compatibility_check", {itemTpl: tplToCheck, slot: equipmentSlot}));
|
||||
this.logger.warning(
|
||||
this.localisationService.getText("bot-invalid_item_compatibility_check", {
|
||||
itemTpl: tplToCheck,
|
||||
slot: equipmentSlot,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
if (!itemToCheck._props)
|
||||
{
|
||||
this.logger.warning(this.localisationService.getText("bot-compatibility_check_missing_props", {id: itemToCheck._id, name: itemToCheck._name, slot: equipmentSlot}));
|
||||
this.logger.warning(
|
||||
this.localisationService.getText("bot-compatibility_check_missing_props", {
|
||||
id: itemToCheck._id,
|
||||
name: itemToCheck._name,
|
||||
slot: equipmentSlot,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
// Does an equipped item have a property that blocks the desired item - check for prop "BlocksX" .e.g BlocksEarpiece / BlocksFaceCover
|
||||
let blockingItem = equippedItems.find(x => x._props[`Blocks${equipmentSlot}`]);
|
||||
let blockingItem = equippedItems.find((x) => x._props[`Blocks${equipmentSlot}`]);
|
||||
if (blockingItem)
|
||||
{
|
||||
// this.logger.warning(`1 incompatibility found between - ${itemToEquip[1]._name} and ${blockingItem._name} - ${equipmentSlot}`);
|
||||
return { incompatible: true, reason: `${tplToCheck} ${itemToCheck._name} in slot: ${equipmentSlot} blocked by: ${blockingItem._id} ${blockingItem._name}` };
|
||||
return {
|
||||
incompatible: true,
|
||||
reason:
|
||||
`${tplToCheck} ${itemToCheck._name} in slot: ${equipmentSlot} blocked by: ${blockingItem._id} ${blockingItem._name}`,
|
||||
};
|
||||
}
|
||||
|
||||
// Check if any of the current inventory templates have the incoming item defined as incompatible
|
||||
blockingItem = equippedItems.find(x => x._props.ConflictingItems?.includes(tplToCheck));
|
||||
blockingItem = equippedItems.find((x) => x._props.ConflictingItems?.includes(tplToCheck));
|
||||
if (blockingItem)
|
||||
{
|
||||
// this.logger.warning(`2 incompatibility found between - ${itemToEquip[1]._name} and ${blockingItem._props.Name} - ${equipmentSlot}`);
|
||||
return { incompatible: true, reason: `${tplToCheck} ${itemToCheck._name} in slot: ${equipmentSlot} blocked by: ${blockingItem._id} ${blockingItem._name}` };
|
||||
return {
|
||||
incompatible: true,
|
||||
reason:
|
||||
`${tplToCheck} ${itemToCheck._name} in slot: ${equipmentSlot} blocked by: ${blockingItem._id} ${blockingItem._name}`,
|
||||
};
|
||||
}
|
||||
|
||||
// Check if the incoming item has any inventory items defined as incompatible
|
||||
const blockingInventoryItem = items.find(x => itemToCheck._props.ConflictingItems?.includes(x._tpl));
|
||||
const blockingInventoryItem = items.find((x) => itemToCheck._props.ConflictingItems?.includes(x._tpl));
|
||||
if (blockingInventoryItem)
|
||||
{
|
||||
// this.logger.warning(`3 incompatibility found between - ${itemToEquip[1]._name} and ${blockingInventoryItem._tpl} - ${equipmentSlot}`)
|
||||
return { incompatible: true, reason: `${tplToCheck} blocks existing item ${blockingInventoryItem._tpl} in slot ${blockingInventoryItem.slotId}` };
|
||||
return {
|
||||
incompatible: true,
|
||||
reason:
|
||||
`${tplToCheck} blocks existing item ${blockingInventoryItem._tpl} in slot ${blockingInventoryItem.slotId}`,
|
||||
};
|
||||
}
|
||||
|
||||
return {incompatible: false, reason: ""};
|
||||
@ -295,9 +368,11 @@ export class BotGeneratorHelper
|
||||
*/
|
||||
public getBotEquipmentRole(botRole: string): string
|
||||
{
|
||||
return ([this.pmcConfig.usecType.toLowerCase(), this.pmcConfig.bearType.toLowerCase()].includes(botRole.toLowerCase()))
|
||||
? "pmc"
|
||||
: botRole;
|
||||
return ([this.pmcConfig.usecType.toLowerCase(), this.pmcConfig.bearType.toLowerCase()].includes(
|
||||
botRole.toLowerCase(),
|
||||
)) ?
|
||||
"pmc" :
|
||||
botRole;
|
||||
}
|
||||
}
|
||||
|
||||
@ -309,7 +384,7 @@ export class ExhaustableArray<T>
|
||||
constructor(
|
||||
private itemPool: T[],
|
||||
private randomUtil: RandomUtil,
|
||||
private jsonUtil: JsonUtil
|
||||
private jsonUtil: JsonUtil,
|
||||
)
|
||||
{
|
||||
this.pool = this.jsonUtil.clone(itemPool);
|
||||
|
@ -24,15 +24,13 @@ export class BotHelper
|
||||
@inject("DatabaseServer") protected databaseServer: DatabaseServer,
|
||||
@inject("RandomUtil") protected randomUtil: RandomUtil,
|
||||
@inject("LocalisationService") protected localisationService: LocalisationService,
|
||||
@inject("ConfigServer") protected configServer: ConfigServer
|
||||
@inject("ConfigServer") protected configServer: ConfigServer,
|
||||
)
|
||||
{
|
||||
this.botConfig = this.configServer.getConfig(ConfigTypes.BOT);
|
||||
this.pmcConfig = this.configServer.getConfig(ConfigTypes.PMC);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Get a template object for the specified botRole from bots.types db
|
||||
* @param role botRole to get template for
|
||||
@ -71,7 +69,7 @@ export class BotHelper
|
||||
|
||||
public isBotBoss(botRole: string): boolean
|
||||
{
|
||||
return this.botConfig.bosses.some(x => x.toLowerCase() === botRole?.toLowerCase());
|
||||
return this.botConfig.bosses.some((x) => x.toLowerCase() === botRole?.toLowerCase());
|
||||
}
|
||||
|
||||
public isBotFollower(botRole: string): boolean
|
||||
@ -187,13 +185,15 @@ export class BotHelper
|
||||
|
||||
public rollChanceToBePmc(role: string, botConvertMinMax: MinMax): boolean
|
||||
{
|
||||
return role.toLowerCase() in this.pmcConfig.convertIntoPmcChance
|
||||
&& this.randomUtil.getChance100(this.randomUtil.getInt(botConvertMinMax.min, botConvertMinMax.max));
|
||||
return role.toLowerCase() in this.pmcConfig.convertIntoPmcChance &&
|
||||
this.randomUtil.getChance100(this.randomUtil.getInt(botConvertMinMax.min, botConvertMinMax.max));
|
||||
}
|
||||
|
||||
public botRoleIsPmc(botRole: string): boolean
|
||||
{
|
||||
return [this.pmcConfig.usecType.toLowerCase(), this.pmcConfig.bearType.toLowerCase()].includes(botRole.toLowerCase());
|
||||
return [this.pmcConfig.usecType.toLowerCase(), this.pmcConfig.bearType.toLowerCase()].includes(
|
||||
botRole.toLowerCase(),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -210,7 +210,7 @@ export class BotHelper
|
||||
return null;
|
||||
}
|
||||
|
||||
return botEquipConfig.randomisation.find(x => botLevel >= x.levelRange.min && botLevel <= x.levelRange.max);
|
||||
return botEquipConfig.randomisation.find((x) => botLevel >= x.levelRange.min && botLevel <= x.levelRange.max);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -219,9 +219,9 @@ export class BotHelper
|
||||
*/
|
||||
public getRandomizedPmcRole(): string
|
||||
{
|
||||
return (this.randomUtil.getChance100(this.pmcConfig.isUsec))
|
||||
? this.pmcConfig.usecType
|
||||
: this.pmcConfig.bearType;
|
||||
return (this.randomUtil.getChance100(this.pmcConfig.isUsec)) ?
|
||||
this.pmcConfig.usecType :
|
||||
this.pmcConfig.bearType;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -248,8 +248,8 @@ export class BotHelper
|
||||
*/
|
||||
protected getRandomizedPmcSide(): string
|
||||
{
|
||||
return (this.randomUtil.getChance100(this.pmcConfig.isUsec))
|
||||
? "Usec"
|
||||
: "Bear";
|
||||
return (this.randomUtil.getChance100(this.pmcConfig.isUsec)) ?
|
||||
"Usec" :
|
||||
"Bear";
|
||||
}
|
||||
}
|
@ -29,7 +29,7 @@ export class BotWeaponGeneratorHelper
|
||||
@inject("InventoryHelper") protected inventoryHelper: InventoryHelper,
|
||||
@inject("WeightedRandomHelper") protected weightedRandomHelper: WeightedRandomHelper,
|
||||
@inject("LocalisationService") protected localisationService: LocalisationService,
|
||||
@inject("ContainerHelper") protected containerHelper: ContainerHelper
|
||||
@inject("ContainerHelper") protected containerHelper: ContainerHelper,
|
||||
)
|
||||
{}
|
||||
|
||||
@ -98,7 +98,7 @@ export class BotWeaponGeneratorHelper
|
||||
{
|
||||
const magazine: Item[] = [{
|
||||
_id: this.hashUtil.generate(),
|
||||
_tpl: magazineTpl
|
||||
_tpl: magazineTpl,
|
||||
}];
|
||||
|
||||
this.itemHelper.fillMagazineWithCartridge(magazine, magTemplate, ammoTpl, 1);
|
||||
@ -113,12 +113,17 @@ export class BotWeaponGeneratorHelper
|
||||
* @param inventory bot inventory to add cartridges to
|
||||
* @param equipmentSlotsToAddTo what equipment slots should bullets be added into
|
||||
*/
|
||||
public addAmmoIntoEquipmentSlots(ammoTpl: string, cartridgeCount: number, inventory: Inventory, equipmentSlotsToAddTo: EquipmentSlots[] = [EquipmentSlots.TACTICAL_VEST, EquipmentSlots.POCKETS] ): void
|
||||
public addAmmoIntoEquipmentSlots(
|
||||
ammoTpl: string,
|
||||
cartridgeCount: number,
|
||||
inventory: Inventory,
|
||||
equipmentSlotsToAddTo: EquipmentSlots[] = [EquipmentSlots.TACTICAL_VEST, EquipmentSlots.POCKETS],
|
||||
): void
|
||||
{
|
||||
const ammoItems = this.itemHelper.splitStack({
|
||||
_id: this.hashUtil.generate(),
|
||||
_tpl: ammoTpl,
|
||||
upd: { StackObjectsCount: cartridgeCount }
|
||||
upd: {StackObjectsCount: cartridgeCount},
|
||||
});
|
||||
|
||||
for (const ammoItem of ammoItems)
|
||||
@ -128,7 +133,8 @@ export class BotWeaponGeneratorHelper
|
||||
ammoItem._id,
|
||||
ammoItem._tpl,
|
||||
[ammoItem],
|
||||
inventory);
|
||||
inventory,
|
||||
);
|
||||
|
||||
if (result === ItemAddedResult.NO_SPACE)
|
||||
{
|
||||
@ -157,16 +163,26 @@ export class BotWeaponGeneratorHelper
|
||||
* @param inventory Inventory to add item+children into
|
||||
* @returns a `boolean` indicating item was added
|
||||
*/
|
||||
public addItemWithChildrenToEquipmentSlot(equipmentSlots: string[], parentId: string, parentTpl: string, itemWithChildren: Item[], inventory: Inventory): ItemAddedResult
|
||||
public addItemWithChildrenToEquipmentSlot(
|
||||
equipmentSlots: string[],
|
||||
parentId: string,
|
||||
parentTpl: string,
|
||||
itemWithChildren: Item[],
|
||||
inventory: Inventory,
|
||||
): ItemAddedResult
|
||||
{
|
||||
for (const slot of equipmentSlots)
|
||||
{
|
||||
// Get container to put item into
|
||||
const container = inventory.items.find(i => i.slotId === slot);
|
||||
const container = inventory.items.find((i) => i.slotId === slot);
|
||||
if (!container)
|
||||
{
|
||||
// Desired equipment container (e.g. backpack) not found
|
||||
this.logger.debug(`Unable to add item: ${itemWithChildren[0]._tpl} to: ${slot}, slot missing/bot generated without equipment`);
|
||||
this.logger.debug(
|
||||
`Unable to add item: ${
|
||||
itemWithChildren[0]._tpl
|
||||
} to: ${slot}, slot missing/bot generated without equipment`,
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -202,10 +218,12 @@ export class BotWeaponGeneratorHelper
|
||||
}
|
||||
|
||||
// Get all base level items in backpack
|
||||
const containerItems = inventory.items.filter(i => i.parentId === container._id && i.slotId === slotGrid._name);
|
||||
const containerItems = inventory.items.filter((i) =>
|
||||
i.parentId === container._id && i.slotId === slotGrid._name
|
||||
);
|
||||
|
||||
// Get a copy of base level items we can iterate over
|
||||
const containerItemsToCheck = containerItems.filter(x => x.slotId === slotGrid._name);
|
||||
const containerItemsToCheck = containerItems.filter((x) => x.slotId === slotGrid._name);
|
||||
for (const item of containerItemsToCheck)
|
||||
{
|
||||
// Look for children on items, insert into array if found
|
||||
@ -218,14 +236,19 @@ export class BotWeaponGeneratorHelper
|
||||
}
|
||||
|
||||
// Get rid of items free/used spots in current grid
|
||||
const slotGridMap = this.inventoryHelper.getContainerMap(slotGrid._props.cellsH, slotGrid._props.cellsV, containerItems, container._id);
|
||||
const slotGridMap = this.inventoryHelper.getContainerMap(
|
||||
slotGrid._props.cellsH,
|
||||
slotGrid._props.cellsV,
|
||||
containerItems,
|
||||
container._id,
|
||||
);
|
||||
// Try to fit item into grid
|
||||
const findSlotResult = this.containerHelper.findSlotForItem(slotGridMap, itemSize[0], itemSize[1]);
|
||||
|
||||
// Open slot found, add item to inventory
|
||||
if (findSlotResult.success)
|
||||
{
|
||||
const parentItem = itemWithChildren.find(i => i._id === parentId);
|
||||
const parentItem = itemWithChildren.find((i) => i._id === parentId);
|
||||
|
||||
// Set items parent to container id
|
||||
parentItem.parentId = container._id;
|
||||
@ -233,7 +256,7 @@ export class BotWeaponGeneratorHelper
|
||||
parentItem.location = {
|
||||
x: findSlotResult.x,
|
||||
y: findSlotResult.y,
|
||||
r: findSlotResult.rotation ? 1 : 0
|
||||
r: findSlotResult.rotation ? 1 : 0,
|
||||
};
|
||||
|
||||
inventory.items.push(...itemWithChildren);
|
||||
|
@ -43,7 +43,7 @@ export class ContainerHelper
|
||||
/**
|
||||
* Try to rotate if there is enough room for the item
|
||||
* Only occupies one grid of items, no rotation required
|
||||
* */
|
||||
*/
|
||||
if (!foundSlot && itemWidth * itemHeight > 1)
|
||||
{
|
||||
foundSlot = this.locateSlot(container2D, containerX, containerY, x, y, itemHeight, itemWidth);
|
||||
@ -77,7 +77,15 @@ export class ContainerHelper
|
||||
* @param itemH Items height
|
||||
* @returns True - slot found
|
||||
*/
|
||||
protected locateSlot(container2D: number[][], containerX: number, containerY: number, x: number, y: number, itemW: number, itemH: number): boolean
|
||||
protected locateSlot(
|
||||
container2D: number[][],
|
||||
containerX: number,
|
||||
containerY: number,
|
||||
x: number,
|
||||
y: number,
|
||||
itemW: number,
|
||||
itemH: number,
|
||||
): boolean
|
||||
{
|
||||
let foundSlot = true;
|
||||
|
||||
@ -124,7 +132,14 @@ export class ContainerHelper
|
||||
* @param rotate is item rotated
|
||||
* @returns Location to place item
|
||||
*/
|
||||
public fillContainerMapWithItem(container2D: number[][], x: number, y: number, itemW: number, itemH: number, rotate: boolean): number[][]
|
||||
public fillContainerMapWithItem(
|
||||
container2D: number[][],
|
||||
x: number,
|
||||
y: number,
|
||||
itemW: number,
|
||||
itemH: number,
|
||||
rotate: boolean,
|
||||
): number[][]
|
||||
{
|
||||
const itemWidth = rotate ? itemH : itemW;
|
||||
const itemHeight = rotate ? itemW : itemH;
|
||||
|
@ -4,7 +4,13 @@ import { ItemHelper } from "@spt-aki/helpers/ItemHelper";
|
||||
import { NotificationSendHelper } from "@spt-aki/helpers/NotificationSendHelper";
|
||||
import { NotifierHelper } from "@spt-aki/helpers/NotifierHelper";
|
||||
import { Item } from "@spt-aki/models/eft/common/tables/IItem";
|
||||
import { Dialogue, Message, MessageContent, MessageItems, MessagePreview } from "@spt-aki/models/eft/profile/IAkiProfile";
|
||||
import {
|
||||
Dialogue,
|
||||
Message,
|
||||
MessageContent,
|
||||
MessageItems,
|
||||
MessagePreview,
|
||||
} from "@spt-aki/models/eft/profile/IAkiProfile";
|
||||
import { MessageType } from "@spt-aki/models/enums/MessageType";
|
||||
import { ILogger } from "@spt-aki/models/spt/utils/ILogger";
|
||||
import { DatabaseServer } from "@spt-aki/servers/DatabaseServer";
|
||||
@ -24,7 +30,7 @@ export class DialogueHelper
|
||||
@inject("NotifierHelper") protected notifierHelper: NotifierHelper,
|
||||
@inject("NotificationSendHelper") protected notificationSendHelper: NotificationSendHelper,
|
||||
@inject("LocalisationService") protected localisationService: LocalisationService,
|
||||
@inject("ItemHelper") protected itemHelper: ItemHelper
|
||||
@inject("ItemHelper") protected itemHelper: ItemHelper,
|
||||
)
|
||||
{}
|
||||
|
||||
@ -35,7 +41,7 @@ export class DialogueHelper
|
||||
{
|
||||
const result: MessageContent = {
|
||||
templateId: templateId,
|
||||
type: messageType
|
||||
type: messageType,
|
||||
};
|
||||
|
||||
if (maxStoreTime)
|
||||
@ -49,7 +55,13 @@ export class DialogueHelper
|
||||
/**
|
||||
* @deprecated Use MailSendService.sendMessage() or helpers
|
||||
*/
|
||||
public addDialogueMessage(dialogueID: string, messageContent: MessageContent, sessionID: string, rewards: Item[] = [], messageType = MessageType.NPC_TRADER): void
|
||||
public addDialogueMessage(
|
||||
dialogueID: string,
|
||||
messageContent: MessageContent,
|
||||
sessionID: string,
|
||||
rewards: Item[] = [],
|
||||
messageType = MessageType.NPC_TRADER,
|
||||
): void
|
||||
{
|
||||
const dialogueData = this.saveServer.getProfile(sessionID).dialogues;
|
||||
const isNewDialogue = !(dialogueID in dialogueData);
|
||||
@ -63,7 +75,7 @@ export class DialogueHelper
|
||||
messages: [],
|
||||
pinned: false,
|
||||
new: 0,
|
||||
attachmentsNew: 0
|
||||
attachmentsNew: 0,
|
||||
};
|
||||
|
||||
dialogueData[dialogueID] = dialogue;
|
||||
@ -79,7 +91,7 @@ export class DialogueHelper
|
||||
const stashId = this.hashUtil.generate();
|
||||
items = {
|
||||
stash: stashId,
|
||||
data: []
|
||||
data: [],
|
||||
};
|
||||
|
||||
rewards = this.itemHelper.replaceIDs(null, rewards);
|
||||
@ -95,7 +107,12 @@ export class DialogueHelper
|
||||
if (!itemTemplate)
|
||||
{
|
||||
// Can happen when modded items are insured + mod is removed
|
||||
this.logger.error(this.localisationService.getText("dialog-missing_item_template", {tpl: reward._tpl, type: MessageType[messageContent.type]}));
|
||||
this.logger.error(
|
||||
this.localisationService.getText("dialog-missing_item_template", {
|
||||
tpl: reward._tpl,
|
||||
type: MessageType[messageContent.type],
|
||||
}),
|
||||
);
|
||||
|
||||
continue;
|
||||
}
|
||||
@ -132,7 +149,9 @@ export class DialogueHelper
|
||||
items: items,
|
||||
maxStorageTime: messageContent.maxStorageTime,
|
||||
systemData: messageContent.systemData ? messageContent.systemData : undefined,
|
||||
profileChangeEvents: (messageContent.profileChangeEvents?.length === 0) ? messageContent.profileChangeEvents : undefined
|
||||
profileChangeEvents: (messageContent.profileChangeEvents?.length === 0) ?
|
||||
messageContent.profileChangeEvents :
|
||||
undefined,
|
||||
};
|
||||
|
||||
if (!message.templateId)
|
||||
@ -145,7 +164,10 @@ export class DialogueHelper
|
||||
// Offer Sold notifications are now separate from the main notification
|
||||
if (messageContent.type === MessageType.FLEAMARKET_MESSAGE && messageContent.ragfair)
|
||||
{
|
||||
const offerSoldMessage = this.notifierHelper.createRagfairOfferSoldNotification(message, messageContent.ragfair);
|
||||
const offerSoldMessage = this.notifierHelper.createRagfairOfferSoldNotification(
|
||||
message,
|
||||
messageContent.ragfair,
|
||||
);
|
||||
this.notificationSendHelper.sendMessage(sessionID, offerSoldMessage);
|
||||
message.type = MessageType.MESSAGE_WITH_ITEMS; // Should prevent getting the same notification popup twice
|
||||
}
|
||||
@ -167,7 +189,7 @@ export class DialogueHelper
|
||||
dt: message?.dt,
|
||||
type: message?.type,
|
||||
templateId: message?.templateId,
|
||||
uid: dialogue._id
|
||||
uid: dialogue._id,
|
||||
};
|
||||
|
||||
if (message?.text)
|
||||
@ -195,7 +217,7 @@ export class DialogueHelper
|
||||
const dialogueData = this.saveServer.getProfile(sessionID).dialogues;
|
||||
for (const dialogueId in dialogueData)
|
||||
{
|
||||
const message = dialogueData[dialogueId].messages.find(x => x._id === messageID);
|
||||
const message = dialogueData[dialogueId].messages.find((x) => x._id === messageID);
|
||||
if (!message)
|
||||
{
|
||||
continue;
|
||||
@ -211,7 +233,7 @@ export class DialogueHelper
|
||||
|
||||
// Check reward count when item being moved isn't in reward list
|
||||
// if count is 0, it means after this move the reward array will be empty and all rewards collected
|
||||
const rewardItemCount = message.items.data.filter(x => x._id !== itemId );
|
||||
const rewardItemCount = message.items.data.filter((x) => x._id !== itemId);
|
||||
if (rewardItemCount.length === 0)
|
||||
{
|
||||
message.rewardCollected = true;
|
||||
|
@ -15,7 +15,7 @@ export class DurabilityLimitsHelper
|
||||
constructor(
|
||||
@inject("RandomUtil") protected randomUtil: RandomUtil,
|
||||
@inject("BotHelper") protected botHelper: BotHelper,
|
||||
@inject("ConfigServer") protected configServer: ConfigServer
|
||||
@inject("ConfigServer") protected configServer: ConfigServer,
|
||||
)
|
||||
{
|
||||
this.botConfig = this.configServer.getConfig(ConfigTypes.BOT);
|
||||
@ -172,12 +172,14 @@ export class DurabilityLimitsHelper
|
||||
const maxDelta = this.getMaxWeaponDeltaFromConfig(botRole);
|
||||
const delta = this.randomUtil.getInt(minDelta, maxDelta);
|
||||
const result = maxDurability - delta;
|
||||
const durabilityValueMinLimit = Math.round((this.getMinWeaponLimitPercentFromConfig(botRole) / 100) * maxDurability);
|
||||
const durabilityValueMinLimit = Math.round(
|
||||
(this.getMinWeaponLimitPercentFromConfig(botRole) / 100) * maxDurability,
|
||||
);
|
||||
|
||||
// Dont let weapon dura go below the percent defined in config
|
||||
return (result >= durabilityValueMinLimit)
|
||||
? result
|
||||
: durabilityValueMinLimit;
|
||||
return (result >= durabilityValueMinLimit) ?
|
||||
result :
|
||||
durabilityValueMinLimit;
|
||||
}
|
||||
|
||||
protected generateArmorDurability(botRole: string, maxDurability: number): number
|
||||
@ -186,12 +188,14 @@ export class DurabilityLimitsHelper
|
||||
const maxDelta = this.getMaxArmorDeltaFromConfig(botRole);
|
||||
const delta = this.randomUtil.getInt(minDelta, maxDelta);
|
||||
const result = maxDurability - delta;
|
||||
const durabilityValueMinLimit = Math.round((this.getMinArmorLimitPercentFromConfig(botRole) / 100) * maxDurability);
|
||||
const durabilityValueMinLimit = Math.round(
|
||||
(this.getMinArmorLimitPercentFromConfig(botRole) / 100) * maxDurability,
|
||||
);
|
||||
|
||||
// Dont let armor dura go below the percent defined in config
|
||||
return (result >= durabilityValueMinLimit)
|
||||
? result
|
||||
: durabilityValueMinLimit;
|
||||
return (result >= durabilityValueMinLimit) ?
|
||||
result :
|
||||
durabilityValueMinLimit;
|
||||
}
|
||||
|
||||
protected getMinWeaponDeltaFromConfig(botRole: string): number
|
||||
|
@ -12,10 +12,9 @@ export class GameEventHelper
|
||||
|
||||
constructor(
|
||||
@inject("DatabaseServer") protected databaseServer: DatabaseServer,
|
||||
@inject("ConfigServer") protected configServer: ConfigServer
|
||||
@inject("ConfigServer") protected configServer: ConfigServer,
|
||||
)
|
||||
{
|
||||
this.seasonalEventConfig = this.configServer.getConfig(ConfigTypes.SEASONAL_EVENT);
|
||||
}
|
||||
|
||||
}
|
@ -89,7 +89,7 @@ export class HandbookHelper
|
||||
return this.handbookPriceCache.items.byId.get(tpl);
|
||||
}
|
||||
|
||||
const handbookItem = this.databaseServer.getTables().templates.handbook.Items.find(x => x.Id === tpl);
|
||||
const handbookItem = this.databaseServer.getTables().templates.handbook.Items.find((x) => x.Id === tpl);
|
||||
if (!handbookItem)
|
||||
{
|
||||
const newValue = 0;
|
||||
|
@ -21,7 +21,7 @@ export class HealthHelper
|
||||
@inject("WinstonLogger") protected logger: ILogger,
|
||||
@inject("TimeUtil") protected timeUtil: TimeUtil,
|
||||
@inject("SaveServer") protected saveServer: SaveServer,
|
||||
@inject("ConfigServer") protected configServer: ConfigServer
|
||||
@inject("ConfigServer") protected configServer: ConfigServer,
|
||||
)
|
||||
{
|
||||
this.healthConfig = this.configServer.getConfig(ConfigTypes.HEALTH);
|
||||
@ -36,11 +36,11 @@ export class HealthHelper
|
||||
{
|
||||
const profile = this.saveServer.getProfile(sessionID);
|
||||
|
||||
if (!profile.vitality) // Occurs on newly created profiles
|
||||
{
|
||||
if (!profile.vitality)
|
||||
{ // Occurs on newly created profiles
|
||||
profile.vitality = {
|
||||
health: null,
|
||||
effects: null
|
||||
effects: null,
|
||||
};
|
||||
}
|
||||
profile.vitality.health = {
|
||||
@ -53,7 +53,7 @@ export class HealthHelper
|
||||
LeftArm: 0,
|
||||
RightArm: 0,
|
||||
LeftLeg: 0,
|
||||
RightLeg: 0
|
||||
RightLeg: 0,
|
||||
};
|
||||
|
||||
profile.vitality.effects = {
|
||||
@ -63,7 +63,7 @@ export class HealthHelper
|
||||
LeftArm: {},
|
||||
RightArm: {},
|
||||
LeftLeg: {},
|
||||
RightLeg: {}
|
||||
RightLeg: {},
|
||||
};
|
||||
|
||||
return profile;
|
||||
@ -77,7 +77,13 @@ export class HealthHelper
|
||||
* @param addEffects Should effects be added or removed (default - add)
|
||||
* @param deleteExistingEffects Should all prior effects be removed before apply new ones
|
||||
*/
|
||||
public saveVitality(pmcData: IPmcData, request: ISyncHealthRequestData, sessionID: string, addEffects = true, deleteExistingEffects = true): void
|
||||
public saveVitality(
|
||||
pmcData: IPmcData,
|
||||
request: ISyncHealthRequestData,
|
||||
sessionID: string,
|
||||
addEffects = true,
|
||||
deleteExistingEffects = true,
|
||||
): void
|
||||
{
|
||||
const postRaidBodyParts = request.Health; // post raid health settings
|
||||
const profile = this.saveServer.getProfile(sessionID);
|
||||
@ -96,20 +102,26 @@ export class HealthHelper
|
||||
{
|
||||
profileEffects[bodyPart] = postRaidBodyParts[bodyPart].Effects;
|
||||
}
|
||||
if (request.IsAlive === true) // is player alive, not is limb alive
|
||||
{
|
||||
if (request.IsAlive === true)
|
||||
{ // is player alive, not is limb alive
|
||||
profileHealth[bodyPart] = postRaidBodyParts[bodyPart].Current;
|
||||
}
|
||||
else
|
||||
{
|
||||
profileHealth[bodyPart] = pmcData.Health.BodyParts[bodyPart].Health.Maximum * this.healthConfig.healthMultipliers.death;
|
||||
profileHealth[bodyPart] = pmcData.Health.BodyParts[bodyPart].Health.Maximum *
|
||||
this.healthConfig.healthMultipliers.death;
|
||||
}
|
||||
}
|
||||
|
||||
// Add effects to body parts
|
||||
if (addEffects)
|
||||
{
|
||||
this.saveEffects(pmcData, sessionID, this.jsonUtil.clone(this.saveServer.getProfile(sessionID).vitality.effects), deleteExistingEffects);
|
||||
this.saveEffects(
|
||||
pmcData,
|
||||
sessionID,
|
||||
this.jsonUtil.clone(this.saveServer.getProfile(sessionID).vitality.effects),
|
||||
deleteExistingEffects,
|
||||
);
|
||||
}
|
||||
|
||||
// Adjust hydration/energy/temp and limb hp
|
||||
@ -159,7 +171,10 @@ export class HealthHelper
|
||||
if (target === 0)
|
||||
{
|
||||
// Blacked body part
|
||||
target = Math.round(pmcData.Health.BodyParts[healthModifier].Health.Maximum * this.healthConfig.healthMultipliers.blacked);
|
||||
target = Math.round(
|
||||
pmcData.Health.BodyParts[healthModifier].Health.Maximum *
|
||||
this.healthConfig.healthMultipliers.blacked,
|
||||
);
|
||||
}
|
||||
|
||||
pmcData.Health.BodyParts[healthModifier].Health.Current = Math.round(target);
|
||||
@ -176,7 +191,12 @@ export class HealthHelper
|
||||
* @param bodyPartsWithEffects dict of body parts with effects that should be added to profile
|
||||
* @param addEffects Should effects be added back to profile
|
||||
*/
|
||||
protected saveEffects(pmcData: IPmcData, sessionId: string, bodyPartsWithEffects: Effects, deleteExistingEffects = true): void
|
||||
protected saveEffects(
|
||||
pmcData: IPmcData,
|
||||
sessionId: string,
|
||||
bodyPartsWithEffects: Effects,
|
||||
deleteExistingEffects = true,
|
||||
): void
|
||||
{
|
||||
if (!this.healthConfig.save.effects)
|
||||
{
|
||||
@ -208,7 +228,9 @@ export class HealthHelper
|
||||
// Sometimes the value can be Infinity instead of -1, blame HealthListener.cs in modules
|
||||
if (time === "Infinity")
|
||||
{
|
||||
this.logger.warning(`Effect ${effectType} found with value of Infinity, changed to -1, this is an issue with HealthListener.cs`);
|
||||
this.logger.warning(
|
||||
`Effect ${effectType} found with value of Infinity, changed to -1, this is an issue with HealthListener.cs`,
|
||||
);
|
||||
time = -1;
|
||||
}
|
||||
this.addEffect(pmcData, bodyPart, effectType, time);
|
||||
@ -245,7 +267,7 @@ export class HealthHelper
|
||||
}
|
||||
}
|
||||
|
||||
protected isEmpty(map: Record<string, { Time: number }>): boolean
|
||||
protected isEmpty(map: Record<string, {Time: number;}>): boolean
|
||||
{
|
||||
for (const key in map)
|
||||
{
|
||||
|
@ -48,7 +48,7 @@ export class HideoutHelper
|
||||
@inject("InventoryHelper") protected inventoryHelper: InventoryHelper,
|
||||
@inject("PlayerService") protected playerService: PlayerService,
|
||||
@inject("LocalisationService") protected localisationService: LocalisationService,
|
||||
@inject("ConfigServer") protected configServer: ConfigServer
|
||||
@inject("ConfigServer") protected configServer: ConfigServer,
|
||||
)
|
||||
{
|
||||
this.hideoutConfig = this.configServer.getConfig(ConfigTypes.HIDEOUT);
|
||||
@ -61,9 +61,13 @@ export class HideoutHelper
|
||||
* @param sessionID Session id
|
||||
* @returns client response
|
||||
*/
|
||||
public registerProduction(pmcData: IPmcData, body: IHideoutSingleProductionStartRequestData | IHideoutContinuousProductionStartRequestData, sessionID: string): IItemEventRouterResponse
|
||||
public registerProduction(
|
||||
pmcData: IPmcData,
|
||||
body: IHideoutSingleProductionStartRequestData | IHideoutContinuousProductionStartRequestData,
|
||||
sessionID: string,
|
||||
): IItemEventRouterResponse
|
||||
{
|
||||
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);
|
||||
if (!recipe)
|
||||
{
|
||||
this.logger.error(this.localisationService.getText("hideout-missing_recipe_in_db", body.recipeId));
|
||||
@ -71,7 +75,8 @@ export class HideoutHelper
|
||||
return this.httpResponse.appendErrorToOutput(this.eventOutputHolder.getOutput(sessionID));
|
||||
}
|
||||
|
||||
const modifiedProductionTime = recipe.productionTime - this.getCraftingSkillProductionTimeReduction(pmcData, recipe.productionTime);
|
||||
const modifiedProductionTime = recipe.productionTime -
|
||||
this.getCraftingSkillProductionTimeReduction(pmcData, recipe.productionTime);
|
||||
|
||||
// @Important: Here we need to be very exact:
|
||||
// - normal recipe: Production time value is stored in attribute "productionType" with small "p"
|
||||
@ -80,7 +85,11 @@ export class HideoutHelper
|
||||
{
|
||||
pmcData.Hideout.Production = {};
|
||||
}
|
||||
pmcData.Hideout.Production[body.recipeId] = this.initProduction(body.recipeId, modifiedProductionTime, recipe.needFuelForAllProductionTime);
|
||||
pmcData.Hideout.Production[body.recipeId] = this.initProduction(
|
||||
body.recipeId,
|
||||
modifiedProductionTime,
|
||||
recipe.needFuelForAllProductionTime,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -100,7 +109,7 @@ export class HideoutHelper
|
||||
Interrupted: false,
|
||||
NeedFuelForAllProductionTime: needFuelForAllProductionTime, // Used when sending to client
|
||||
needFuelForAllProductionTime: needFuelForAllProductionTime, // used when stored in production.json
|
||||
SkipTime: 0
|
||||
SkipTime: 0,
|
||||
};
|
||||
}
|
||||
|
||||
@ -124,12 +133,15 @@ export class HideoutHelper
|
||||
// Handle additional changes some bonuses need before being added
|
||||
switch (bonus.type)
|
||||
{
|
||||
case "StashSize": {
|
||||
case "StashSize":
|
||||
{
|
||||
// Find stash item and adjust tpl to new tpl from bonus
|
||||
const stashItem = pmcData.Inventory.items.find(x => x._id === pmcData.Inventory.stash);
|
||||
const stashItem = pmcData.Inventory.items.find((x) => x._id === pmcData.Inventory.stash);
|
||||
if (!stashItem)
|
||||
{
|
||||
this.logger.warning(`Unable to apply StashSize bonus, stash with id: ${pmcData.Inventory.stash} not found`);
|
||||
this.logger.warning(
|
||||
`Unable to apply StashSize bonus, stash with id: ${pmcData.Inventory.stash} not found`,
|
||||
);
|
||||
}
|
||||
|
||||
stashItem._tpl = bonus.templateId;
|
||||
@ -173,15 +185,19 @@ export class HideoutHelper
|
||||
* @param pmcData Player profile
|
||||
* @returns Properties
|
||||
*/
|
||||
protected getHideoutProperties(pmcData: IPmcData): { btcFarmCGs: number; isGeneratorOn: boolean; waterCollectorHasFilter: boolean; }
|
||||
protected getHideoutProperties(
|
||||
pmcData: IPmcData,
|
||||
): {btcFarmCGs: number; isGeneratorOn: boolean; waterCollectorHasFilter: boolean;}
|
||||
{
|
||||
const bitcoinFarm = pmcData.Hideout.Areas.find(x => x.type === HideoutAreas.BITCOIN_FARM);
|
||||
const bitcoinCount = bitcoinFarm?.slots.filter(slot => slot.item).length ?? 0; // Get slots with an item property
|
||||
const bitcoinFarm = pmcData.Hideout.Areas.find((x) => x.type === HideoutAreas.BITCOIN_FARM);
|
||||
const bitcoinCount = bitcoinFarm?.slots.filter((slot) => slot.item).length ?? 0; // Get slots with an item property
|
||||
|
||||
const hideoutProperties = {
|
||||
btcFarmCGs: bitcoinCount,
|
||||
isGeneratorOn: pmcData.Hideout.Areas.find(x => x.type === HideoutAreas.GENERATOR)?.active ?? false,
|
||||
waterCollectorHasFilter: this.doesWaterCollectorHaveFilter(pmcData.Hideout.Areas.find(x => x.type === HideoutAreas.WATER_COLLECTOR))
|
||||
isGeneratorOn: pmcData.Hideout.Areas.find((x) => x.type === HideoutAreas.GENERATOR)?.active ?? false,
|
||||
waterCollectorHasFilter: this.doesWaterCollectorHaveFilter(
|
||||
pmcData.Hideout.Areas.find((x) => x.type === HideoutAreas.WATER_COLLECTOR),
|
||||
),
|
||||
};
|
||||
|
||||
return hideoutProperties;
|
||||
@ -189,10 +205,10 @@ export class HideoutHelper
|
||||
|
||||
protected doesWaterCollectorHaveFilter(waterCollector: HideoutArea): boolean
|
||||
{
|
||||
if (waterCollector.level === 3) // can put filters in from L3
|
||||
{
|
||||
if (waterCollector.level === 3)
|
||||
{ // can put filters in from L3
|
||||
// Has filter in at least one slot
|
||||
return waterCollector.slots.some(x => x.item);
|
||||
return waterCollector.slots.some((x) => x.item);
|
||||
}
|
||||
|
||||
// No Filter
|
||||
@ -205,7 +221,11 @@ export class HideoutHelper
|
||||
* @param productionId id of water collection production to update
|
||||
* @param hideoutProperties Hideout properties
|
||||
*/
|
||||
protected updateWaterCollectorProductionTimer(pmcData: IPmcData, productionId: string, hideoutProperties: { btcFarmCGs?: number; isGeneratorOn: boolean; waterCollectorHasFilter: boolean; }): void
|
||||
protected updateWaterCollectorProductionTimer(
|
||||
pmcData: IPmcData,
|
||||
productionId: string,
|
||||
hideoutProperties: {btcFarmCGs?: number; isGeneratorOn: boolean; waterCollectorHasFilter: boolean;},
|
||||
): void
|
||||
{
|
||||
const timeElapsed = this.getTimeElapsedSinceLastServerTick(pmcData, hideoutProperties.isGeneratorOn);
|
||||
if (hideoutProperties.waterCollectorHasFilter)
|
||||
@ -219,7 +239,10 @@ export class HideoutHelper
|
||||
* @param pmcData Profile to check for productions and update
|
||||
* @param hideoutProperties Hideout properties
|
||||
*/
|
||||
protected updateProductionTimers(pmcData: IPmcData, hideoutProperties: { btcFarmCGs: number; isGeneratorOn: boolean; waterCollectorHasFilter: boolean; }): void
|
||||
protected updateProductionTimers(
|
||||
pmcData: IPmcData,
|
||||
hideoutProperties: {btcFarmCGs: number; isGeneratorOn: boolean; waterCollectorHasFilter: boolean;},
|
||||
): void
|
||||
{
|
||||
const recipes = this.databaseServer.getTables().hideout.production;
|
||||
|
||||
@ -255,12 +278,16 @@ export class HideoutHelper
|
||||
|
||||
if (prodId === HideoutHelper.bitcoinFarm)
|
||||
{
|
||||
pmcData.Hideout.Production[prodId] = this.updateBitcoinFarm(pmcData, hideoutProperties.btcFarmCGs, hideoutProperties.isGeneratorOn);
|
||||
pmcData.Hideout.Production[prodId] = this.updateBitcoinFarm(
|
||||
pmcData,
|
||||
hideoutProperties.btcFarmCGs,
|
||||
hideoutProperties.isGeneratorOn,
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Other recipes not covered by above
|
||||
const recipe = recipes.find(r => r._id === prodId);
|
||||
const recipe = recipes.find((r) => r._id === prodId);
|
||||
if (!recipe)
|
||||
{
|
||||
this.logger.error(this.localisationService.getText("hideout-missing_recipe_for_area", prodId));
|
||||
@ -279,7 +306,12 @@ export class HideoutHelper
|
||||
* @param recipe Recipe data being crafted
|
||||
* @param hideoutProperties
|
||||
*/
|
||||
protected updateProductionProgress(pmcData: IPmcData, prodId: string, recipe: IHideoutProduction, hideoutProperties: { btcFarmCGs?: number; isGeneratorOn: boolean; waterCollectorHasFilter?: boolean; }): void
|
||||
protected updateProductionProgress(
|
||||
pmcData: IPmcData,
|
||||
prodId: string,
|
||||
recipe: IHideoutProduction,
|
||||
hideoutProperties: {btcFarmCGs?: number; isGeneratorOn: boolean; waterCollectorHasFilter?: boolean;},
|
||||
): void
|
||||
{
|
||||
// Production is complete, no need to do any calculations
|
||||
if (this.doesProgressMatchProductionTime(pmcData, prodId))
|
||||
@ -321,7 +353,8 @@ export class HideoutHelper
|
||||
*/
|
||||
protected updateScavCaseProductionTimer(pmcData: IPmcData, productionId: string): void
|
||||
{
|
||||
const timeElapsed = (this.timeUtil.getTimestamp() - pmcData.Hideout.Production[productionId].StartTimestamp) - pmcData.Hideout.Production[productionId].Progress;
|
||||
const timeElapsed = (this.timeUtil.getTimestamp() - pmcData.Hideout.Production[productionId].StartTimestamp) -
|
||||
pmcData.Hideout.Production[productionId].Progress;
|
||||
pmcData.Hideout.Production[productionId].Progress += timeElapsed;
|
||||
}
|
||||
|
||||
@ -331,7 +364,11 @@ export class HideoutHelper
|
||||
* @param pmcData Profile to update areas of
|
||||
* @param hideoutProperties hideout properties
|
||||
*/
|
||||
protected updateAreasWithResources(sessionID: string, pmcData: IPmcData, hideoutProperties: { btcFarmCGs: number; isGeneratorOn: boolean; waterCollectorHasFilter: boolean; }): void
|
||||
protected updateAreasWithResources(
|
||||
sessionID: string,
|
||||
pmcData: IPmcData,
|
||||
hideoutProperties: {btcFarmCGs: number; isGeneratorOn: boolean; waterCollectorHasFilter: boolean;},
|
||||
): void
|
||||
{
|
||||
for (const area of pmcData.Hideout.Areas)
|
||||
{
|
||||
@ -361,9 +398,10 @@ export class HideoutHelper
|
||||
{
|
||||
// 1 resource last 14 min 27 sec, 1/14.45/60 = 0.00115
|
||||
// 10-10-2021 From wiki, 1 resource last 12 minutes 38 seconds, 1/12.63333/60 = 0.00131
|
||||
let fuelDrainRate = this.databaseServer.getTables().hideout.settings.generatorFuelFlowRate * this.hideoutConfig.runIntervalSeconds;
|
||||
let fuelDrainRate = this.databaseServer.getTables().hideout.settings.generatorFuelFlowRate *
|
||||
this.hideoutConfig.runIntervalSeconds;
|
||||
// implemented moddable bonus for fuel consumption bonus instead of using solar power variable as before
|
||||
const fuelBonus = pmcData.Bonuses.find(b => b.type === "FuelConsumption");
|
||||
const fuelBonus = pmcData.Bonuses.find((b) => b.type === "FuelConsumption");
|
||||
const fuelBonusPercent = 1.0 - (fuelBonus ? Math.abs(fuelBonus.value) : 0) / 100;
|
||||
fuelDrainRate *= fuelBonusPercent;
|
||||
// Hideout management resource consumption bonus:
|
||||
@ -376,9 +414,9 @@ export class HideoutHelper
|
||||
{
|
||||
if (generatorArea.slots[i].item)
|
||||
{
|
||||
let resourceValue = (generatorArea.slots[i].item[0].upd?.Resource)
|
||||
? generatorArea.slots[i].item[0].upd.Resource.Value
|
||||
: null;
|
||||
let resourceValue = (generatorArea.slots[i].item[0].upd?.Resource) ?
|
||||
generatorArea.slots[i].item[0].upd.Resource.Value :
|
||||
null;
|
||||
if (resourceValue === 0)
|
||||
{
|
||||
continue;
|
||||
@ -386,9 +424,9 @@ export class HideoutHelper
|
||||
else if (!resourceValue)
|
||||
{
|
||||
const fuelItem = HideoutHelper.expeditionaryFuelTank;
|
||||
resourceValue = generatorArea.slots[i].item[0]._tpl === fuelItem
|
||||
? 60 - fuelDrainRate
|
||||
: 100 - fuelDrainRate;
|
||||
resourceValue = generatorArea.slots[i].item[0]._tpl === fuelItem ?
|
||||
60 - fuelDrainRate :
|
||||
100 - fuelDrainRate;
|
||||
pointsConsumed = fuelDrainRate;
|
||||
}
|
||||
else
|
||||
@ -432,7 +470,12 @@ export class HideoutHelper
|
||||
}
|
||||
}
|
||||
|
||||
protected updateWaterCollector(sessionId: string, pmcData: IPmcData, area: HideoutArea, isGeneratorOn: boolean): void
|
||||
protected updateWaterCollector(
|
||||
sessionId: string,
|
||||
pmcData: IPmcData,
|
||||
area: HideoutArea,
|
||||
isGeneratorOn: boolean,
|
||||
): void
|
||||
{
|
||||
// Skip water collector when not level 3 (cant collect until 3)
|
||||
if (area.level !== 3)
|
||||
@ -454,7 +497,7 @@ export class HideoutHelper
|
||||
recipeId: HideoutHelper.waterCollector,
|
||||
Action: "HideoutSingleProductionStart",
|
||||
items: [],
|
||||
timestamp: this.timeUtil.getTimestamp()
|
||||
timestamp: this.timeUtil.getTimestamp(),
|
||||
};
|
||||
|
||||
this.registerProduction(pmcData, recipe, sessionId);
|
||||
@ -469,13 +512,23 @@ export class HideoutHelper
|
||||
* @param pmcData Player profile
|
||||
* @returns Updated HideoutArea object
|
||||
*/
|
||||
protected updateWaterFilters(waterFilterArea: HideoutArea, production: Production, isGeneratorOn: boolean, pmcData: IPmcData): HideoutArea
|
||||
protected updateWaterFilters(
|
||||
waterFilterArea: HideoutArea,
|
||||
production: Production,
|
||||
isGeneratorOn: boolean,
|
||||
pmcData: IPmcData,
|
||||
): HideoutArea
|
||||
{
|
||||
let filterDrainRate = this.getWaterFilterDrainRate(pmcData);
|
||||
const productionTime = this.getTotalProductionTimeSeconds(HideoutHelper.waterCollector);
|
||||
const secondsSinceServerTick = this.getTimeElapsedSinceLastServerTick(pmcData, isGeneratorOn);
|
||||
|
||||
filterDrainRate = this.adjustWaterFilterDrainRate(secondsSinceServerTick, productionTime, production.Progress, filterDrainRate);
|
||||
filterDrainRate = this.adjustWaterFilterDrainRate(
|
||||
secondsSinceServerTick,
|
||||
productionTime,
|
||||
production.Progress,
|
||||
filterDrainRate,
|
||||
);
|
||||
|
||||
// Production hasn't completed
|
||||
let pointsConsumed = 0;
|
||||
@ -489,9 +542,9 @@ export class HideoutHelper
|
||||
if (waterFilterArea.slots[i].item)
|
||||
{
|
||||
// How many units of filter are left
|
||||
let resourceValue = (waterFilterArea.slots[i].item[0].upd?.Resource)
|
||||
? waterFilterArea.slots[i].item[0].upd.Resource.Value
|
||||
: null;
|
||||
let resourceValue = (waterFilterArea.slots[i].item[0].upd?.Resource) ?
|
||||
waterFilterArea.slots[i].item[0].upd.Resource.Value :
|
||||
null;
|
||||
if (!resourceValue)
|
||||
{
|
||||
// None left
|
||||
@ -500,7 +553,8 @@ export class HideoutHelper
|
||||
}
|
||||
else
|
||||
{
|
||||
pointsConsumed = (waterFilterArea.slots[i].item[0].upd.Resource.UnitsConsumed || 0) + filterDrainRate;
|
||||
pointsConsumed = (waterFilterArea.slots[i].item[0].upd.Resource.UnitsConsumed || 0) +
|
||||
filterDrainRate;
|
||||
resourceValue -= filterDrainRate;
|
||||
}
|
||||
|
||||
@ -545,11 +599,17 @@ export class HideoutHelper
|
||||
* @param baseFilterDrainRate Base drain rate
|
||||
* @returns
|
||||
*/
|
||||
protected adjustWaterFilterDrainRate(secondsSinceServerTick: number, totalProductionTime: number, productionProgress: number, baseFilterDrainRate: number): number
|
||||
protected adjustWaterFilterDrainRate(
|
||||
secondsSinceServerTick: number,
|
||||
totalProductionTime: number,
|
||||
productionProgress: number,
|
||||
baseFilterDrainRate: number,
|
||||
): number
|
||||
{
|
||||
const drainRateMultiplier = secondsSinceServerTick > totalProductionTime
|
||||
? (totalProductionTime - productionProgress) // more time passed than prod time, get total minus the current progress
|
||||
: secondsSinceServerTick;
|
||||
const drainRateMultiplier = secondsSinceServerTick > totalProductionTime ?
|
||||
(totalProductionTime - productionProgress) // more time passed than prod time, get total minus the current progress
|
||||
:
|
||||
secondsSinceServerTick;
|
||||
|
||||
// Multiply drain rate by calculated multiplier
|
||||
baseFilterDrainRate *= drainRateMultiplier;
|
||||
@ -578,7 +638,7 @@ export class HideoutHelper
|
||||
*/
|
||||
protected getTotalProductionTimeSeconds(prodId: string): number
|
||||
{
|
||||
const recipe = this.databaseServer.getTables().hideout.production.find(prod => prod._id === prodId);
|
||||
const recipe = this.databaseServer.getTables().hideout.production.find((prod) => prod._id === prodId);
|
||||
|
||||
return (recipe.productionTime || 0);
|
||||
}
|
||||
@ -596,8 +656,8 @@ export class HideoutHelper
|
||||
StackObjectsCount: stackCount,
|
||||
Resource: {
|
||||
Value: resourceValue,
|
||||
UnitsConsumed: resourceUnitsConsumed
|
||||
}
|
||||
UnitsConsumed: resourceUnitsConsumed,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@ -608,7 +668,8 @@ export class HideoutHelper
|
||||
Lasts for 17 hours 38 minutes and 49 seconds (23 hours 31 minutes and 45 seconds with elite hideout management skill),
|
||||
300/17.64694/60/60 = 0.004722
|
||||
*/
|
||||
let filterDrainRate = this.databaseServer.getTables().hideout.settings.airFilterUnitFlowRate * this.hideoutConfig.runIntervalSeconds;
|
||||
let filterDrainRate = this.databaseServer.getTables().hideout.settings.airFilterUnitFlowRate *
|
||||
this.hideoutConfig.runIntervalSeconds;
|
||||
// Hideout management resource consumption bonus:
|
||||
const hideoutManagementConsumptionBonus = 1.0 - this.getHideoutManagementConsumptionBonus(pmcData);
|
||||
filterDrainRate *= hideoutManagementConsumptionBonus;
|
||||
@ -618,9 +679,9 @@ export class HideoutHelper
|
||||
{
|
||||
if (airFilterArea.slots[i].item)
|
||||
{
|
||||
let resourceValue = (airFilterArea.slots[i].item[0].upd?.Resource)
|
||||
? airFilterArea.slots[i].item[0].upd.Resource.Value
|
||||
: null;
|
||||
let resourceValue = (airFilterArea.slots[i].item[0].upd?.Resource) ?
|
||||
airFilterArea.slots[i].item[0].upd.Resource.Value :
|
||||
null;
|
||||
if (!resourceValue)
|
||||
{
|
||||
resourceValue = 300 - filterDrainRate;
|
||||
@ -647,8 +708,8 @@ export class HideoutHelper
|
||||
StackObjectsCount: 1,
|
||||
Resource: {
|
||||
Value: resourceValue,
|
||||
UnitsConsumed: pointsConsumed
|
||||
}
|
||||
UnitsConsumed: pointsConsumed,
|
||||
},
|
||||
};
|
||||
this.logger.debug(`Air filter: ${resourceValue} filter left on slot ${i + 1}`);
|
||||
break; // Break here to avoid updating all filters
|
||||
@ -666,7 +727,9 @@ export class HideoutHelper
|
||||
protected updateBitcoinFarm(pmcData: IPmcData, btcFarmCGs: number, isGeneratorOn: boolean): Production
|
||||
{
|
||||
const btcProd = pmcData.Hideout.Production[HideoutHelper.bitcoinFarm];
|
||||
const bitcoinProdData = this.databaseServer.getTables().hideout.production.find(p => p._id === "5d5c205bd582a50d042a3c0e");
|
||||
const bitcoinProdData = this.databaseServer.getTables().hideout.production.find((p) =>
|
||||
p._id === "5d5c205bd582a50d042a3c0e"
|
||||
);
|
||||
const coinSlotCount = this.getBTCSlots(pmcData);
|
||||
|
||||
// Full on bitcoins, halt progress
|
||||
@ -718,7 +781,8 @@ export class HideoutHelper
|
||||
}
|
||||
*/
|
||||
// BSG finally fixed their settings, they now get loaded from the settings and used in the client
|
||||
const coinCraftTimeSeconds = bitcoinProdData.productionTime / (1 + (btcFarmCGs - 1) * this.databaseServer.getTables().hideout.settings.gpuBoostRate);
|
||||
const coinCraftTimeSeconds = bitcoinProdData.productionTime /
|
||||
(1 + (btcFarmCGs - 1) * this.databaseServer.getTables().hideout.settings.gpuBoostRate);
|
||||
while (btcProd.Progress > coinCraftTimeSeconds)
|
||||
{
|
||||
if (btcProd.Products.length < coinSlotCount)
|
||||
@ -753,8 +817,8 @@ export class HideoutHelper
|
||||
_id: this.hashUtil.generate(),
|
||||
_tpl: "59faff1d86f7746c51718c9c",
|
||||
upd: {
|
||||
StackObjectsCount: 1
|
||||
}
|
||||
StackObjectsCount: 1,
|
||||
},
|
||||
});
|
||||
|
||||
btcProd.Progress -= coinCraftTimeSeconds;
|
||||
@ -767,13 +831,17 @@ export class HideoutHelper
|
||||
* @param recipe Hideout production recipe being crafted we need the ticks for
|
||||
* @returns Amount of time elapsed in seconds
|
||||
*/
|
||||
protected getTimeElapsedSinceLastServerTick(pmcData: IPmcData, isGeneratorOn: boolean, recipe: IHideoutProduction = null): number
|
||||
protected getTimeElapsedSinceLastServerTick(
|
||||
pmcData: IPmcData,
|
||||
isGeneratorOn: boolean,
|
||||
recipe: IHideoutProduction = null,
|
||||
): number
|
||||
{
|
||||
// Reduce time elapsed (and progress) when generator is off
|
||||
let timeElapsed = this.timeUtil.getTimestamp() - pmcData.Hideout.sptUpdateLastRunTimestamp;
|
||||
|
||||
if (recipe?.areaType === HideoutAreas.LAVATORY) // Lavatory works at 100% when power is on / off
|
||||
{
|
||||
if (recipe?.areaType === HideoutAreas.LAVATORY)
|
||||
{ // Lavatory works at 100% when power is on / off
|
||||
return timeElapsed;
|
||||
}
|
||||
|
||||
@ -792,7 +860,9 @@ export class HideoutHelper
|
||||
*/
|
||||
protected getBTCSlots(pmcData: IPmcData): number
|
||||
{
|
||||
const bitcoinProduction = this.databaseServer.getTables().hideout.production.find(p => p._id === HideoutHelper.bitcoinFarm);
|
||||
const bitcoinProduction = this.databaseServer.getTables().hideout.production.find((p) =>
|
||||
p._id === HideoutHelper.bitcoinFarm
|
||||
);
|
||||
const productionSlots = bitcoinProduction?.productionLimitCount || 3;
|
||||
const hasManagementSkillSlots = this.profileHelper.hasEliteSkillLevel(SkillTypes.HIDEOUT_MANAGEMENT, pmcData);
|
||||
const managementSlotsCount = this.getBitcoinMinerContainerSlotSize() || 2;
|
||||
@ -805,7 +875,8 @@ export class HideoutHelper
|
||||
*/
|
||||
protected getBitcoinMinerContainerSlotSize(): number
|
||||
{
|
||||
return this.databaseServer.getTables().globals.config.SkillsSettings.HideoutManagement.EliteSlots.BitcoinFarm.Container;
|
||||
return this.databaseServer.getTables().globals.config.SkillsSettings.HideoutManagement.EliteSlots.BitcoinFarm
|
||||
.Container;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -826,11 +897,13 @@ export class HideoutHelper
|
||||
// at level 1 you already get 0.5%, so it goes up until level 50. For some reason the wiki
|
||||
// says that it caps at level 51 with 25% but as per dump data that is incorrect apparently
|
||||
let roundedLevel = Math.floor(hideoutManagementSkill.Progress / 100);
|
||||
roundedLevel = (roundedLevel === 51)
|
||||
? roundedLevel - 1
|
||||
: roundedLevel;
|
||||
roundedLevel = (roundedLevel === 51) ?
|
||||
roundedLevel - 1 :
|
||||
roundedLevel;
|
||||
|
||||
return (roundedLevel * this.databaseServer.getTables().globals.config.SkillsSettings.HideoutManagement.ConsumptionReductionPerLevel) / 100;
|
||||
return (roundedLevel *
|
||||
this.databaseServer.getTables().globals.config.SkillsSettings.HideoutManagement
|
||||
.ConsumptionReductionPerLevel) / 100;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -841,7 +914,7 @@ export class HideoutHelper
|
||||
*/
|
||||
protected getCraftingSkillProductionTimeReduction(pmcData: IPmcData, productionTime: number): number
|
||||
{
|
||||
const craftingSkill = pmcData.Skills.Common.find(x=> x.Id === SkillTypes.CRAFTING);
|
||||
const craftingSkill = pmcData.Skills.Common.find((x) => x.Id === SkillTypes.CRAFTING);
|
||||
if (!craftingSkill)
|
||||
{
|
||||
return productionTime;
|
||||
@ -865,7 +938,11 @@ export class HideoutHelper
|
||||
* @param sessionId Session id
|
||||
* @returns IItemEventRouterResponse
|
||||
*/
|
||||
public getBTC(pmcData: IPmcData, request: IHideoutTakeProductionRequestData, sessionId: string): IItemEventRouterResponse
|
||||
public getBTC(
|
||||
pmcData: IPmcData,
|
||||
request: IHideoutTakeProductionRequestData,
|
||||
sessionId: string,
|
||||
): IItemEventRouterResponse
|
||||
{
|
||||
const output = this.eventOutputHolder.getOutput(sessionId);
|
||||
|
||||
@ -911,9 +988,9 @@ export class HideoutHelper
|
||||
items: [{
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
item_id: HideoutHelper.bitcoin,
|
||||
count: pmcData.Hideout.Production[HideoutHelper.bitcoinFarm].Products.length
|
||||
count: pmcData.Hideout.Production[HideoutHelper.bitcoinFarm].Products.length,
|
||||
}],
|
||||
tid: "ragfair"
|
||||
tid: "ragfair",
|
||||
};
|
||||
}
|
||||
|
||||
@ -923,9 +1000,9 @@ export class HideoutHelper
|
||||
*/
|
||||
public unlockHideoutWallInProfile(pmcProfile: IPmcData): void
|
||||
{
|
||||
const waterCollector = pmcProfile.Hideout.Areas.find(x => x.type === HideoutAreas.WATER_COLLECTOR);
|
||||
const medStation = pmcProfile.Hideout.Areas.find(x => x.type === HideoutAreas.MEDSTATION);
|
||||
const wall = pmcProfile.Hideout.Areas.find(x => x.type === HideoutAreas.EMERGENCY_WALL);
|
||||
const waterCollector = pmcProfile.Hideout.Areas.find((x) => x.type === HideoutAreas.WATER_COLLECTOR);
|
||||
const medStation = pmcProfile.Hideout.Areas.find((x) => x.type === HideoutAreas.MEDSTATION);
|
||||
const wall = pmcProfile.Hideout.Areas.find((x) => x.type === HideoutAreas.EMERGENCY_WALL);
|
||||
|
||||
// No collector or med station, skip
|
||||
if (!(waterCollector && medStation))
|
||||
@ -947,9 +1024,9 @@ export class HideoutHelper
|
||||
*/
|
||||
protected hideoutImprovementIsComplete(improvement: IHideoutImprovement): boolean
|
||||
{
|
||||
return improvement?.completed
|
||||
? true
|
||||
: false;
|
||||
return improvement?.completed ?
|
||||
true :
|
||||
false;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -961,7 +1038,10 @@ export class HideoutHelper
|
||||
for (const improvementId in pmcProfile.Hideout.Improvement)
|
||||
{
|
||||
const improvementDetails = pmcProfile.Hideout.Improvement[improvementId];
|
||||
if (improvementDetails.completed === false && improvementDetails.improveCompleteTimestamp < this.timeUtil.getTimestamp())
|
||||
if (
|
||||
improvementDetails.completed === false &&
|
||||
improvementDetails.improveCompleteTimestamp < this.timeUtil.getTimestamp()
|
||||
)
|
||||
{
|
||||
improvementDetails.completed = true;
|
||||
}
|
||||
|
@ -18,11 +18,11 @@ export class HttpServerHelper
|
||||
json: "application/json",
|
||||
png: "image/png",
|
||||
svg: "image/svg+xml",
|
||||
txt: "text/plain"
|
||||
txt: "text/plain",
|
||||
};
|
||||
|
||||
constructor(
|
||||
@inject("ConfigServer") protected configServer: ConfigServer
|
||||
@inject("ConfigServer") protected configServer: ConfigServer,
|
||||
)
|
||||
{
|
||||
this.httpConfig = this.configServer.getConfig(ConfigTypes.HTTP);
|
||||
|
@ -39,7 +39,7 @@ export class InRaidHelper
|
||||
@inject("PaymentHelper") protected paymentHelper: PaymentHelper,
|
||||
@inject("LocalisationService") protected localisationService: LocalisationService,
|
||||
@inject("ProfileFixerService") protected profileFixerService: ProfileFixerService,
|
||||
@inject("ConfigServer") protected configServer: ConfigServer
|
||||
@inject("ConfigServer") protected configServer: ConfigServer,
|
||||
)
|
||||
{
|
||||
this.lostOnDeathConfig = this.configServer.getConfig(ConfigTypes.LOST_ON_DEATH);
|
||||
@ -95,7 +95,12 @@ export class InRaidHelper
|
||||
{
|
||||
return acc + standingForKill;
|
||||
}
|
||||
this.logger.warning(this.localisationService.getText("inraid-missing_standing_for_kill", {victimSide: victim.Side, victimRole: victim.Role}));
|
||||
this.logger.warning(
|
||||
this.localisationService.getText("inraid-missing_standing_for_kill", {
|
||||
victimSide: victim.Side,
|
||||
victimRole: victim.Role,
|
||||
}),
|
||||
);
|
||||
|
||||
return acc;
|
||||
}, existingFenceStanding);
|
||||
@ -131,7 +136,11 @@ export class InRaidHelper
|
||||
* @param sessionID Session id
|
||||
* @returns Reset profile object
|
||||
*/
|
||||
public updateProfileBaseStats(profileData: IPmcData, saveProgressRequest: ISaveProgressRequestData, sessionID: string): IPmcData
|
||||
public updateProfileBaseStats(
|
||||
profileData: IPmcData,
|
||||
saveProgressRequest: ISaveProgressRequestData,
|
||||
sessionID: string,
|
||||
): IPmcData
|
||||
{
|
||||
// remove old skill fatigue
|
||||
this.resetSkillPointsEarnedDuringRaid(saveProgressRequest.profile);
|
||||
@ -168,7 +177,9 @@ export class InRaidHelper
|
||||
|
||||
if (matchingPreRaidCounter.value !== postRaidValue)
|
||||
{
|
||||
this.logger.error(`Backendcounter: ${backendCounterKey} value is different post raid, old: ${matchingPreRaidCounter.value} new: ${postRaidValue}`);
|
||||
this.logger.error(
|
||||
`Backendcounter: ${backendCounterKey} value is different post raid, old: ${matchingPreRaidCounter.value} new: ${postRaidValue}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -199,7 +210,6 @@ export class InRaidHelper
|
||||
return profileData;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Look for quests not are now status = fail that were not failed pre-raid and run the failQuest() function
|
||||
* @param sessionId Player id
|
||||
@ -207,7 +217,12 @@ export class InRaidHelper
|
||||
* @param preRaidQuests Quests prior to starting raid
|
||||
* @param postRaidQuests Quest after raid
|
||||
*/
|
||||
protected processFailedQuests(sessionId: string, pmcData: IPmcData, preRaidQuests: IQuestStatus[], postRaidQuests: IQuestStatus[]): void
|
||||
protected processFailedQuests(
|
||||
sessionId: string,
|
||||
pmcData: IPmcData,
|
||||
preRaidQuests: IQuestStatus[],
|
||||
postRaidQuests: IQuestStatus[],
|
||||
): void
|
||||
{
|
||||
if (!preRaidQuests)
|
||||
{
|
||||
@ -219,7 +234,7 @@ export class InRaidHelper
|
||||
for (const postRaidQuest of postRaidQuests)
|
||||
{
|
||||
// Find matching pre-raid quest
|
||||
const preRaidQuest = preRaidQuests?.find(x => x.qid === postRaidQuest.qid);
|
||||
const preRaidQuest = preRaidQuests?.find((x) => x.qid === postRaidQuest.qid);
|
||||
if (preRaidQuest)
|
||||
{
|
||||
// Post-raid quest is failed but wasn't pre-raid
|
||||
@ -230,12 +245,11 @@ export class InRaidHelper
|
||||
const failBody: IFailQuestRequestData = {
|
||||
Action: "QuestComplete",
|
||||
qid: postRaidQuest.qid,
|
||||
removeExcessItems: true
|
||||
removeExcessItems: true,
|
||||
};
|
||||
this.questHelper.failQuest(pmcData, failBody, sessionId);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -252,7 +266,10 @@ export class InRaidHelper
|
||||
* @param saveProgressRequest post-raid request
|
||||
* @param profileData player profile on server
|
||||
*/
|
||||
protected transferPostRaidLimbEffectsToProfile(saveProgressRequest: ISaveProgressRequestData, profileData: IPmcData): void
|
||||
protected transferPostRaidLimbEffectsToProfile(
|
||||
saveProgressRequest: ISaveProgressRequestData,
|
||||
profileData: IPmcData,
|
||||
): void
|
||||
{
|
||||
// Iterate over each body part
|
||||
for (const bodyPartId in saveProgressRequest.profile.Health.BodyParts)
|
||||
@ -287,7 +304,10 @@ export class InRaidHelper
|
||||
* @param tradersServerProfile Server
|
||||
* @param tradersClientProfile Client
|
||||
*/
|
||||
protected applyTraderStandingAdjustments(tradersServerProfile: Record<string, TraderInfo>, tradersClientProfile: Record<string, TraderInfo>): void
|
||||
protected applyTraderStandingAdjustments(
|
||||
tradersServerProfile: Record<string, TraderInfo>,
|
||||
tradersClientProfile: Record<string, TraderInfo>,
|
||||
): void
|
||||
{
|
||||
for (const traderId in tradersClientProfile)
|
||||
{
|
||||
@ -356,12 +376,13 @@ export class InRaidHelper
|
||||
public removeSpawnedInSessionPropertyFromItems(postRaidProfile: IPostRaidPmcData): IPostRaidPmcData
|
||||
{
|
||||
const dbItems = this.databaseServer.getTables().templates.items;
|
||||
const itemsToRemovePropertyFrom = postRaidProfile.Inventory.items.filter(x =>
|
||||
const itemsToRemovePropertyFrom = postRaidProfile.Inventory.items.filter((x) =>
|
||||
{
|
||||
// Has upd object + upd.SpawnedInSession property + not a quest item
|
||||
return "upd" in x && "SpawnedInSession" in x.upd
|
||||
&& !dbItems[x._tpl]._props.QuestItem
|
||||
&& !(this.inRaidConfig.keepFiRSecureContainerOnDeath && this.itemHelper.itemIsInsideContainer(x, "SecuredContainer", postRaidProfile.Inventory.items));
|
||||
return "upd" in x && "SpawnedInSession" in x.upd &&
|
||||
!dbItems[x._tpl]._props.QuestItem &&
|
||||
!(this.inRaidConfig.keepFiRSecureContainerOnDeath &&
|
||||
this.itemHelper.itemIsInsideContainer(x, "SecuredContainer", postRaidProfile.Inventory.items));
|
||||
});
|
||||
|
||||
for (const item of itemsToRemovePropertyFrom)
|
||||
@ -409,7 +430,7 @@ export class InRaidHelper
|
||||
public deleteInventory(pmcData: IPmcData, sessionID: string): void
|
||||
{
|
||||
// Get inventory item ids to remove from players profile
|
||||
const itemIdsToDeleteFromProfile = this.getInventoryItemsLostOnDeath(pmcData).map(x => x._id);
|
||||
const itemIdsToDeleteFromProfile = this.getInventoryItemsLostOnDeath(pmcData).map((x) => x._id);
|
||||
for (const itemId of itemIdsToDeleteFromProfile)
|
||||
{
|
||||
this.inventoryHelper.removeItem(pmcData, itemId, sessionID);
|
||||
@ -430,7 +451,7 @@ export class InRaidHelper
|
||||
const equipment = pmcProfile?.Inventory?.equipment;
|
||||
const questRaidItems = pmcProfile?.Inventory?.questRaidItems;
|
||||
|
||||
return inventoryItems.filter(x =>
|
||||
return inventoryItems.filter((x) =>
|
||||
{
|
||||
// Keep items flagged as kept after death
|
||||
if (this.isItemKeptAfterDeath(pmcProfile, x))
|
||||
@ -461,13 +482,13 @@ export class InRaidHelper
|
||||
*/
|
||||
protected getBaseItemsInRigPocketAndBackpack(pmcData: IPmcData): Item[]
|
||||
{
|
||||
const rig = pmcData.Inventory.items.find(x => x.slotId === "TacticalVest");
|
||||
const pockets = pmcData.Inventory.items.find(x => x.slotId === "Pockets");
|
||||
const backpack = pmcData.Inventory.items.find(x => x.slotId === "Backpack");
|
||||
const rig = pmcData.Inventory.items.find((x) => x.slotId === "TacticalVest");
|
||||
const pockets = pmcData.Inventory.items.find((x) => x.slotId === "Pockets");
|
||||
const backpack = pmcData.Inventory.items.find((x) => x.slotId === "Backpack");
|
||||
|
||||
const baseItemsInRig = pmcData.Inventory.items.filter(x => x.parentId === rig?._id);
|
||||
const baseItemsInPockets = pmcData.Inventory.items.filter(x => x.parentId === pockets?._id);
|
||||
const baseItemsInBackpack = pmcData.Inventory.items.filter(x => x.parentId === backpack?._id);
|
||||
const baseItemsInRig = pmcData.Inventory.items.filter((x) => x.parentId === rig?._id);
|
||||
const baseItemsInPockets = pmcData.Inventory.items.filter((x) => x.parentId === pockets?._id);
|
||||
const baseItemsInBackpack = pmcData.Inventory.items.filter((x) => x.parentId === backpack?._id);
|
||||
|
||||
return [...baseItemsInRig, ...baseItemsInPockets, ...baseItemsInBackpack];
|
||||
}
|
||||
@ -539,7 +560,7 @@ export class InRaidHelper
|
||||
"pocket1",
|
||||
"pocket2",
|
||||
"pocket3",
|
||||
"pocket4"
|
||||
"pocket4",
|
||||
];
|
||||
|
||||
let inventoryItems: Item[] = [];
|
||||
@ -574,7 +595,7 @@ export class InRaidHelper
|
||||
// Add these new found items to our list of inventory items
|
||||
inventoryItems = [
|
||||
...inventoryItems,
|
||||
...foundItems
|
||||
...foundItems,
|
||||
];
|
||||
|
||||
// Now find the children of these items
|
||||
|
@ -32,11 +32,11 @@ import { JsonUtil } from "@spt-aki/utils/JsonUtil";
|
||||
export interface OwnerInventoryItems
|
||||
{
|
||||
/** Inventory items from source */
|
||||
from: Item[]
|
||||
from: Item[];
|
||||
/** Inventory items at destination */
|
||||
to: Item[]
|
||||
sameInventory: boolean,
|
||||
isMail: boolean
|
||||
to: Item[];
|
||||
sameInventory: boolean;
|
||||
isMail: boolean;
|
||||
}
|
||||
|
||||
@injectable()
|
||||
@ -58,7 +58,7 @@ export class InventoryHelper
|
||||
@inject("ContainerHelper") protected containerHelper: ContainerHelper,
|
||||
@inject("ProfileHelper") protected profileHelper: ProfileHelper,
|
||||
@inject("LocalisationService") protected localisationService: LocalisationService,
|
||||
@inject("ConfigServer") protected configServer: ConfigServer
|
||||
@inject("ConfigServer") protected configServer: ConfigServer,
|
||||
)
|
||||
{
|
||||
this.inventoryConfig = this.configServer.getConfig(ConfigTypes.INVENTORY);
|
||||
@ -76,7 +76,16 @@ export class InventoryHelper
|
||||
* @param useSortingTable Allow items to go into sorting table when stash has no space
|
||||
* @returns IItemEventRouterResponse
|
||||
*/
|
||||
public addItem(pmcData: IPmcData, request: IAddItemRequestData, output: IItemEventRouterResponse, sessionID: string, callback: () => void, foundInRaid = false, addUpd = null, useSortingTable = false): IItemEventRouterResponse
|
||||
public addItem(
|
||||
pmcData: IPmcData,
|
||||
request: IAddItemRequestData,
|
||||
output: IItemEventRouterResponse,
|
||||
sessionID: string,
|
||||
callback: () => void,
|
||||
foundInRaid = false,
|
||||
addUpd = null,
|
||||
useSortingTable = false,
|
||||
): IItemEventRouterResponse
|
||||
{
|
||||
const itemLib: Item[] = []; // TODO: what is the purpose of this property
|
||||
const itemsToAdd: IAddItemTempObject[] = [];
|
||||
@ -85,7 +94,9 @@ export class InventoryHelper
|
||||
{
|
||||
if (requestItem.item_id in this.databaseServer.getTables().globals.ItemPresets)
|
||||
{
|
||||
const presetItems = this.jsonUtil.clone(this.databaseServer.getTables().globals.ItemPresets[requestItem.item_id]._items);
|
||||
const presetItems = this.jsonUtil.clone(
|
||||
this.databaseServer.getTables().globals.ItemPresets[requestItem.item_id]._items,
|
||||
);
|
||||
itemLib.push(...presetItems);
|
||||
requestItem.isPreset = true;
|
||||
requestItem.item_id = presetItems[0]._id;
|
||||
@ -97,7 +108,7 @@ export class InventoryHelper
|
||||
else if (request.tid === Traders.FENCE)
|
||||
{
|
||||
const fenceItems = this.fenceService.getRawFenceAssorts().items;
|
||||
const itemIndex = fenceItems.findIndex(i => i._id === requestItem.item_id);
|
||||
const itemIndex = fenceItems.findIndex((i) => i._id === requestItem.item_id);
|
||||
if (itemIndex === -1)
|
||||
{
|
||||
this.logger.debug(`Tried to buy item ${requestItem.item_id} from fence that no longer exists`);
|
||||
@ -105,7 +116,10 @@ export class InventoryHelper
|
||||
return this.httpResponse.appendErrorToOutput(output, message);
|
||||
}
|
||||
|
||||
const purchasedItemWithChildren = this.itemHelper.findAndReturnChildrenAsItems(fenceItems, requestItem.item_id);
|
||||
const purchasedItemWithChildren = this.itemHelper.findAndReturnChildrenAsItems(
|
||||
fenceItems,
|
||||
requestItem.item_id,
|
||||
);
|
||||
addUpd = purchasedItemWithChildren[0].upd; // Must persist the fence upd properties (e.g. durability/currentHp)
|
||||
itemLib.push(...purchasedItemWithChildren);
|
||||
}
|
||||
@ -118,7 +132,9 @@ export class InventoryHelper
|
||||
// Only grab the relevant trader items and add unique values
|
||||
const traderItems = this.traderAssortHelper.getAssort(sessionID, request.tid).items;
|
||||
const relevantItems = this.itemHelper.findAndReturnChildrenAsItems(traderItems, requestItem.item_id);
|
||||
const toAdd = relevantItems.filter(traderItem => !itemLib.some(item => traderItem._id === item._id)); // what's this
|
||||
const toAdd = relevantItems.filter((traderItem) =>
|
||||
!itemLib.some((item) => traderItem._id === item._id)
|
||||
); // what's this
|
||||
itemLib.push(...toAdd);
|
||||
}
|
||||
|
||||
@ -133,7 +149,15 @@ export class InventoryHelper
|
||||
|
||||
for (const itemToAdd of itemsToAdd)
|
||||
{
|
||||
const errorOutput = this.placeItemInInventory(itemToAdd, stashFS2D, sortingTableFS2D, itemLib, pmcData.Inventory, useSortingTable, output);
|
||||
const errorOutput = this.placeItemInInventory(
|
||||
itemToAdd,
|
||||
stashFS2D,
|
||||
sortingTableFS2D,
|
||||
itemLib,
|
||||
pmcData.Inventory,
|
||||
useSortingTable,
|
||||
output,
|
||||
);
|
||||
if (errorOutput)
|
||||
{
|
||||
return errorOutput;
|
||||
@ -151,9 +175,9 @@ export class InventoryHelper
|
||||
catch (err)
|
||||
{
|
||||
// Callback failed
|
||||
const message = typeof err === "string"
|
||||
? err
|
||||
: this.localisationService.getText("http-unknown_error");
|
||||
const message = typeof err === "string" ?
|
||||
err :
|
||||
this.localisationService.getText("http-unknown_error");
|
||||
|
||||
return this.httpResponse.appendErrorToOutput(output, message);
|
||||
}
|
||||
@ -223,7 +247,7 @@ export class InventoryHelper
|
||||
parentId: itemToAdd.containerId,
|
||||
slotId: "hideout",
|
||||
location: {x: itemToAdd.location.x, y: itemToAdd.location.y, r: itemToAdd.location.rotation ? 1 : 0},
|
||||
upd: this.jsonUtil.clone(upd)
|
||||
upd: this.jsonUtil.clone(upd),
|
||||
});
|
||||
|
||||
pmcData.Inventory.items.push({
|
||||
@ -232,7 +256,7 @@ export class InventoryHelper
|
||||
parentId: itemToAdd.containerId,
|
||||
slotId: "hideout",
|
||||
location: {x: itemToAdd.location.x, y: itemToAdd.location.y, r: itemToAdd.location.rotation ? 1 : 0},
|
||||
upd: this.jsonUtil.clone(upd) // Clone upd to prevent multi-purchases of same item referencing same upd object in memory
|
||||
upd: this.jsonUtil.clone(upd), // Clone upd to prevent multi-purchases of same item referencing same upd object in memory
|
||||
});
|
||||
|
||||
if (this.itemHelper.isOfBaseclass(itemToAdd.itemRef._tpl, BaseClasses.AMMO_BOX))
|
||||
@ -278,8 +302,9 @@ export class InventoryHelper
|
||||
location: {
|
||||
x: itemToAdd.location.x,
|
||||
y: itemToAdd.location.y,
|
||||
r: "Horizontal" },
|
||||
upd: this.jsonUtil.clone(upd)
|
||||
r: "Horizontal",
|
||||
},
|
||||
upd: this.jsonUtil.clone(upd),
|
||||
});
|
||||
|
||||
pmcData.Inventory.items.push({
|
||||
@ -290,8 +315,9 @@ export class InventoryHelper
|
||||
location: {
|
||||
x: itemToAdd.location.x,
|
||||
y: itemToAdd.location.y,
|
||||
r: "Horizontal" },
|
||||
upd: this.jsonUtil.clone(upd)
|
||||
r: "Horizontal",
|
||||
},
|
||||
upd: this.jsonUtil.clone(upd),
|
||||
});
|
||||
}
|
||||
else
|
||||
@ -310,7 +336,7 @@ export class InventoryHelper
|
||||
parentId: toDo[0][1],
|
||||
slotId: slotID,
|
||||
...itemLocation,
|
||||
upd: this.jsonUtil.clone(upd)
|
||||
upd: this.jsonUtil.clone(upd),
|
||||
});
|
||||
|
||||
pmcData.Inventory.items.push({
|
||||
@ -319,7 +345,7 @@ export class InventoryHelper
|
||||
parentId: toDo[0][1],
|
||||
slotId: itemLib[tmpKey].slotId,
|
||||
...itemLocation,
|
||||
upd: this.jsonUtil.clone(upd)
|
||||
upd: this.jsonUtil.clone(upd),
|
||||
});
|
||||
this.logger.debug(`Added ${itemLib[tmpKey]._tpl} with id: ${idForItemToAdd} to inventory`);
|
||||
}
|
||||
@ -346,7 +372,15 @@ export class InventoryHelper
|
||||
* @param output Client output object
|
||||
* @returns Client error output if placing item failed
|
||||
*/
|
||||
protected placeItemInInventory(itemToAdd: IAddItemTempObject, stashFS2D: number[][], sortingTableFS2D: number[][], itemLib: Item[], playerInventory: Inventory, useSortingTable: boolean, output: IItemEventRouterResponse): IItemEventRouterResponse
|
||||
protected placeItemInInventory(
|
||||
itemToAdd: IAddItemTempObject,
|
||||
stashFS2D: number[][],
|
||||
sortingTableFS2D: number[][],
|
||||
itemLib: Item[],
|
||||
playerInventory: Inventory,
|
||||
useSortingTable: boolean,
|
||||
output: IItemEventRouterResponse,
|
||||
): IItemEventRouterResponse
|
||||
{
|
||||
const itemSize = this.getItemSize(itemToAdd.itemRef._tpl, itemToAdd.itemRef._id, itemLib);
|
||||
|
||||
@ -360,16 +394,26 @@ export class InventoryHelper
|
||||
|
||||
try
|
||||
{
|
||||
stashFS2D = this.containerHelper.fillContainerMapWithItem(stashFS2D, findSlotResult.x, findSlotResult.y, itemSizeX, itemSizeY, false); // TODO: rotation not passed in, bad?
|
||||
stashFS2D = this.containerHelper.fillContainerMapWithItem(
|
||||
stashFS2D,
|
||||
findSlotResult.x,
|
||||
findSlotResult.y,
|
||||
itemSizeX,
|
||||
itemSizeY,
|
||||
false,
|
||||
); // TODO: rotation not passed in, bad?
|
||||
}
|
||||
catch (err)
|
||||
{
|
||||
const errorText = typeof err === "string"
|
||||
? ` -> ${err}`
|
||||
: "";
|
||||
const errorText = typeof err === "string" ?
|
||||
` -> ${err}` :
|
||||
"";
|
||||
this.logger.error(this.localisationService.getText("inventory-fill_container_failed", errorText));
|
||||
|
||||
return this.httpResponse.appendErrorToOutput(output, this.localisationService.getText("inventory-no_stash_space"));
|
||||
return this.httpResponse.appendErrorToOutput(
|
||||
output,
|
||||
this.localisationService.getText("inventory-no_stash_space"),
|
||||
);
|
||||
}
|
||||
// Store details for object, incuding container item will be placed in
|
||||
itemToAdd.containerId = playerInventory.stash;
|
||||
@ -377,7 +421,7 @@ export class InventoryHelper
|
||||
x: findSlotResult.x,
|
||||
y: findSlotResult.y,
|
||||
r: findSlotResult.rotation ? 1 : 0,
|
||||
rotation: findSlotResult.rotation
|
||||
rotation: findSlotResult.rotation,
|
||||
};
|
||||
|
||||
// Success! exit
|
||||
@ -387,19 +431,33 @@ export class InventoryHelper
|
||||
// Space not found in main stash, use sorting table
|
||||
if (useSortingTable)
|
||||
{
|
||||
const findSortingSlotResult = this.containerHelper.findSlotForItem(sortingTableFS2D, itemSize[0], itemSize[1]);
|
||||
const findSortingSlotResult = this.containerHelper.findSlotForItem(
|
||||
sortingTableFS2D,
|
||||
itemSize[0],
|
||||
itemSize[1],
|
||||
);
|
||||
const itemSizeX = findSortingSlotResult.rotation ? itemSize[1] : itemSize[0];
|
||||
const itemSizeY = findSortingSlotResult.rotation ? itemSize[0] : itemSize[1];
|
||||
try
|
||||
{
|
||||
sortingTableFS2D = this.containerHelper.fillContainerMapWithItem(sortingTableFS2D, findSortingSlotResult.x, findSortingSlotResult.y, itemSizeX, itemSizeY, false); // TODO: rotation not passed in, bad?
|
||||
sortingTableFS2D = this.containerHelper.fillContainerMapWithItem(
|
||||
sortingTableFS2D,
|
||||
findSortingSlotResult.x,
|
||||
findSortingSlotResult.y,
|
||||
itemSizeX,
|
||||
itemSizeY,
|
||||
false,
|
||||
); // TODO: rotation not passed in, bad?
|
||||
}
|
||||
catch (err)
|
||||
{
|
||||
const errorText = typeof err === "string" ? ` -> ${err}` : "";
|
||||
this.logger.error(this.localisationService.getText("inventory-fill_container_failed", errorText));
|
||||
|
||||
return this.httpResponse.appendErrorToOutput(output, this.localisationService.getText("inventory-no_stash_space"));
|
||||
return this.httpResponse.appendErrorToOutput(
|
||||
output,
|
||||
this.localisationService.getText("inventory-no_stash_space"),
|
||||
);
|
||||
}
|
||||
|
||||
// Store details for object, incuding container item will be placed in
|
||||
@ -408,12 +466,15 @@ export class InventoryHelper
|
||||
x: findSortingSlotResult.x,
|
||||
y: findSortingSlotResult.y,
|
||||
r: findSortingSlotResult.rotation ? 1 : 0,
|
||||
rotation: findSortingSlotResult.rotation
|
||||
rotation: findSortingSlotResult.rotation,
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
return this.httpResponse.appendErrorToOutput(output, this.localisationService.getText("inventory-no_stash_space"));
|
||||
return this.httpResponse.appendErrorToOutput(
|
||||
output,
|
||||
this.localisationService.getText("inventory-no_stash_space"),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -427,7 +488,14 @@ export class InventoryHelper
|
||||
* @param output object to send to client
|
||||
* @param foundInRaid should ammo be FiR
|
||||
*/
|
||||
protected hydrateAmmoBoxWithAmmo(pmcData: IPmcData, itemToAdd: IAddItemTempObject, parentId: string, sessionID: string, output: IItemEventRouterResponse, foundInRaid: boolean): void
|
||||
protected hydrateAmmoBoxWithAmmo(
|
||||
pmcData: IPmcData,
|
||||
itemToAdd: IAddItemTempObject,
|
||||
parentId: string,
|
||||
sessionID: string,
|
||||
output: IItemEventRouterResponse,
|
||||
foundInRaid: boolean,
|
||||
): void
|
||||
{
|
||||
const itemInfo = this.itemHelper.getItem(itemToAdd.itemRef._tpl)[1];
|
||||
const stackSlots = itemInfo._props.StackSlots;
|
||||
@ -450,7 +518,7 @@ export class InventoryHelper
|
||||
parentId: parentId,
|
||||
slotId: "cartridges",
|
||||
location: location,
|
||||
upd: { StackObjectsCount: ammoStackSize }
|
||||
upd: {StackObjectsCount: ammoStackSize},
|
||||
};
|
||||
|
||||
if (foundInRaid)
|
||||
@ -472,7 +540,6 @@ export class InventoryHelper
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param assortItems Items to add to inventory
|
||||
* @param requestItem Details of purchased item to add to inventory
|
||||
* @param result Array split stacks are added to
|
||||
@ -488,18 +555,21 @@ export class InventoryHelper
|
||||
const itemToAdd: IAddItemTempObject = {
|
||||
itemRef: item,
|
||||
count: requestItem.count,
|
||||
isPreset: requestItem.isPreset };
|
||||
isPreset: requestItem.isPreset,
|
||||
};
|
||||
|
||||
// Split stacks if the size is higher than allowed by items StackMaxSize property
|
||||
let maxStackCount = 1;
|
||||
if (requestItem.count > itemDetails._props.StackMaxSize)
|
||||
{
|
||||
let remainingCountOfItemToAdd = requestItem.count;
|
||||
const calc = requestItem.count - (Math.floor(requestItem.count / itemDetails._props.StackMaxSize) * itemDetails._props.StackMaxSize);
|
||||
const calc = requestItem.count -
|
||||
(Math.floor(requestItem.count / itemDetails._props.StackMaxSize) *
|
||||
itemDetails._props.StackMaxSize);
|
||||
|
||||
maxStackCount = (calc > 0)
|
||||
? maxStackCount + Math.floor(remainingCountOfItemToAdd / itemDetails._props.StackMaxSize)
|
||||
: Math.floor(remainingCountOfItemToAdd / itemDetails._props.StackMaxSize);
|
||||
maxStackCount = (calc > 0) ?
|
||||
maxStackCount + Math.floor(remainingCountOfItemToAdd / itemDetails._props.StackMaxSize) :
|
||||
Math.floor(remainingCountOfItemToAdd / itemDetails._props.StackMaxSize);
|
||||
|
||||
// Iterate until totalCountOfPurchasedItem is 0
|
||||
for (let i = 0; i < maxStackCount; i++)
|
||||
@ -542,7 +612,12 @@ export class InventoryHelper
|
||||
* @param output Existing IItemEventRouterResponse object to append data to, creates new one by default if not supplied
|
||||
* @returns IItemEventRouterResponse
|
||||
*/
|
||||
public removeItem(profile: IPmcData, itemId: string, sessionID: string, output: IItemEventRouterResponse = undefined): IItemEventRouterResponse
|
||||
public removeItem(
|
||||
profile: IPmcData,
|
||||
itemId: string,
|
||||
sessionID: string,
|
||||
output: IItemEventRouterResponse = undefined,
|
||||
): IItemEventRouterResponse
|
||||
{
|
||||
if (!itemId)
|
||||
{
|
||||
@ -566,7 +641,7 @@ export class InventoryHelper
|
||||
{
|
||||
// We expect that each inventory item and each insured item has unique "_id", respective "itemId".
|
||||
// Therefore we want to use a NON-Greedy function and escape the iteration as soon as we find requested item.
|
||||
const inventoryIndex = inventoryItems.findIndex(item => item._id === childId);
|
||||
const inventoryIndex = inventoryItems.findIndex((item) => item._id === childId);
|
||||
if (inventoryIndex > -1)
|
||||
{
|
||||
inventoryItems.splice(inventoryIndex, 1);
|
||||
@ -574,10 +649,12 @@ export class InventoryHelper
|
||||
|
||||
if (inventoryIndex === -1)
|
||||
{
|
||||
this.logger.warning(`Unable to remove item with Id: ${childId} as it was not found in inventory ${profile._id}`);
|
||||
this.logger.warning(
|
||||
`Unable to remove item with Id: ${childId} as it was not found in inventory ${profile._id}`,
|
||||
);
|
||||
}
|
||||
|
||||
const insuredIndex = insuredItems.findIndex(item => item.itemId === childId);
|
||||
const insuredIndex = insuredItems.findIndex((item) => item.itemId === childId);
|
||||
if (insuredIndex > -1)
|
||||
{
|
||||
insuredItems.splice(insuredIndex, 1);
|
||||
@ -587,7 +664,11 @@ export class InventoryHelper
|
||||
return output;
|
||||
}
|
||||
|
||||
public removeItemAndChildrenFromMailRewards(sessionId: string, removeRequest: IInventoryRemoveRequestData, output: IItemEventRouterResponse): IItemEventRouterResponse
|
||||
public removeItemAndChildrenFromMailRewards(
|
||||
sessionId: string,
|
||||
removeRequest: IInventoryRemoveRequestData,
|
||||
output: IItemEventRouterResponse,
|
||||
): IItemEventRouterResponse
|
||||
{
|
||||
const fullProfile = this.profileHelper.getFullProfile(sessionId);
|
||||
|
||||
@ -595,18 +676,23 @@ export class InventoryHelper
|
||||
const dialogs = Object.values(fullProfile.dialogues);
|
||||
for (const dialog of dialogs)
|
||||
{
|
||||
const messageWithReward = dialog.messages.find(x => x._id === removeRequest.fromOwner.id);
|
||||
const messageWithReward = dialog.messages.find((x) => x._id === removeRequest.fromOwner.id);
|
||||
if (messageWithReward)
|
||||
{
|
||||
// Find item + any possible children and remove them from mails items array
|
||||
const itemWithChildern = this.itemHelper.findAndReturnChildrenAsItems(messageWithReward.items.data, removeRequest.item);
|
||||
const itemWithChildern = this.itemHelper.findAndReturnChildrenAsItems(
|
||||
messageWithReward.items.data,
|
||||
removeRequest.item,
|
||||
);
|
||||
for (const itemToDelete of itemWithChildern)
|
||||
{
|
||||
// Get index of item to remove from reward array + remove it
|
||||
const indexOfItemToRemove = messageWithReward.items.data.indexOf(itemToDelete);
|
||||
if (indexOfItemToRemove === -1)
|
||||
{
|
||||
this.logger.error(`Unable to remove item: ${removeRequest.item} from mail: ${removeRequest.fromOwner.id} as item could not be found, restart client immediately to prevent data corruption`);
|
||||
this.logger.error(
|
||||
`Unable to remove item: ${removeRequest.item} from mail: ${removeRequest.fromOwner.id} as item could not be found, restart client immediately to prevent data corruption`,
|
||||
);
|
||||
continue;
|
||||
}
|
||||
messageWithReward.items.data.splice(indexOfItemToRemove, 1);
|
||||
@ -622,10 +708,18 @@ export class InventoryHelper
|
||||
return output;
|
||||
}
|
||||
|
||||
public removeItemByCount(pmcData: IPmcData, itemId: string, count: number, sessionID: string, output: IItemEventRouterResponse = undefined): IItemEventRouterResponse
|
||||
public removeItemByCount(
|
||||
pmcData: IPmcData,
|
||||
itemId: string,
|
||||
count: number,
|
||||
sessionID: string,
|
||||
output: IItemEventRouterResponse = undefined,
|
||||
): IItemEventRouterResponse
|
||||
{
|
||||
if (!itemId)
|
||||
{
|
||||
return output;
|
||||
}
|
||||
|
||||
const itemsToReduce = this.itemHelper.findAndReturnChildrenAsItems(pmcData.Inventory.items, itemId);
|
||||
let remainingCount = count;
|
||||
@ -644,12 +738,16 @@ export class InventoryHelper
|
||||
itemToReduce.upd.StackObjectsCount -= remainingCount;
|
||||
remainingCount = 0;
|
||||
if (output)
|
||||
{
|
||||
output.profileChanges[sessionID].items.change.push(itemToReduce);
|
||||
}
|
||||
}
|
||||
|
||||
if (remainingCount === 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
@ -666,7 +764,11 @@ export class InventoryHelper
|
||||
|
||||
// note from 2027: there IS a thing i didn't explore and that is Merges With Children
|
||||
// -> Prepares item Width and height returns [sizeX, sizeY]
|
||||
protected getSizeByInventoryItemHash(itemTpl: string, itemID: string, inventoryItemHash: InventoryHelper.InventoryItemHash): number[]
|
||||
protected getSizeByInventoryItemHash(
|
||||
itemTpl: string,
|
||||
itemID: string,
|
||||
inventoryItemHash: InventoryHelper.InventoryItemHash,
|
||||
): number[]
|
||||
{
|
||||
const toDo = [itemID];
|
||||
const result = this.itemHelper.getItem(itemTpl);
|
||||
@ -681,7 +783,10 @@ export class InventoryHelper
|
||||
// Item found but no _props property
|
||||
if (tmpItem && !tmpItem._props)
|
||||
{
|
||||
this.localisationService.getText("inventory-item_missing_props_property", {itemTpl: itemTpl, itemName: tmpItem?._name});
|
||||
this.localisationService.getText("inventory-item_missing_props_property", {
|
||||
itemTpl: itemTpl,
|
||||
itemName: tmpItem?._name,
|
||||
});
|
||||
}
|
||||
|
||||
// No item object or getItem() returned false
|
||||
@ -711,7 +816,7 @@ export class InventoryHelper
|
||||
const skipThisItems: string[] = [
|
||||
BaseClasses.BACKPACK,
|
||||
BaseClasses.SEARCHABLE_ITEM,
|
||||
BaseClasses.SIMPLE_CONTAINER
|
||||
BaseClasses.SIMPLE_CONTAINER,
|
||||
];
|
||||
const rootFolded = rootItem.upd?.Foldable && rootItem.upd.Foldable.Folded === true;
|
||||
|
||||
@ -741,7 +846,12 @@ export class InventoryHelper
|
||||
const itemResult = this.itemHelper.getItem(item._tpl);
|
||||
if (!itemResult[0])
|
||||
{
|
||||
this.logger.error(this.localisationService.getText("inventory-get_item_size_item_not_found_by_tpl", item._tpl));
|
||||
this.logger.error(
|
||||
this.localisationService.getText(
|
||||
"inventory-get_item_size_item_not_found_by_tpl",
|
||||
item._tpl,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
const itm = itemResult[1];
|
||||
@ -781,7 +891,7 @@ export class InventoryHelper
|
||||
|
||||
return [
|
||||
outX + sizeLeft + sizeRight + forcedLeft + forcedRight,
|
||||
outY + sizeUp + sizeDown + forcedUp + forcedDown
|
||||
outY + sizeUp + sizeDown + forcedUp + forcedDown,
|
||||
];
|
||||
}
|
||||
|
||||
@ -789,7 +899,7 @@ export class InventoryHelper
|
||||
{
|
||||
const inventoryItemHash: InventoryHelper.InventoryItemHash = {
|
||||
byItemId: {},
|
||||
byParentId: {}
|
||||
byParentId: {},
|
||||
};
|
||||
|
||||
for (const item of inventoryItem)
|
||||
@ -832,8 +942,14 @@ export class InventoryHelper
|
||||
const tmpSize = this.getSizeByInventoryItemHash(item._tpl, item._id, inventoryItemHash);
|
||||
const iW = tmpSize[0]; // x
|
||||
const iH = tmpSize[1]; // y
|
||||
const fH = (((item.location as Location).r === 1 || (item.location as Location).r === "Vertical" || (item.location as Location).rotation === "Vertical") ? iW : iH);
|
||||
const fW = (((item.location as Location).r === 1 || (item.location as Location).r === "Vertical" || (item.location as Location).rotation === "Vertical") ? iH : iW);
|
||||
const fH = ((item.location as Location).r === 1 || (item.location as Location).r === "Vertical" ||
|
||||
(item.location as Location).rotation === "Vertical") ?
|
||||
iW :
|
||||
iH;
|
||||
const fW = ((item.location as Location).r === 1 || (item.location as Location).r === "Vertical" ||
|
||||
(item.location as Location).rotation === "Vertical") ?
|
||||
iH :
|
||||
iW;
|
||||
const fillTo = (item.location as Location).x + fW;
|
||||
|
||||
for (let y = 0; y < fH; y++)
|
||||
@ -844,7 +960,12 @@ export class InventoryHelper
|
||||
}
|
||||
catch (e)
|
||||
{
|
||||
this.logger.error(this.localisationService.getText("inventory-unable_to_fill_container", {id: item._id, error: e}));
|
||||
this.logger.error(
|
||||
this.localisationService.getText("inventory-unable_to_fill_container", {
|
||||
id: item._id,
|
||||
error: e,
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -860,7 +981,10 @@ export class InventoryHelper
|
||||
* @param sessionId Session id / playerid
|
||||
* @returns OwnerInventoryItems with inventory of player/scav to adjust
|
||||
*/
|
||||
public getOwnerInventoryItems(request: IInventoryMoveRequestData | IInventorySplitRequestData | IInventoryMergeRequestData, sessionId: string): OwnerInventoryItems
|
||||
public getOwnerInventoryItems(
|
||||
request: IInventoryMoveRequestData | IInventorySplitRequestData | IInventoryMergeRequestData,
|
||||
sessionId: string,
|
||||
): OwnerInventoryItems
|
||||
{
|
||||
let isSameInventory = false;
|
||||
const pmcItems = this.profileHelper.getPmcProfile(sessionId).Inventory.items;
|
||||
@ -878,9 +1002,9 @@ export class InventoryHelper
|
||||
else if (request.fromOwner.type.toLocaleLowerCase() === "mail")
|
||||
{
|
||||
// Split requests dont use 'use' but 'splitItem' property
|
||||
const item = "splitItem" in request
|
||||
? request.splitItem
|
||||
: request.item;
|
||||
const item = "splitItem" in request ?
|
||||
request.splitItem :
|
||||
request.item;
|
||||
fromInventoryItems = this.dialogueHelper.getMessageItemContents(request.fromOwner.id, sessionId, item);
|
||||
fromType = "mail";
|
||||
}
|
||||
@ -908,7 +1032,7 @@ export class InventoryHelper
|
||||
from: fromInventoryItems,
|
||||
to: toInventoryItems,
|
||||
sameInventory: isSameInventory,
|
||||
isMail: fromType === "mail"
|
||||
isMail: fromType === "mail",
|
||||
};
|
||||
}
|
||||
|
||||
@ -921,7 +1045,12 @@ export class InventoryHelper
|
||||
protected getStashSlotMap(pmcData: IPmcData, sessionID: string): number[][]
|
||||
{
|
||||
const playerStashSize = this.getPlayerStashSize(sessionID);
|
||||
return this.getContainerMap(playerStashSize[0], playerStashSize[1], pmcData.Inventory.items, pmcData.Inventory.stash);
|
||||
return this.getContainerMap(
|
||||
playerStashSize[0],
|
||||
playerStashSize[1],
|
||||
pmcData.Inventory.items,
|
||||
pmcData.Inventory.stash,
|
||||
);
|
||||
}
|
||||
|
||||
protected getSortingTableSlotMap(pmcData: IPmcData): number[][]
|
||||
@ -948,12 +1077,12 @@ export class InventoryHelper
|
||||
this.logger.error(this.localisationService.getText("inventory-stash_not_found", stashTPL));
|
||||
}
|
||||
|
||||
const stashX = stashItemDetails[1]._props.Grids[0]._props.cellsH !== 0
|
||||
? stashItemDetails[1]._props.Grids[0]._props.cellsH
|
||||
: 10;
|
||||
const stashY = stashItemDetails[1]._props.Grids[0]._props.cellsV !== 0
|
||||
? stashItemDetails[1]._props.Grids[0]._props.cellsV
|
||||
: 66;
|
||||
const stashX = stashItemDetails[1]._props.Grids[0]._props.cellsH !== 0 ?
|
||||
stashItemDetails[1]._props.Grids[0]._props.cellsH :
|
||||
10;
|
||||
const stashY = stashItemDetails[1]._props.Grids[0]._props.cellsV !== 0 ?
|
||||
stashItemDetails[1]._props.Grids[0]._props.cellsV :
|
||||
66;
|
||||
return [stashX, stashY];
|
||||
}
|
||||
|
||||
@ -965,7 +1094,7 @@ export class InventoryHelper
|
||||
protected getStashType(sessionID: string): string
|
||||
{
|
||||
const pmcData = this.profileHelper.getPmcProfile(sessionID);
|
||||
const stashObj = pmcData.Inventory.items.find(item => item._id === pmcData.Inventory.stash);
|
||||
const stashObj = pmcData.Inventory.items.find((item) => item._id === pmcData.Inventory.stash);
|
||||
if (!stashObj)
|
||||
{
|
||||
this.logger.error(this.localisationService.getText("inventory-unable_to_find_stash"));
|
||||
@ -987,7 +1116,7 @@ export class InventoryHelper
|
||||
const idsToMove = this.itemHelper.findAndReturnChildrenByItems(fromItems, body.item);
|
||||
for (const itemId of idsToMove)
|
||||
{
|
||||
const itemToMove = fromItems.find(x => x._id === itemId);
|
||||
const itemToMove = fromItems.find((x) => x._id === itemId);
|
||||
if (!itemToMove)
|
||||
{
|
||||
this.logger.error(`Unable to find item to move: ${itemId}`);
|
||||
@ -1026,12 +1155,16 @@ export class InventoryHelper
|
||||
* @param moveRequest
|
||||
* @returns True if move was successful
|
||||
*/
|
||||
public moveItemInternal(pmcData: IPmcData, inventoryItems: Item[], moveRequest: IInventoryMoveRequestData): {success: boolean, errorMessage?: string}
|
||||
public moveItemInternal(
|
||||
pmcData: IPmcData,
|
||||
inventoryItems: Item[],
|
||||
moveRequest: IInventoryMoveRequestData,
|
||||
): {success: boolean; errorMessage?: string;}
|
||||
{
|
||||
this.handleCartridges(inventoryItems, moveRequest);
|
||||
|
||||
// Find item we want to 'move'
|
||||
const matchingInventoryItem = inventoryItems.find(x => x._id === moveRequest.item);
|
||||
const matchingInventoryItem = inventoryItems.find((x) => x._id === moveRequest.item);
|
||||
if (!matchingInventoryItem)
|
||||
{
|
||||
const errorMesage = `Unable to move item: ${moveRequest.item}, cannot find in inventory`;
|
||||
@ -1040,12 +1173,19 @@ export class InventoryHelper
|
||||
return {success: false, errorMessage: errorMesage};
|
||||
}
|
||||
|
||||
this.logger.debug(`${moveRequest.Action} item: ${moveRequest.item} from slotid: ${matchingInventoryItem.slotId} to container: ${moveRequest.to.container}`);
|
||||
this.logger.debug(
|
||||
`${moveRequest.Action} item: ${moveRequest.item} from slotid: ${matchingInventoryItem.slotId} to container: ${moveRequest.to.container}`,
|
||||
);
|
||||
|
||||
// don't move shells from camora to cartridges (happens when loading shells into mts-255 revolver shotgun)
|
||||
if (matchingInventoryItem.slotId.includes("camora_") && moveRequest.to.container === "cartridges")
|
||||
{
|
||||
this.logger.warning(this.localisationService.getText("inventory-invalid_move_to_container", {slotId: matchingInventoryItem.slotId, container: moveRequest.to.container}));
|
||||
this.logger.warning(
|
||||
this.localisationService.getText("inventory-invalid_move_to_container", {
|
||||
slotId: matchingInventoryItem.slotId,
|
||||
container: moveRequest.to.container,
|
||||
}),
|
||||
);
|
||||
|
||||
return {success: true};
|
||||
}
|
||||
@ -1059,7 +1199,6 @@ export class InventoryHelper
|
||||
if ("location" in moveRequest.to)
|
||||
{
|
||||
matchingInventoryItem.location = moveRequest.to.location;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -1085,7 +1224,7 @@ export class InventoryHelper
|
||||
if (pmcData.Inventory.fastPanel[itemKey] === itemBeingMoved._id)
|
||||
{
|
||||
// Get moved items parent
|
||||
const itemParent = pmcData.Inventory.items.find(x => x._id === itemBeingMoved.parentId);
|
||||
const itemParent = pmcData.Inventory.items.find((x) => x._id === itemBeingMoved.parentId);
|
||||
|
||||
// Empty out id if item is moved to a container other than pocket/rig
|
||||
if (itemParent && !(itemParent.slotId?.startsWith("Pockets") || itemParent.slotId === "TacticalVest"))
|
||||
@ -1140,7 +1279,7 @@ namespace InventoryHelper
|
||||
{
|
||||
export interface InventoryItemHash
|
||||
{
|
||||
byItemId: Record<string, Item>
|
||||
byParentId: Record<string, Item[]>
|
||||
byItemId: Record<string, Item>;
|
||||
byParentId: Record<string, Item[]>;
|
||||
}
|
||||
}
|
@ -29,7 +29,7 @@ class ItemHelper
|
||||
BaseClasses.SORTING_TABLE,
|
||||
BaseClasses.INVENTORY,
|
||||
BaseClasses.STATIONARY_CONTAINER,
|
||||
BaseClasses.POCKETS
|
||||
BaseClasses.POCKETS,
|
||||
];
|
||||
|
||||
constructor(
|
||||
@ -44,7 +44,7 @@ class ItemHelper
|
||||
@inject("ItemBaseClassService") protected itemBaseClassService: ItemBaseClassService,
|
||||
@inject("ItemFilterService") protected itemFilterService: ItemFilterService,
|
||||
@inject("LocalisationService") protected localisationService: LocalisationService,
|
||||
@inject("LocaleService") protected localeService: LocaleService
|
||||
@inject("LocaleService") protected localeService: LocaleService,
|
||||
)
|
||||
{}
|
||||
|
||||
@ -68,11 +68,11 @@ class ItemHelper
|
||||
}
|
||||
|
||||
// Is item valid
|
||||
return !itemDetails[1]._props.QuestItem
|
||||
&& itemDetails[1]._type === "Item"
|
||||
&& invalidBaseTypes.every(x => !this.isOfBaseclass(tpl, x))
|
||||
&& this.getItemPrice(tpl) > 0
|
||||
&& !this.itemFilterService.isItemBlacklisted(tpl);
|
||||
return !itemDetails[1]._props.QuestItem &&
|
||||
itemDetails[1]._type === "Item" &&
|
||||
invalidBaseTypes.every((x) => !this.isOfBaseclass(tpl, x)) &&
|
||||
this.getItemPrice(tpl) > 0 &&
|
||||
!this.itemFilterService.isItemBlacklisted(tpl);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -177,7 +177,7 @@ class ItemHelper
|
||||
if (item.upd === undefined)
|
||||
{
|
||||
item.upd = {
|
||||
StackObjectsCount: 1
|
||||
StackObjectsCount: 1,
|
||||
};
|
||||
}
|
||||
|
||||
@ -244,8 +244,8 @@ class ItemHelper
|
||||
slotId: slotId,
|
||||
location: 0,
|
||||
upd: {
|
||||
StackObjectsCount: count
|
||||
}
|
||||
StackObjectsCount: count,
|
||||
},
|
||||
};
|
||||
stackSlotItems.push(stackSlotItem);
|
||||
}
|
||||
@ -352,7 +352,6 @@ class ItemHelper
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get a quality value based on a repairable items (weapon/armor) current state between current and max durability
|
||||
* @param itemDetails Db details for item we want quality value for
|
||||
@ -365,7 +364,9 @@ class ItemHelper
|
||||
// Edge case, max durability is below durability
|
||||
if (repairable.Durability > repairable.MaxDurability)
|
||||
{
|
||||
this.logger.warning(`Max durability: ${repairable.MaxDurability} for item id: ${item._id} was below Durability: ${repairable.Durability}, adjusting values to match`);
|
||||
this.logger.warning(
|
||||
`Max durability: ${repairable.MaxDurability} for item id: ${item._id} was below Durability: ${repairable.Durability}, adjusting values to match`,
|
||||
);
|
||||
repairable.MaxDurability = repairable.Durability;
|
||||
}
|
||||
|
||||
@ -377,9 +378,9 @@ class ItemHelper
|
||||
|
||||
// Weapon
|
||||
// Get max dura from props, if it isnt there use repairable max dura value
|
||||
const maxDurability = (itemDetails._props.MaxDurability)
|
||||
? itemDetails._props.MaxDurability
|
||||
: repairable.MaxDurability;
|
||||
const maxDurability = (itemDetails._props.MaxDurability) ?
|
||||
itemDetails._props.MaxDurability :
|
||||
repairable.MaxDurability;
|
||||
const durability = repairable.Durability / maxDurability;
|
||||
|
||||
if (!durability)
|
||||
@ -434,7 +435,7 @@ class ItemHelper
|
||||
continue;
|
||||
}
|
||||
|
||||
if (childItem.parentId === baseItemId && !list.find(item => childItem._id === item._id))
|
||||
if (childItem.parentId === baseItemId && !list.find((item) => childItem._id === item._id))
|
||||
{
|
||||
list.push(...this.findAndReturnChildrenAsItems(items, childItem._id));
|
||||
}
|
||||
@ -455,7 +456,7 @@ class ItemHelper
|
||||
|
||||
for (const itemFromAssort of assort)
|
||||
{
|
||||
if (itemFromAssort.parentId === itemIdToFind && !list.find(item => itemFromAssort._id === item._id))
|
||||
if (itemFromAssort.parentId === itemIdToFind && !list.find((item) => itemFromAssort._id === item._id))
|
||||
{
|
||||
list.push(itemFromAssort);
|
||||
list = list.concat(this.findAndReturnChildrenByAssort(itemFromAssort._id, assort));
|
||||
@ -472,8 +473,10 @@ class ItemHelper
|
||||
*/
|
||||
public hasBuyRestrictions(itemToCheck: Item): boolean
|
||||
{
|
||||
if (itemToCheck.upd?.BuyRestrictionCurrent !== undefined
|
||||
&& itemToCheck.upd?.BuyRestrictionMax !== undefined)
|
||||
if (
|
||||
itemToCheck.upd?.BuyRestrictionCurrent !== undefined &&
|
||||
itemToCheck.upd?.BuyRestrictionMax !== undefined
|
||||
)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@ -571,18 +574,18 @@ class ItemHelper
|
||||
public findBarterItems(by: "tpl" | "id", items: Item[], barterItemId: string): Item[]
|
||||
{
|
||||
// find required items to take after buying (handles multiple items)
|
||||
const barterIDs = typeof barterItemId === "string"
|
||||
? [barterItemId]
|
||||
: barterItemId;
|
||||
const barterIDs = typeof barterItemId === "string" ?
|
||||
[barterItemId] :
|
||||
barterItemId;
|
||||
|
||||
let barterItems: Item[] = [];
|
||||
for (const barterID of barterIDs)
|
||||
{
|
||||
const filterResult = items.filter(item =>
|
||||
const filterResult = items.filter((item) =>
|
||||
{
|
||||
return by === "tpl"
|
||||
? (item._tpl === barterID)
|
||||
: (item._id === barterID);
|
||||
return by === "tpl" ?
|
||||
(item._tpl === barterID) :
|
||||
(item._id === barterID);
|
||||
});
|
||||
|
||||
barterItems = Object.assign(barterItems, filterResult);
|
||||
@ -615,17 +618,19 @@ class ItemHelper
|
||||
{
|
||||
// Insured items shouldn't be renamed
|
||||
// only works for pmcs.
|
||||
if (insuredItems?.find(insuredItem => insuredItem.itemId === item._id))
|
||||
if (insuredItems?.find((insuredItem) => insuredItem.itemId === item._id))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Do not replace important ID's
|
||||
if (item._id === pmcData.Inventory.equipment
|
||||
|| item._id === pmcData.Inventory.questRaidItems
|
||||
|| item._id === pmcData.Inventory.questStashItems
|
||||
|| item._id === pmcData.Inventory.sortingTable
|
||||
|| item._id === pmcData.Inventory.stash)
|
||||
if (
|
||||
item._id === pmcData.Inventory.equipment ||
|
||||
item._id === pmcData.Inventory.questRaidItems ||
|
||||
item._id === pmcData.Inventory.questStashItems ||
|
||||
item._id === pmcData.Inventory.sortingTable ||
|
||||
item._id === pmcData.Inventory.stash
|
||||
)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@ -804,7 +809,9 @@ class ItemHelper
|
||||
let isRequiredSlot = false;
|
||||
if (parentTemplate[0] && parentTemplate[1]?._props?.Slots)
|
||||
{
|
||||
isRequiredSlot = parentTemplate[1]._props.Slots.some(slot => slot._name === item.slotId && slot._required);
|
||||
isRequiredSlot = parentTemplate[1]._props.Slots.some((slot) =>
|
||||
slot._name === item.slotId && slot._required
|
||||
);
|
||||
}
|
||||
|
||||
return itemTemplate[0] && parentTemplate[0] && !(isNotRaidModdable || isRequiredSlot);
|
||||
@ -865,7 +872,7 @@ class ItemHelper
|
||||
*/
|
||||
public getItemSize(items: Item[], rootItemId: string): ItemHelper.ItemSize
|
||||
{
|
||||
const rootTemplate = this.getItem(items.filter(x => x._id === rootItemId)[0]._tpl)[1];
|
||||
const rootTemplate = this.getItem(items.filter((x) => x._id === rootItemId)[0]._tpl)[1];
|
||||
const width = rootTemplate._props.Width;
|
||||
const height = rootTemplate._props.Height;
|
||||
|
||||
@ -897,13 +904,15 @@ class ItemHelper
|
||||
sizeUp = sizeUp < itemTemplate._props.ExtraSizeUp ? itemTemplate._props.ExtraSizeUp : sizeUp;
|
||||
sizeDown = sizeDown < itemTemplate._props.ExtraSizeDown ? itemTemplate._props.ExtraSizeDown : sizeDown;
|
||||
sizeLeft = sizeLeft < itemTemplate._props.ExtraSizeLeft ? itemTemplate._props.ExtraSizeLeft : sizeLeft;
|
||||
sizeRight = sizeRight < itemTemplate._props.ExtraSizeRight ? itemTemplate._props.ExtraSizeRight : sizeRight;
|
||||
sizeRight = sizeRight < itemTemplate._props.ExtraSizeRight ?
|
||||
itemTemplate._props.ExtraSizeRight :
|
||||
sizeRight;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
width: width + sizeLeft + sizeRight + forcedLeft + forcedRight,
|
||||
height: height + sizeUp + sizeDown + forcedUp + forcedDown
|
||||
height: height + sizeUp + sizeDown + forcedUp + forcedDown,
|
||||
};
|
||||
}
|
||||
|
||||
@ -945,9 +954,9 @@ class ItemHelper
|
||||
while (currentStoredCartridgeCount < ammoBoxMaxCartridgeCount)
|
||||
{
|
||||
const remainingSpace = ammoBoxMaxCartridgeCount - currentStoredCartridgeCount;
|
||||
const cartridgeCountToAdd = (remainingSpace < maxPerStack)
|
||||
? remainingSpace
|
||||
: maxPerStack;
|
||||
const cartridgeCountToAdd = (remainingSpace < maxPerStack) ?
|
||||
remainingSpace :
|
||||
maxPerStack;
|
||||
|
||||
// Add cartridge item into items array
|
||||
ammoBox.push(this.createCartridges(ammoBox[0]._id, cartridgeTpl, cartridgeCountToAdd, location));
|
||||
@ -967,7 +976,7 @@ class ItemHelper
|
||||
public itemIsInsideContainer(item: Item, desiredContainerSlotId: string, items: Item[]): boolean
|
||||
{
|
||||
// Get items parent
|
||||
const parent = items.find(x => x._id === item.parentId);
|
||||
const parent = items.find((x) => x._id === item.parentId);
|
||||
if (!parent)
|
||||
{
|
||||
// No parent, end of line, not inside container
|
||||
@ -997,7 +1006,7 @@ class ItemHelper
|
||||
magTemplate: ITemplateItem,
|
||||
staticAmmoDist: Record<string, IStaticAmmoDetails[]>,
|
||||
caliber: string = undefined,
|
||||
minSizePercent = 0.25
|
||||
minSizePercent = 0.25,
|
||||
): void
|
||||
{
|
||||
// no caliber defined, choose one at random
|
||||
@ -1028,7 +1037,7 @@ class ItemHelper
|
||||
magazine: Item[],
|
||||
magTemplate: ITemplateItem,
|
||||
cartridgeTpl: string,
|
||||
minSizePercent = 0.25
|
||||
minSizePercent = 0.25,
|
||||
): void
|
||||
{
|
||||
// Get cartrdge properties and max allowed stack size
|
||||
@ -1037,7 +1046,10 @@ class ItemHelper
|
||||
|
||||
// Get max number of cartridges in magazine, choose random value between min/max
|
||||
const magazineCartridgeMaxCount = magTemplate._props.Cartridges[0]._max_count;
|
||||
const desiredStackCount = this.randomUtil.getInt(Math.round(minSizePercent * magazineCartridgeMaxCount), magazineCartridgeMaxCount);
|
||||
const desiredStackCount = this.randomUtil.getInt(
|
||||
Math.round(minSizePercent * magazineCartridgeMaxCount),
|
||||
magazineCartridgeMaxCount,
|
||||
);
|
||||
|
||||
// Loop over cartridge count and add stacks to magazine
|
||||
let currentStoredCartridgeCount = 0;
|
||||
@ -1045,9 +1057,9 @@ class ItemHelper
|
||||
while (currentStoredCartridgeCount < desiredStackCount)
|
||||
{
|
||||
// Get stack size of cartridges
|
||||
let cartridgeCountToAdd = (desiredStackCount <= cartridgeMaxStackSize)
|
||||
? desiredStackCount
|
||||
: cartridgeMaxStackSize;
|
||||
let cartridgeCountToAdd = (desiredStackCount <= cartridgeMaxStackSize) ?
|
||||
desiredStackCount :
|
||||
cartridgeMaxStackSize;
|
||||
|
||||
// Ensure we don't go over the max stackcount size
|
||||
const remainingSpace = desiredStackCount - currentStoredCartridgeCount;
|
||||
@ -1075,11 +1087,11 @@ class ItemHelper
|
||||
const calibers = [
|
||||
...new Set(
|
||||
ammoTpls.filter(
|
||||
(x: string) => this.getItem(x)[0]
|
||||
(x: string) => this.getItem(x)[0],
|
||||
).map(
|
||||
(x: string) => this.getItem(x)[1]._props.Caliber
|
||||
)
|
||||
)
|
||||
(x: string) => this.getItem(x)[1]._props.Caliber,
|
||||
),
|
||||
),
|
||||
];
|
||||
return this.randomUtil.drawRandomFromList(calibers)[0];
|
||||
}
|
||||
@ -1096,7 +1108,7 @@ class ItemHelper
|
||||
for (const icd of staticAmmoDist[caliber])
|
||||
{
|
||||
ammoArray.push(
|
||||
new ProbabilityObject(icd.tpl, icd.relativeProbability)
|
||||
new ProbabilityObject(icd.tpl, icd.relativeProbability),
|
||||
);
|
||||
}
|
||||
return ammoArray.draw(1)[0];
|
||||
@ -1118,7 +1130,7 @@ class ItemHelper
|
||||
parentId: parentId,
|
||||
slotId: "cartridges",
|
||||
location: location,
|
||||
upd: { StackObjectsCount: stackCount }
|
||||
upd: {StackObjectsCount: stackCount},
|
||||
};
|
||||
}
|
||||
|
||||
@ -1149,7 +1161,9 @@ class ItemHelper
|
||||
|
||||
public getItemTplsOfBaseType(desiredBaseType: string): string[]
|
||||
{
|
||||
return Object.values(this.databaseServer.getTables().templates.items).filter(x => x._parent === desiredBaseType).map(x =>x._id);
|
||||
return Object.values(this.databaseServer.getTables().templates.items).filter((x) =>
|
||||
x._parent === desiredBaseType
|
||||
).map((x) => x._id);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1157,10 +1171,9 @@ namespace ItemHelper
|
||||
{
|
||||
export interface ItemSize
|
||||
{
|
||||
width: number
|
||||
height: number
|
||||
width: number;
|
||||
height: number;
|
||||
}
|
||||
}
|
||||
|
||||
export {ItemHelper};
|
||||
|
||||
|
@ -16,7 +16,7 @@ export class NotificationSendHelper
|
||||
@inject("WebSocketServer") protected webSocketServer: WebSocketServer,
|
||||
@inject("HashUtil") protected hashUtil: HashUtil,
|
||||
@inject("SaveServer") protected saveServer: SaveServer,
|
||||
@inject("NotificationService") protected notificationService: NotificationService
|
||||
@inject("NotificationService") protected notificationService: NotificationService,
|
||||
)
|
||||
{}
|
||||
|
||||
@ -44,7 +44,12 @@ export class NotificationSendHelper
|
||||
* @param messageText Text to send player
|
||||
* @param messageType Underlying type of message being sent
|
||||
*/
|
||||
public sendMessageToPlayer(sessionId: string, senderDetails: IUserDialogInfo, messageText: string, messageType: MessageType): void
|
||||
public sendMessageToPlayer(
|
||||
sessionId: string,
|
||||
senderDetails: IUserDialogInfo,
|
||||
messageText: string,
|
||||
messageType: MessageType,
|
||||
): void
|
||||
{
|
||||
const dialog = this.getDialog(sessionId, messageType, senderDetails);
|
||||
|
||||
@ -57,7 +62,7 @@ export class NotificationSendHelper
|
||||
text: messageText,
|
||||
hasRewards: undefined,
|
||||
rewardCollected: undefined,
|
||||
items: undefined
|
||||
items: undefined,
|
||||
};
|
||||
dialog.messages.push(message);
|
||||
|
||||
@ -65,7 +70,7 @@ export class NotificationSendHelper
|
||||
type: NotificationType.NEW_MESSAGE,
|
||||
eventId: message._id,
|
||||
dialogId: message.uid,
|
||||
message: message
|
||||
message: message,
|
||||
};
|
||||
this.sendMessage(sessionId, notification);
|
||||
}
|
||||
@ -80,7 +85,9 @@ export class NotificationSendHelper
|
||||
protected getDialog(sessionId: string, messageType: MessageType, senderDetails: IUserDialogInfo): Dialogue
|
||||
{
|
||||
// Use trader id if sender is trader, otherwise use nickname
|
||||
const key = (senderDetails.info.MemberCategory === MemberCategory.TRADER) ? senderDetails._id : senderDetails.info.Nickname;
|
||||
const key = (senderDetails.info.MemberCategory === MemberCategory.TRADER) ?
|
||||
senderDetails._id :
|
||||
senderDetails.info.Nickname;
|
||||
const dialogueData = this.saveServer.getProfile(sessionId).dialogues;
|
||||
const isNewDialogue = !(key in dialogueData);
|
||||
let dialogue: Dialogue = dialogueData[key];
|
||||
@ -95,7 +102,7 @@ export class NotificationSendHelper
|
||||
pinned: false,
|
||||
new: 0,
|
||||
attachmentsNew: 0,
|
||||
Users: (senderDetails.info.MemberCategory === MemberCategory.TRADER) ? undefined : [senderDetails]
|
||||
Users: (senderDetails.info.MemberCategory === MemberCategory.TRADER) ? undefined : [senderDetails],
|
||||
};
|
||||
|
||||
dialogueData[key] = dialogue;
|
||||
|
@ -12,11 +12,11 @@ export class NotifierHelper
|
||||
*/
|
||||
protected defaultNotification: INotification = {
|
||||
type: NotificationType.PING,
|
||||
eventId: "ping"
|
||||
eventId: "ping",
|
||||
};
|
||||
|
||||
constructor(
|
||||
@inject("HttpServerHelper") protected httpServerHelper: HttpServerHelper
|
||||
@inject("HttpServerHelper") protected httpServerHelper: HttpServerHelper,
|
||||
)
|
||||
{}
|
||||
|
||||
@ -31,13 +31,16 @@ export class NotifierHelper
|
||||
* @param ragfairData Ragfair data to attach to notification
|
||||
* @returns
|
||||
*/
|
||||
public createRagfairOfferSoldNotification(dialogueMessage: Message, ragfairData: MessageContentRagfair): INotification
|
||||
public createRagfairOfferSoldNotification(
|
||||
dialogueMessage: Message,
|
||||
ragfairData: MessageContentRagfair,
|
||||
): INotification
|
||||
{
|
||||
return {
|
||||
type: NotificationType.RAGFAIR_OFFER_SOLD,
|
||||
eventId: dialogueMessage._id,
|
||||
dialogId: dialogueMessage.uid,
|
||||
...ragfairData
|
||||
...ragfairData,
|
||||
};
|
||||
}
|
||||
|
||||
@ -52,7 +55,7 @@ export class NotifierHelper
|
||||
type: NotificationType.NEW_MESSAGE,
|
||||
eventId: dialogueMessage._id,
|
||||
dialogId: dialogueMessage.uid,
|
||||
message: dialogueMessage
|
||||
message: dialogueMessage,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -11,7 +11,7 @@ export class PaymentHelper
|
||||
protected inventoryConfig: IInventoryConfig;
|
||||
|
||||
constructor(
|
||||
@inject("ConfigServer") protected configServer: ConfigServer
|
||||
@inject("ConfigServer") protected configServer: ConfigServer,
|
||||
)
|
||||
{
|
||||
this.inventoryConfig = this.configServer.getConfig(ConfigTypes.INVENTORY);
|
||||
@ -24,7 +24,9 @@ export class PaymentHelper
|
||||
*/
|
||||
public isMoneyTpl(tpl: string): boolean
|
||||
{
|
||||
return [Money.DOLLARS, Money.EUROS, Money.ROUBLES, ...this.inventoryConfig.customMoneyTpls].some(element => element === tpl);
|
||||
return [Money.DOLLARS, Money.EUROS, Money.ROUBLES, ...this.inventoryConfig.customMoneyTpls].some((element) =>
|
||||
element === tpl
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -12,7 +12,7 @@ export class PresetHelper
|
||||
|
||||
constructor(
|
||||
@inject("JsonUtil") protected jsonUtil: JsonUtil,
|
||||
@inject("DatabaseServer") protected databaseServer: DatabaseServer
|
||||
@inject("DatabaseServer") protected databaseServer: DatabaseServer,
|
||||
)
|
||||
{}
|
||||
|
||||
@ -26,7 +26,7 @@ export class PresetHelper
|
||||
if (!this.defaultPresets)
|
||||
{
|
||||
this.defaultPresets = Object.values(this.databaseServer.getTables().globals.ItemPresets)
|
||||
.filter(x => x._encyclopedia !== undefined)
|
||||
.filter((x) => x._encyclopedia !== undefined)
|
||||
.reduce((acc, cur) =>
|
||||
{
|
||||
acc[cur._id] = cur;
|
||||
|
@ -8,7 +8,7 @@ export class ProbabilityHelper
|
||||
{
|
||||
constructor(
|
||||
@inject("WinstonLogger") protected logger: ILogger,
|
||||
@inject("RandomUtil") protected randomUtil: RandomUtil
|
||||
@inject("RandomUtil") protected randomUtil: RandomUtil,
|
||||
)
|
||||
{}
|
||||
|
||||
|
@ -27,7 +27,7 @@ export class ProfileHelper
|
||||
@inject("DatabaseServer") protected databaseServer: DatabaseServer,
|
||||
@inject("ItemHelper") protected itemHelper: ItemHelper,
|
||||
@inject("ProfileSnapshotService") protected profileSnapshotService: ProfileSnapshotService,
|
||||
@inject("LocalisationService") protected localisationService: LocalisationService
|
||||
@inject("LocalisationService") protected localisationService: LocalisationService,
|
||||
)
|
||||
{}
|
||||
|
||||
@ -41,7 +41,7 @@ export class ProfileHelper
|
||||
for (const questId in questConditionId)
|
||||
{
|
||||
const conditionId = questConditionId[questId];
|
||||
const profileQuest = pmcData.Quests.find(x => x.qid === questId);
|
||||
const profileQuest = pmcData.Quests.find((x) => x.qid === questId);
|
||||
|
||||
// Find index of condition in array
|
||||
const index = profileQuest.completedConditions.indexOf(conditionId);
|
||||
@ -97,7 +97,12 @@ export class ProfileHelper
|
||||
* @param scavProfile post-raid scav profile
|
||||
* @returns updated profile array
|
||||
*/
|
||||
protected postRaidXpWorkaroundFix(sessionId: string, output: IPmcData[], pmcProfile: IPmcData, scavProfile: IPmcData): IPmcData[]
|
||||
protected postRaidXpWorkaroundFix(
|
||||
sessionId: string,
|
||||
output: IPmcData[],
|
||||
pmcProfile: IPmcData,
|
||||
scavProfile: IPmcData,
|
||||
): IPmcData[]
|
||||
{
|
||||
const clonedPmc = this.jsonUtil.clone(pmcProfile);
|
||||
const clonedScav = this.jsonUtil.clone(scavProfile);
|
||||
@ -133,8 +138,10 @@ export class ProfileHelper
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!this.sessionIdMatchesProfileId(profile.info.id, sessionID)
|
||||
&& this.nicknameMatches(profile.characters.pmc.Info.LowerNickname, nicknameRequest.nickname))
|
||||
if (
|
||||
!this.sessionIdMatchesProfileId(profile.info.id, sessionID) &&
|
||||
this.nicknameMatches(profile.characters.pmc.Info.LowerNickname, nicknameRequest.nickname)
|
||||
)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@ -211,7 +218,7 @@ export class ProfileHelper
|
||||
public getDefaultAkiDataObject(): any
|
||||
{
|
||||
return {
|
||||
version: this.getServerVersion()
|
||||
version: this.getServerVersion(),
|
||||
};
|
||||
}
|
||||
|
||||
@ -253,7 +260,7 @@ export class ProfileHelper
|
||||
DamageHistory: {
|
||||
LethalDamagePart: "Head",
|
||||
LethalDamage: undefined,
|
||||
BodyParts: <any>[]
|
||||
BodyParts: <any>[],
|
||||
},
|
||||
DroppedItems: [],
|
||||
ExperienceBonusMult: 0,
|
||||
@ -266,8 +273,8 @@ export class ProfileHelper
|
||||
SurvivorClass: "Unknown",
|
||||
TotalInGameTime: 0,
|
||||
TotalSessionExperience: 0,
|
||||
Victims: []
|
||||
}
|
||||
Victims: [],
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@ -289,14 +296,17 @@ export class ProfileHelper
|
||||
public removeSecureContainer(profile: IPmcData): IPmcData
|
||||
{
|
||||
const items = profile.Inventory.items;
|
||||
const secureContainer = items.find(x => x.slotId === "SecuredContainer");
|
||||
const secureContainer = items.find((x) => x.slotId === "SecuredContainer");
|
||||
if (secureContainer)
|
||||
{
|
||||
// Find and remove container + children
|
||||
const childItemsInSecureContainer = this.itemHelper.findAndReturnChildrenByItems(items, secureContainer._id);
|
||||
const childItemsInSecureContainer = this.itemHelper.findAndReturnChildrenByItems(
|
||||
items,
|
||||
secureContainer._id,
|
||||
);
|
||||
|
||||
// Remove child items + secure container
|
||||
profile.Inventory.items = items.filter(x => !childItemsInSecureContainer.includes(x._id));
|
||||
profile.Inventory.items = items.filter((x) => !childItemsInSecureContainer.includes(x._id));
|
||||
}
|
||||
|
||||
return profile;
|
||||
@ -340,7 +350,7 @@ export class ProfileHelper
|
||||
return false;
|
||||
}
|
||||
|
||||
return !!profile.aki.receivedGifts.find(x => x.giftId === giftId);
|
||||
return !!profile.aki.receivedGifts.find((x) => x.giftId === giftId);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -350,7 +360,7 @@ export class ProfileHelper
|
||||
*/
|
||||
public incrementStatCounter(counters: CounterKeyValue[], keyToIncrement: string): void
|
||||
{
|
||||
const stat = counters.find(x => x.Key.includes(keyToIncrement));
|
||||
const stat = counters.find((x) => x.Key.includes(keyToIncrement));
|
||||
if (stat)
|
||||
{
|
||||
stat.Value++;
|
||||
@ -371,7 +381,7 @@ export class ProfileHelper
|
||||
return false;
|
||||
}
|
||||
|
||||
const profileSkill = profileSkills.find(x => x.Id === skillType);
|
||||
const profileSkill = profileSkills.find((x) => x.Id === skillType);
|
||||
if (!profileSkill)
|
||||
{
|
||||
this.logger.warning(`Unable to check for elite skill ${skillType}, not found in profile`);
|
||||
@ -389,11 +399,18 @@ export class ProfileHelper
|
||||
* @param useSkillProgressRateMultipler Skills are multiplied by a value in globals, default is off to maintain compatibility with legacy code
|
||||
* @returns
|
||||
*/
|
||||
public addSkillPointsToPlayer(pmcProfile: IPmcData, skill: SkillTypes, pointsToAdd: number, useSkillProgressRateMultipler = false): void
|
||||
public addSkillPointsToPlayer(
|
||||
pmcProfile: IPmcData,
|
||||
skill: SkillTypes,
|
||||
pointsToAdd: number,
|
||||
useSkillProgressRateMultipler = false,
|
||||
): void
|
||||
{
|
||||
if (!pointsToAdd || pointsToAdd < 0)
|
||||
{
|
||||
this.logger.error(this.localisationService.getText("player-attempt_to_increment_skill_with_negative_value", skill));
|
||||
this.logger.error(
|
||||
this.localisationService.getText("player-attempt_to_increment_skill_with_negative_value", skill),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -405,7 +422,7 @@ export class ProfileHelper
|
||||
return;
|
||||
}
|
||||
|
||||
const profileSkill = profileSkills.find(x => x.Id === skill);
|
||||
const profileSkill = profileSkills.find((x) => x.Id === skill);
|
||||
if (!profileSkill)
|
||||
{
|
||||
this.logger.error(this.localisationService.getText("quest-no_skill_found", skill));
|
||||
@ -426,7 +443,7 @@ export class ProfileHelper
|
||||
|
||||
public getSkillFromProfile(pmcData: IPmcData, skill: SkillTypes): Common
|
||||
{
|
||||
const skillToReturn = pmcData.Skills.Common.find(x => x.Id === skill);
|
||||
const skillToReturn = pmcData.Skills.Common.find((x) => x.Id === skill);
|
||||
if (!skillToReturn)
|
||||
{
|
||||
this.logger.warning(`Profile ${pmcData.sessionId} does not have a skill named: ${skill}`);
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
import { injectable } from "tsyringe";
|
||||
|
||||
import { AvailableForConditions } from "@spt-aki/models/eft/common/tables/IQuest";
|
||||
@ -6,29 +5,45 @@ import { AvailableForConditions } from "@spt-aki/models/eft/common/tables/IQuest
|
||||
@injectable()
|
||||
export class QuestConditionHelper
|
||||
{
|
||||
public getQuestConditions(q: AvailableForConditions[], furtherFilter: (a: AvailableForConditions) => AvailableForConditions[] = null): AvailableForConditions[]
|
||||
public getQuestConditions(
|
||||
q: AvailableForConditions[],
|
||||
furtherFilter: (a: AvailableForConditions) => AvailableForConditions[] = null,
|
||||
): AvailableForConditions[]
|
||||
{
|
||||
return this.filterConditions(q, "Quest", furtherFilter);
|
||||
}
|
||||
|
||||
public getLevelConditions(q: AvailableForConditions[], furtherFilter: (a: AvailableForConditions) => AvailableForConditions[] = null): AvailableForConditions[]
|
||||
public getLevelConditions(
|
||||
q: AvailableForConditions[],
|
||||
furtherFilter: (a: AvailableForConditions) => AvailableForConditions[] = null,
|
||||
): AvailableForConditions[]
|
||||
{
|
||||
return this.filterConditions(q, "Level", furtherFilter);
|
||||
}
|
||||
|
||||
public getLoyaltyConditions(q: AvailableForConditions[], furtherFilter: (a: AvailableForConditions) => AvailableForConditions[] = null): AvailableForConditions[]
|
||||
public getLoyaltyConditions(
|
||||
q: AvailableForConditions[],
|
||||
furtherFilter: (a: AvailableForConditions) => AvailableForConditions[] = null,
|
||||
): AvailableForConditions[]
|
||||
{
|
||||
return this.filterConditions(q, "TraderLoyalty", furtherFilter);
|
||||
}
|
||||
|
||||
public getStandingConditions(q: AvailableForConditions[], furtherFilter: (a: AvailableForConditions) => AvailableForConditions[] = null): AvailableForConditions[]
|
||||
public getStandingConditions(
|
||||
q: AvailableForConditions[],
|
||||
furtherFilter: (a: AvailableForConditions) => AvailableForConditions[] = null,
|
||||
): AvailableForConditions[]
|
||||
{
|
||||
return this.filterConditions(q, "TraderStanding", furtherFilter);
|
||||
}
|
||||
|
||||
protected filterConditions(q: AvailableForConditions[], questType: string, furtherFilter: (a: AvailableForConditions) => AvailableForConditions[] = null): AvailableForConditions[]
|
||||
protected filterConditions(
|
||||
q: AvailableForConditions[],
|
||||
questType: string,
|
||||
furtherFilter: (a: AvailableForConditions) => AvailableForConditions[] = null,
|
||||
): AvailableForConditions[]
|
||||
{
|
||||
const filteredQuests = q.filter(c =>
|
||||
const filteredQuests = q.filter((c) =>
|
||||
{
|
||||
if (c._parent === questType)
|
||||
{
|
||||
|
@ -53,7 +53,7 @@ export class QuestHelper
|
||||
@inject("LocalisationService") protected localisationService: LocalisationService,
|
||||
@inject("TraderHelper") protected traderHelper: TraderHelper,
|
||||
@inject("MailSendService") protected mailSendService: MailSendService,
|
||||
@inject("ConfigServer") protected configServer: ConfigServer
|
||||
@inject("ConfigServer") protected configServer: ConfigServer,
|
||||
)
|
||||
{
|
||||
this.questConfig = this.configServer.getConfig(ConfigTypes.QUEST);
|
||||
@ -67,11 +67,11 @@ export class QuestHelper
|
||||
*/
|
||||
public getQuestStatus(pmcData: IPmcData, questId: string): QuestStatus
|
||||
{
|
||||
const quest = pmcData.Quests?.find(q => q.qid === questId);
|
||||
const quest = pmcData.Quests?.find((q) => q.qid === questId);
|
||||
|
||||
return quest
|
||||
? quest.status
|
||||
: QuestStatus.Locked;
|
||||
return quest ?
|
||||
quest.status :
|
||||
QuestStatus.Locked;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -97,7 +97,12 @@ export class QuestHelper
|
||||
case "=":
|
||||
return playerLevel === <number>condition._props.value;
|
||||
default:
|
||||
this.logger.error(this.localisationService.getText("quest-unable_to_find_compare_condition", condition._props.compareMethod));
|
||||
this.logger.error(
|
||||
this.localisationService.getText(
|
||||
"quest-unable_to_find_compare_condition",
|
||||
condition._props.compareMethod,
|
||||
),
|
||||
);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -189,7 +194,6 @@ export class QuestHelper
|
||||
return this.localeService.getLocaleDb()[questNameKey];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check if trader has sufficient loyalty to fulfill quest requirement
|
||||
* @param questProperties Quest props
|
||||
@ -274,9 +278,11 @@ export class QuestHelper
|
||||
// separate base item and mods, fix stacks
|
||||
if (item._id === reward.target)
|
||||
{
|
||||
if ((item.parentId !== undefined) && (item.parentId === "hideout")
|
||||
&& (item.upd !== undefined) && (item.upd.StackObjectsCount !== undefined)
|
||||
&& (item.upd.StackObjectsCount > 1))
|
||||
if (
|
||||
(item.parentId !== undefined) && (item.parentId === "hideout") &&
|
||||
(item.upd !== undefined) && (item.upd.StackObjectsCount !== undefined) &&
|
||||
(item.upd.StackObjectsCount > 1)
|
||||
)
|
||||
{
|
||||
item.upd.StackObjectsCount = 1;
|
||||
}
|
||||
@ -323,9 +329,11 @@ export class QuestHelper
|
||||
{
|
||||
// Iterate over all rewards with the desired status, flatten out items that have a type of Item
|
||||
const questRewards = quest.rewards[QuestStatus[status]]
|
||||
.flatMap((reward: Reward) => reward.type === "Item"
|
||||
? this.processReward(reward)
|
||||
: []);
|
||||
.flatMap((reward: Reward) =>
|
||||
reward.type === "Item" ?
|
||||
this.processReward(reward) :
|
||||
[]
|
||||
);
|
||||
|
||||
return questRewards;
|
||||
}
|
||||
@ -336,9 +344,13 @@ export class QuestHelper
|
||||
* @param newState State the new quest should be in when returned
|
||||
* @param acceptedQuest Details of accepted quest from client
|
||||
*/
|
||||
public getQuestReadyForProfile(pmcData: IPmcData, newState: QuestStatus, acceptedQuest: IAcceptQuestRequestData): IQuestStatus
|
||||
public getQuestReadyForProfile(
|
||||
pmcData: IPmcData,
|
||||
newState: QuestStatus,
|
||||
acceptedQuest: IAcceptQuestRequestData,
|
||||
): IQuestStatus
|
||||
{
|
||||
const existingQuest = pmcData.Quests.find(q => q.qid === acceptedQuest.qid);
|
||||
const existingQuest = pmcData.Quests.find((q) => q.qid === acceptedQuest.qid);
|
||||
if (existingQuest)
|
||||
{
|
||||
// Quest exists, update its status
|
||||
@ -360,12 +372,12 @@ export class QuestHelper
|
||||
qid: acceptedQuest.qid,
|
||||
startTime: this.timeUtil.getTimestamp(),
|
||||
status: newState,
|
||||
statusTimers: {}
|
||||
statusTimers: {},
|
||||
};
|
||||
|
||||
// Check if quest has a prereq to be placed in a 'pending' state
|
||||
const questDbData = this.getQuestFromDb(acceptedQuest.qid, pmcData);
|
||||
const waitTime = questDbData.conditions.AvailableForStart.find(x => x._props.availableAfter > 0);
|
||||
const waitTime = questDbData.conditions.AvailableForStart.find((x) => x._props.availableAfter > 0);
|
||||
if (waitTime && acceptedQuest.type !== "repeatable")
|
||||
{
|
||||
// Quest should be put into 'pending' state
|
||||
@ -392,18 +404,18 @@ export class QuestHelper
|
||||
{
|
||||
// Get quest acceptance data from profile
|
||||
const profile: IPmcData = this.profileHelper.getPmcProfile(sessionID);
|
||||
const startedQuestInProfile = profile.Quests.find(x => x.qid === startedQuestId);
|
||||
const startedQuestInProfile = profile.Quests.find((x) => x.qid === startedQuestId);
|
||||
|
||||
// Get quests that
|
||||
const eligibleQuests = this.getQuestsFromDb().filter((quest) =>
|
||||
{
|
||||
// Quest is accessible to player when the accepted quest passed into param is started
|
||||
// e.g. Quest A passed in, quest B is looped over and has requirement of A to be started, include it
|
||||
const acceptedQuestCondition = quest.conditions.AvailableForStart.find(x =>
|
||||
const acceptedQuestCondition = quest.conditions.AvailableForStart.find((x) =>
|
||||
{
|
||||
return x._parent === "Quest"
|
||||
&& x._props.target === startedQuestId
|
||||
&& x._props.status[0] === QuestStatus.Started;
|
||||
return x._parent === "Quest" &&
|
||||
x._props.target === startedQuestId &&
|
||||
x._props.status[0] === QuestStatus.Started;
|
||||
});
|
||||
|
||||
// Not found, skip quest
|
||||
@ -412,7 +424,9 @@ export class QuestHelper
|
||||
return false;
|
||||
}
|
||||
|
||||
const standingRequirements = this.questConditionHelper.getStandingConditions(quest.conditions.AvailableForStart);
|
||||
const standingRequirements = this.questConditionHelper.getStandingConditions(
|
||||
quest.conditions.AvailableForStart,
|
||||
);
|
||||
for (const condition of standingRequirements)
|
||||
{
|
||||
if (!this.traderStandingRequirementCheck(condition._props, profile))
|
||||
@ -421,7 +435,9 @@ export class QuestHelper
|
||||
}
|
||||
}
|
||||
|
||||
const loyaltyRequirements = this.questConditionHelper.getLoyaltyConditions(quest.conditions.AvailableForStart);
|
||||
const loyaltyRequirements = this.questConditionHelper.getLoyaltyConditions(
|
||||
quest.conditions.AvailableForStart,
|
||||
);
|
||||
for (const condition of loyaltyRequirements)
|
||||
{
|
||||
if (!this.traderLoyaltyLevelRequirementCheck(condition._props, profile))
|
||||
@ -431,7 +447,8 @@ export class QuestHelper
|
||||
}
|
||||
|
||||
// Include if quest found in profile and is started or ready to hand in
|
||||
return startedQuestInProfile && ([QuestStatus.Started, QuestStatus.AvailableForFinish].includes(startedQuestInProfile.status));
|
||||
return startedQuestInProfile &&
|
||||
([QuestStatus.Started, QuestStatus.AvailableForFinish].includes(startedQuestInProfile.status));
|
||||
});
|
||||
|
||||
return this.getQuestsWithOnlyLevelRequirementStartCondition(eligibleQuests);
|
||||
@ -446,17 +463,18 @@ export class QuestHelper
|
||||
public failedUnlocked(failedQuestId: string, sessionId: string): IQuest[]
|
||||
{
|
||||
const profile = this.profileHelper.getPmcProfile(sessionId);
|
||||
const profileQuest = profile.Quests.find(x => x.qid === failedQuestId);
|
||||
const profileQuest = profile.Quests.find((x) => x.qid === failedQuestId);
|
||||
|
||||
const quests = this.getQuestsFromDb().filter((q) =>
|
||||
{
|
||||
const acceptedQuestCondition = q.conditions.AvailableForStart.find(
|
||||
c =>
|
||||
(c) =>
|
||||
{
|
||||
return c._parent === "Quest"
|
||||
&& c._props.target === failedQuestId
|
||||
&& c._props.status[0] === QuestStatus.Fail;
|
||||
});
|
||||
return c._parent === "Quest" &&
|
||||
c._props.target === failedQuestId &&
|
||||
c._props.status[0] === QuestStatus.Fail;
|
||||
},
|
||||
);
|
||||
|
||||
if (!acceptedQuestCondition)
|
||||
{
|
||||
@ -490,7 +508,9 @@ export class QuestHelper
|
||||
{
|
||||
if (this.paymentHelper.isMoneyTpl(reward.items[0]._tpl))
|
||||
{
|
||||
reward.items[0].upd.StackObjectsCount += Math.round(reward.items[0].upd.StackObjectsCount * multiplier / 100);
|
||||
reward.items[0].upd.StackObjectsCount += Math.round(
|
||||
reward.items[0].upd.StackObjectsCount * multiplier / 100,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -507,9 +527,15 @@ export class QuestHelper
|
||||
* @param sessionID Session id
|
||||
* @param output ItemEvent router response
|
||||
*/
|
||||
public changeItemStack(pmcData: IPmcData, itemId: string, newStackSize: number, sessionID: string, output: IItemEventRouterResponse): void
|
||||
public changeItemStack(
|
||||
pmcData: IPmcData,
|
||||
itemId: string,
|
||||
newStackSize: number,
|
||||
sessionID: string,
|
||||
output: IItemEventRouterResponse,
|
||||
): void
|
||||
{
|
||||
const inventoryItemIndex = pmcData.Inventory.items.findIndex(item => item._id === itemId);
|
||||
const inventoryItemIndex = pmcData.Inventory.items.findIndex((item) => item._id === itemId);
|
||||
if (inventoryItemIndex < 0)
|
||||
{
|
||||
this.logger.error(this.localisationService.getText("quest-item_not_found_in_inventory", itemId));
|
||||
@ -543,7 +569,11 @@ export class QuestHelper
|
||||
* @param sessionId Session id
|
||||
* @param item Item that was adjusted
|
||||
*/
|
||||
protected addItemStackSizeChangeIntoEventResponse(output: IItemEventRouterResponse, sessionId: string, item: Item): void
|
||||
protected addItemStackSizeChangeIntoEventResponse(
|
||||
output: IItemEventRouterResponse,
|
||||
sessionId: string,
|
||||
item: Item,
|
||||
): void
|
||||
{
|
||||
output.profileChanges[sessionId].items.change.push({
|
||||
_id: item._id,
|
||||
@ -552,8 +582,8 @@ export class QuestHelper
|
||||
slotId: item.slotId,
|
||||
location: item.location,
|
||||
upd: {
|
||||
StackObjectsCount: item.upd.StackObjectsCount
|
||||
}
|
||||
StackObjectsCount: item.upd.StackObjectsCount,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@ -580,7 +610,7 @@ export class QuestHelper
|
||||
public getQuestWithOnlyLevelRequirementStartCondition(quest: IQuest): IQuest
|
||||
{
|
||||
quest = this.jsonUtil.clone(quest);
|
||||
quest.conditions.AvailableForStart = quest.conditions.AvailableForStart.filter(q => q._parent === "Level");
|
||||
quest.conditions.AvailableForStart = quest.conditions.AvailableForStart.filter((q) => q._parent === "Level");
|
||||
|
||||
return quest;
|
||||
}
|
||||
@ -593,7 +623,12 @@ export class QuestHelper
|
||||
* @param output Client output
|
||||
* @returns Item event router response
|
||||
*/
|
||||
public failQuest(pmcData: IPmcData, failRequest: IFailQuestRequestData, sessionID: string, output: IItemEventRouterResponse = null): IItemEventRouterResponse
|
||||
public failQuest(
|
||||
pmcData: IPmcData,
|
||||
failRequest: IFailQuestRequestData,
|
||||
sessionID: string,
|
||||
output: IItemEventRouterResponse = null,
|
||||
): IItemEventRouterResponse
|
||||
{
|
||||
// Prepare response to send back client
|
||||
if (!output)
|
||||
@ -613,7 +648,7 @@ export class QuestHelper
|
||||
MessageType.QUEST_FAIL,
|
||||
quest.failMessageText,
|
||||
questRewards,
|
||||
this.timeUtil.getHoursAsSeconds(this.questConfig.redeemTime)
|
||||
this.timeUtil.getHoursAsSeconds(this.questConfig.redeemTime),
|
||||
);
|
||||
|
||||
output.profileChanges[sessionID].quests.push(this.failedUnlocked(failRequest.qid, sessionID));
|
||||
@ -647,7 +682,7 @@ export class QuestHelper
|
||||
// Check daily/weekly objects
|
||||
for (const repeatableType of pmcData.RepeatableQuests)
|
||||
{
|
||||
quest = <IQuest><unknown>repeatableType.activeQuests.find(x => x._id === questId);
|
||||
quest = <IQuest><unknown>repeatableType.activeQuests.find((x) => x._id === questId);
|
||||
if (quest)
|
||||
{
|
||||
break;
|
||||
@ -668,7 +703,10 @@ export class QuestHelper
|
||||
{
|
||||
// blank or is a guid, use description instead
|
||||
const startedMessageText = this.getQuestLocaleIdFromDb(startedMessageTextId);
|
||||
if (!startedMessageText || startedMessageText.trim() === "" || startedMessageText.toLowerCase() === "test" || startedMessageText.length === 24)
|
||||
if (
|
||||
!startedMessageText || startedMessageText.trim() === "" || startedMessageText.toLowerCase() === "test" ||
|
||||
startedMessageText.length === 24
|
||||
)
|
||||
{
|
||||
return questDescriptionId;
|
||||
}
|
||||
@ -696,7 +734,7 @@ export class QuestHelper
|
||||
public updateQuestState(pmcData: IPmcData, newQuestState: QuestStatus, questId: string): void
|
||||
{
|
||||
// Find quest in profile, update status to desired status
|
||||
const questToUpdate = pmcData.Quests.find(quest => quest.qid === questId);
|
||||
const questToUpdate = pmcData.Quests.find((quest) => quest.qid === questId);
|
||||
if (questToUpdate)
|
||||
{
|
||||
questToUpdate.status = newQuestState;
|
||||
@ -713,7 +751,13 @@ export class QuestHelper
|
||||
* @param questResponse Response to send back to client
|
||||
* @returns Array of reward objects
|
||||
*/
|
||||
public applyQuestReward(pmcData: IPmcData, questId: string, state: QuestStatus, sessionId: string, questResponse: IItemEventRouterResponse): Reward[]
|
||||
public applyQuestReward(
|
||||
pmcData: IPmcData,
|
||||
questId: string,
|
||||
state: QuestStatus,
|
||||
sessionId: string,
|
||||
questResponse: IItemEventRouterResponse,
|
||||
): Reward[]
|
||||
{
|
||||
let questDetails = this.getQuestFromDb(questId, pmcData);
|
||||
if (!questDetails)
|
||||
@ -738,7 +782,11 @@ export class QuestHelper
|
||||
switch (reward.type)
|
||||
{
|
||||
case QuestRewardType.SKILL:
|
||||
this.profileHelper.addSkillPointsToPlayer(pmcData, reward.target as SkillTypes, Number(reward.value));
|
||||
this.profileHelper.addSkillPointsToPlayer(
|
||||
pmcData,
|
||||
reward.target as SkillTypes,
|
||||
Number(reward.value),
|
||||
);
|
||||
break;
|
||||
case QuestRewardType.EXPERIENCE:
|
||||
this.profileHelper.addExperienceToPmc(sessionId, parseInt(<string>reward.value)); // this must occur first as the output object needs to take the modified profile exp value
|
||||
@ -759,10 +807,22 @@ export class QuestHelper
|
||||
this.logger.debug("Not implemented stash rows reward yet");
|
||||
break;
|
||||
case QuestRewardType.PRODUCTIONS_SCHEME:
|
||||
this.findAndAddHideoutProductionIdToProfile(pmcData, reward, questDetails, sessionId, questResponse);
|
||||
this.findAndAddHideoutProductionIdToProfile(
|
||||
pmcData,
|
||||
reward,
|
||||
questDetails,
|
||||
sessionId,
|
||||
questResponse,
|
||||
);
|
||||
break;
|
||||
default:
|
||||
this.logger.error(this.localisationService.getText("quest-reward_type_not_handled", {rewardType: reward.type, questId: questId, questName: questDetails.QuestName}));
|
||||
this.logger.error(
|
||||
this.localisationService.getText("quest-reward_type_not_handled", {
|
||||
rewardType: reward.type,
|
||||
questId: questId,
|
||||
questName: questDetails.QuestName,
|
||||
}),
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -779,19 +839,31 @@ export class QuestHelper
|
||||
* @param sessionID Session id
|
||||
* @param response Response to send back to client
|
||||
*/
|
||||
protected findAndAddHideoutProductionIdToProfile(pmcData: IPmcData, craftUnlockReward: Reward, questDetails: IQuest, sessionID: string, response: IItemEventRouterResponse): void
|
||||
protected findAndAddHideoutProductionIdToProfile(
|
||||
pmcData: IPmcData,
|
||||
craftUnlockReward: Reward,
|
||||
questDetails: IQuest,
|
||||
sessionID: string,
|
||||
response: IItemEventRouterResponse,
|
||||
): void
|
||||
{
|
||||
// Get hideout crafts and find those that match by areatype/required level/end product tpl - hope for just one match
|
||||
const hideoutProductions = this.databaseServer.getTables().hideout.production;
|
||||
const matchingProductions = hideoutProductions.filter(x =>
|
||||
x.areaType === Number.parseInt(craftUnlockReward.traderId)
|
||||
&& x.requirements.some(x => x.requiredLevel === craftUnlockReward.loyaltyLevel)
|
||||
&& x.endProduct === craftUnlockReward.items[0]._tpl);
|
||||
const matchingProductions = hideoutProductions.filter((x) =>
|
||||
x.areaType === Number.parseInt(craftUnlockReward.traderId) &&
|
||||
x.requirements.some((x) => x.requiredLevel === craftUnlockReward.loyaltyLevel) &&
|
||||
x.endProduct === craftUnlockReward.items[0]._tpl
|
||||
);
|
||||
|
||||
// More/less than 1 match, above filtering wasn't strict enough
|
||||
if (matchingProductions.length !== 1)
|
||||
{
|
||||
this.logger.error(this.localisationService.getText("quest-unable_to_find_matching_hideout_production", {questName: questDetails.QuestName, matchCount: matchingProductions.length}));
|
||||
this.logger.error(
|
||||
this.localisationService.getText("quest-unable_to_find_matching_hideout_production", {
|
||||
questName: questDetails.QuestName,
|
||||
matchCount: matchingProductions.length,
|
||||
}),
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
@ -810,7 +882,7 @@ export class QuestHelper
|
||||
protected getQuestMoneyRewardBonus(pmcData: IPmcData): number
|
||||
{
|
||||
// Check player has intel center
|
||||
const moneyRewardBonuses = pmcData.Bonuses.filter(x => x.type === "QuestMoneyReward");
|
||||
const moneyRewardBonuses = pmcData.Bonuses.filter((x) => x.type === "QuestMoneyReward");
|
||||
if (!moneyRewardBonuses)
|
||||
{
|
||||
return 0;
|
||||
@ -823,7 +895,7 @@ export class QuestHelper
|
||||
const hideoutManagementSkill = this.profileHelper.getSkillFromProfile(pmcData, SkillTypes.HIDEOUT_MANAGEMENT);
|
||||
if (hideoutManagementSkill)
|
||||
{
|
||||
moneyRewardBonus *= (1 + (hideoutManagementSkill.Progress / 10000)); // 5100 becomes 0.51, add 1 to it, 1.51, multiply the moneyreward bonus by it (e.g. 15 x 51)
|
||||
moneyRewardBonus *= 1 + (hideoutManagementSkill.Progress / 10000); // 5100 becomes 0.51, add 1 to it, 1.51, multiply the moneyreward bonus by it (e.g. 15 x 51)
|
||||
}
|
||||
|
||||
return moneyRewardBonus;
|
||||
@ -835,19 +907,27 @@ export class QuestHelper
|
||||
* @param questIds Quests to search through for the findItem condition
|
||||
* @returns quest id with 'FindItem' condition id
|
||||
*/
|
||||
public getFindItemConditionByQuestItem(itemTpl: string, questIds: string[], allQuests: IQuest[]): Record<string, string>
|
||||
public getFindItemConditionByQuestItem(
|
||||
itemTpl: string,
|
||||
questIds: string[],
|
||||
allQuests: IQuest[],
|
||||
): Record<string, string>
|
||||
{
|
||||
const result: Record<string, string> = {};
|
||||
for (const questId of questIds)
|
||||
{
|
||||
const questInDb = allQuests.find(x => x._id === questId);
|
||||
const questInDb = allQuests.find((x) => x._id === questId);
|
||||
if (!questInDb)
|
||||
{
|
||||
this.logger.warning(`Unable to find quest: ${questId} in db, cannot get 'FindItem' condition, skipping`);
|
||||
this.logger.warning(
|
||||
`Unable to find quest: ${questId} in db, cannot get 'FindItem' condition, skipping`,
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
const condition = questInDb.conditions.AvailableForFinish.find(c => c._parent === "FindItem" && c._props?.target?.includes(itemTpl));
|
||||
const condition = questInDb.conditions.AvailableForFinish.find((c) =>
|
||||
c._parent === "FindItem" && c._props?.target?.includes(itemTpl)
|
||||
);
|
||||
if (condition)
|
||||
{
|
||||
result[questId] = condition._props.id;
|
||||
@ -872,7 +952,7 @@ export class QuestHelper
|
||||
{
|
||||
// Quest from db matches quests in profile, skip
|
||||
const questData = quests[questKey];
|
||||
if (pmcProfile.Quests.find(x => x.qid === questData._id))
|
||||
if (pmcProfile.Quests.find((x) => x.qid === questData._id))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@ -889,13 +969,13 @@ export class QuestHelper
|
||||
status: statuses[statuses.length - 1],
|
||||
statusTimers: statusesDict,
|
||||
completedConditions: [],
|
||||
availableAfter: 0
|
||||
availableAfter: 0,
|
||||
};
|
||||
|
||||
if (pmcProfile.Quests.some(x => x.qid === questKey))
|
||||
if (pmcProfile.Quests.some((x) => x.qid === questKey))
|
||||
{
|
||||
// Update existing
|
||||
const existingQuest = pmcProfile.Quests.find(x => x.qid === questKey);
|
||||
const existingQuest = pmcProfile.Quests.find((x) => x.qid === questKey);
|
||||
existingQuest.status = questRecordToAdd.status;
|
||||
existingQuest.statusTimers = questRecordToAdd.statusTimers;
|
||||
}
|
||||
@ -909,7 +989,7 @@ export class QuestHelper
|
||||
|
||||
public findAndRemoveQuestFromArrayIfExists(questId: string, quests: IQuestStatus[]): void
|
||||
{
|
||||
const pmcQuestToReplaceStatus = quests.find(x => x.qid === questId);
|
||||
const pmcQuestToReplaceStatus = quests.find((x) => x.qid === questId);
|
||||
if (pmcQuestToReplaceStatus)
|
||||
{
|
||||
quests.splice(quests.indexOf(pmcQuestToReplaceStatus, 1));
|
||||
|
@ -31,7 +31,7 @@ export class RagfairHelper
|
||||
@inject("ItemHelper") protected itemHelper: ItemHelper,
|
||||
@inject("RagfairLinkedItemService") protected ragfairLinkedItemService: RagfairLinkedItemService,
|
||||
@inject("UtilityHelper") protected utilityHelper: UtilityHelper,
|
||||
@inject("ConfigServer") protected configServer: ConfigServer
|
||||
@inject("ConfigServer") protected configServer: ConfigServer,
|
||||
)
|
||||
{
|
||||
this.ragfairConfig = this.configServer.getConfig(ConfigTypes.RAGFAIR);
|
||||
@ -73,9 +73,9 @@ export class RagfairHelper
|
||||
if (info.linkedSearchId)
|
||||
{
|
||||
const data = this.ragfairLinkedItemService.getLinkedItems(info.linkedSearchId);
|
||||
result = !data
|
||||
? []
|
||||
: [...data];
|
||||
result = !data ?
|
||||
[] :
|
||||
[...data];
|
||||
}
|
||||
|
||||
// Case: category
|
||||
@ -185,7 +185,7 @@ export class RagfairHelper
|
||||
for (let item of items)
|
||||
{
|
||||
item = this.itemHelper.fixItemStackCount(item);
|
||||
const isChild = items.find(it => it._id === item.parentId);
|
||||
const isChild = items.find((it) => it._id === item.parentId);
|
||||
|
||||
if (!isChild)
|
||||
{
|
||||
|
@ -60,7 +60,7 @@ export class RagfairOfferHelper
|
||||
@inject("LocaleService") protected localeService: LocaleService,
|
||||
@inject("LocalisationService") protected localisationService: LocalisationService,
|
||||
@inject("MailSendService") protected mailSendService: MailSendService,
|
||||
@inject("ConfigServer") protected configServer: ConfigServer
|
||||
@inject("ConfigServer") protected configServer: ConfigServer,
|
||||
)
|
||||
{
|
||||
this.ragfairConfig = this.configServer.getConfig(ConfigTypes.RAGFAIR);
|
||||
@ -75,9 +75,16 @@ export class RagfairOfferHelper
|
||||
* @param pmcProfile Player profile
|
||||
* @returns Offers the player should see
|
||||
*/
|
||||
public getValidOffers(searchRequest: ISearchRequestData, itemsToAdd: string[], traderAssorts: Record<string, ITraderAssort>, pmcProfile: IPmcData): IRagfairOffer[]
|
||||
public getValidOffers(
|
||||
searchRequest: ISearchRequestData,
|
||||
itemsToAdd: string[],
|
||||
traderAssorts: Record<string, ITraderAssort>,
|
||||
pmcProfile: IPmcData,
|
||||
): IRagfairOffer[]
|
||||
{
|
||||
return this.ragfairOfferService.getOffers().filter(x => this.isDisplayableOffer(searchRequest, itemsToAdd, traderAssorts, x, pmcProfile));
|
||||
return this.ragfairOfferService.getOffers().filter((x) =>
|
||||
this.isDisplayableOffer(searchRequest, itemsToAdd, traderAssorts, x, pmcProfile)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -88,7 +95,12 @@ export class RagfairOfferHelper
|
||||
* @param pmcProfile Player profile
|
||||
* @returns IRagfairOffer array
|
||||
*/
|
||||
public getOffersForBuild(searchRequest: ISearchRequestData, itemsToAdd: string[], traderAssorts: Record<string, ITraderAssort>, pmcProfile: IPmcData): IRagfairOffer[]
|
||||
public getOffersForBuild(
|
||||
searchRequest: ISearchRequestData,
|
||||
itemsToAdd: string[],
|
||||
traderAssorts: Record<string, ITraderAssort>,
|
||||
pmcProfile: IPmcData,
|
||||
): IRagfairOffer[]
|
||||
{
|
||||
const offersMap = new Map<string, IRagfairOffer[]>();
|
||||
const offers: IRagfairOffer[] = [];
|
||||
@ -139,7 +151,7 @@ export class RagfairOfferHelper
|
||||
const lockedOffers = this.getLoyaltyLockedOffers(possibleOffers, pmcProfile);
|
||||
|
||||
// Exclude locked offers + above loyalty locked offers if at least 1 was found
|
||||
const availableOffers = possibleOffers.filter(x => !(x.locked || lockedOffers.includes(x._id)));
|
||||
const availableOffers = possibleOffers.filter((x) => !(x.locked || lockedOffers.includes(x._id)));
|
||||
if (availableOffers.length > 0)
|
||||
{
|
||||
possibleOffers = availableOffers;
|
||||
@ -174,7 +186,9 @@ export class RagfairOfferHelper
|
||||
*/
|
||||
public traderOfferItemQuestLocked(offer: IRagfairOffer, traderAssorts: Record<string, ITraderAssort>): boolean
|
||||
{
|
||||
return offer.items?.some(i => traderAssorts[offer.user.id].barter_scheme[i._id]?.some(bs1 => bs1?.some(bs2 => bs2.sptQuestLocked)));
|
||||
return offer.items?.some((i) =>
|
||||
traderAssorts[offer.user.id].barter_scheme[i._id]?.some((bs1) => bs1?.some((bs2) => bs2.sptQuestLocked))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -200,12 +214,16 @@ export class RagfairOfferHelper
|
||||
protected traderBuyRestrictionReached(offer: IRagfairOffer): boolean
|
||||
{
|
||||
const traderAssorts = this.traderHelper.getTraderAssortsByTraderId(offer.user.id).items;
|
||||
const assortData = traderAssorts.find(x => x._id === offer.items[0]._id);
|
||||
const assortData = traderAssorts.find((x) => x._id === offer.items[0]._id);
|
||||
|
||||
// No trader assort data
|
||||
if (!assortData)
|
||||
{
|
||||
this.logger.warning(`Unable to find trader: ${offer.user.nickname} assort for item: ${this.itemHelper.getItemName(offer.items[0]._tpl)} ${offer.items[0]._tpl}, cannot check if buy restriction reached`);
|
||||
this.logger.warning(
|
||||
`Unable to find trader: ${offer.user.nickname} assort for item: ${
|
||||
this.itemHelper.getItemName(offer.items[0]._tpl)
|
||||
} ${offer.items[0]._tpl}, cannot check if buy restriction reached`,
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -279,7 +297,10 @@ export class RagfairOfferHelper
|
||||
boughtAmount = offer.sellResult[0].amount;
|
||||
}
|
||||
|
||||
this.increaseProfileRagfairRating(this.saveServer.getProfile(sessionID), offer.summaryCost / totalItemsCount * boughtAmount);
|
||||
this.increaseProfileRagfairRating(
|
||||
this.saveServer.getProfile(sessionID),
|
||||
offer.summaryCost / totalItemsCount * boughtAmount,
|
||||
);
|
||||
|
||||
this.completeOffer(sessionID, offer, boughtAmount);
|
||||
offer.sellResult.splice(0, 1);
|
||||
@ -331,7 +352,7 @@ export class RagfairOfferHelper
|
||||
protected deleteOfferById(sessionID: string, offerId: string): void
|
||||
{
|
||||
const profileRagfairInfo = this.saveServer.getProfile(sessionID).characters.pmc.RagfairInfo;
|
||||
const index = profileRagfairInfo.offers.findIndex(o => o._id === offerId);
|
||||
const index = profileRagfairInfo.offers.findIndex((o) => o._id === offerId);
|
||||
profileRagfairInfo.offers.splice(index, 1);
|
||||
// Also delete from ragfair
|
||||
this.ragfairOfferService.removeOfferById(offerId);
|
||||
@ -357,7 +378,7 @@ export class RagfairOfferHelper
|
||||
else
|
||||
{
|
||||
offer.items[0].upd.StackObjectsCount -= boughtAmount;
|
||||
const rootItems = offer.items.filter(i => i.parentId === "hideout");
|
||||
const rootItems = offer.items.filter((i) => i.parentId === "hideout");
|
||||
rootItems.splice(0, 1);
|
||||
|
||||
let removeCount = boughtAmount;
|
||||
@ -385,10 +406,11 @@ export class RagfairOfferHelper
|
||||
{
|
||||
foundNewItems = false;
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
for (const id of idsToRemove)
|
||||
{
|
||||
const newIds = offer.items.filter(i => !idsToRemove.includes(i._id) && idsToRemove.includes(i.parentId)).map(i => i._id);
|
||||
const newIds = offer.items.filter((i) =>
|
||||
!idsToRemove.includes(i._id) && idsToRemove.includes(i.parentId)
|
||||
).map((i) => i._id);
|
||||
if (newIds.length > 0)
|
||||
{
|
||||
foundNewItems = true;
|
||||
@ -399,7 +421,7 @@ export class RagfairOfferHelper
|
||||
|
||||
if (idsToRemove.length > 0)
|
||||
{
|
||||
offer.items = offer.items.filter(i => !idsToRemove.includes(i._id));
|
||||
offer.items = offer.items.filter((i) => !idsToRemove.includes(i._id));
|
||||
}
|
||||
}
|
||||
|
||||
@ -410,7 +432,7 @@ export class RagfairOfferHelper
|
||||
const requestedItem: Item = {
|
||||
_id: this.hashUtil.generate(),
|
||||
_tpl: requirement._tpl,
|
||||
upd: { StackObjectsCount: requirement.count * boughtAmount }
|
||||
upd: {StackObjectsCount: requirement.count * boughtAmount},
|
||||
};
|
||||
|
||||
const stacks = this.itemHelper.splitStack(requestedItem);
|
||||
@ -436,7 +458,7 @@ export class RagfairOfferHelper
|
||||
const ragfairDetails = {
|
||||
offerId: offer._id,
|
||||
count: offer.sellInOnePiece ? offerStackCount : boughtAmount, // pack-offers NEED to the full item count otherwise it only removes 1 from the pack, leaving phantom offer on client ui
|
||||
handbookId: itemTpl
|
||||
handbookId: itemTpl,
|
||||
};
|
||||
|
||||
this.mailSendService.sendDirectNpcMessageToPlayer(
|
||||
@ -447,7 +469,8 @@ export class RagfairOfferHelper
|
||||
itemsToSend,
|
||||
this.timeUtil.getHoursAsSeconds(this.questConfig.redeemTime),
|
||||
null,
|
||||
ragfairDetails);
|
||||
ragfairDetails,
|
||||
);
|
||||
|
||||
return this.eventOutputHolder.getOutput(sessionID);
|
||||
}
|
||||
@ -465,14 +488,19 @@ export class RagfairOfferHelper
|
||||
const soldMessageLocaleGuid = globalLocales[RagfairOfferHelper.goodSoldTemplate];
|
||||
if (!soldMessageLocaleGuid)
|
||||
{
|
||||
this.logger.error(this.localisationService.getText("ragfair-unable_to_find_locale_by_key", RagfairOfferHelper.goodSoldTemplate));
|
||||
this.logger.error(
|
||||
this.localisationService.getText(
|
||||
"ragfair-unable_to_find_locale_by_key",
|
||||
RagfairOfferHelper.goodSoldTemplate,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Used to replace tokens in sold message sent to player
|
||||
const tplVars: ISystemData = {
|
||||
soldItem: globalLocales[`${itemTpl} Name`] || itemTpl,
|
||||
buyerNickname: this.ragfairServerHelper.getNickname(this.hashUtil.generate()),
|
||||
itemCount: boughtAmount
|
||||
itemCount: boughtAmount,
|
||||
};
|
||||
|
||||
const offerSoldMessageText = soldMessageLocaleGuid.replace(/{\w+}/g, (matched) =>
|
||||
@ -492,14 +520,23 @@ export class RagfairOfferHelper
|
||||
* @param pmcProfile Player profile
|
||||
* @returns True = should be shown to player
|
||||
*/
|
||||
public isDisplayableOffer(searchRequest: ISearchRequestData, itemsToAdd: string[], traderAssorts: Record<string, ITraderAssort>, offer: IRagfairOffer, pmcProfile: IPmcData): boolean
|
||||
public isDisplayableOffer(
|
||||
searchRequest: ISearchRequestData,
|
||||
itemsToAdd: string[],
|
||||
traderAssorts: Record<string, ITraderAssort>,
|
||||
offer: IRagfairOffer,
|
||||
pmcProfile: IPmcData,
|
||||
): boolean
|
||||
{
|
||||
const item = offer.items[0];
|
||||
const money = offer.requirements[0]._tpl;
|
||||
const isTraderOffer = offer.user.memberType === MemberCategory.TRADER;
|
||||
const isDefaultUserOffer = offer.user.memberType === MemberCategory.DEFAULT;
|
||||
|
||||
if (pmcProfile.Info.Level < this.databaseServer.getTables().globals.config.RagFair.minUserLevel && isDefaultUserOffer)
|
||||
if (
|
||||
pmcProfile.Info.Level < this.databaseServer.getTables().globals.config.RagFair.minUserLevel &&
|
||||
isDefaultUserOffer
|
||||
)
|
||||
{
|
||||
// Skip item if player is < global unlock level (default is 15) and item is from a dynamically generated source
|
||||
return false;
|
||||
@ -512,7 +549,7 @@ export class RagfairOfferHelper
|
||||
}
|
||||
|
||||
// Performing a required search and offer doesn't have requirement for item
|
||||
if (searchRequest.neededSearchId && !offer.requirements.some(x => x._tpl === searchRequest.neededSearchId))
|
||||
if (searchRequest.neededSearchId && !offer.requirements.some((x) => x._tpl === searchRequest.neededSearchId))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@ -559,7 +596,10 @@ export class RagfairOfferHelper
|
||||
return false;
|
||||
}
|
||||
|
||||
if (( item.upd.MedKit || item.upd.Repairable ) && !this.itemQualityInRange(item, searchRequest.conditionFrom, searchRequest.conditionTo))
|
||||
if (
|
||||
(item.upd.MedKit || item.upd.Repairable) &&
|
||||
!this.itemQualityInRange(item, searchRequest.conditionFrom, searchRequest.conditionTo)
|
||||
)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@ -612,10 +652,12 @@ export class RagfairOfferHelper
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!traderAssorts[offer.user.id].items.find((item) =>
|
||||
if (
|
||||
!traderAssorts[offer.user.id].items.find((item) =>
|
||||
{
|
||||
return item._id === offer.root;
|
||||
}))
|
||||
})
|
||||
)
|
||||
{
|
||||
// skip (quest) locked items
|
||||
return false;
|
||||
|
@ -17,7 +17,7 @@ export class RagfairSellHelper
|
||||
@inject("WinstonLogger") protected logger: ILogger,
|
||||
@inject("RandomUtil") protected randomUtil: RandomUtil,
|
||||
@inject("TimeUtil") protected timeUtil: TimeUtil,
|
||||
@inject("ConfigServer") protected configServer: ConfigServer
|
||||
@inject("ConfigServer") protected configServer: ConfigServer,
|
||||
)
|
||||
{
|
||||
this.ragfairConfig = this.configServer.getConfig(ConfigTypes.RAGFAIR);
|
||||
@ -30,15 +30,20 @@ export class RagfairSellHelper
|
||||
* @param qualityMultiplier Quality multipler of item being sold
|
||||
* @returns percent value
|
||||
*/
|
||||
public calculateSellChance(averageOfferPriceRub: number, playerListedPriceRub: number, qualityMultiplier: number): number
|
||||
public calculateSellChance(
|
||||
averageOfferPriceRub: number,
|
||||
playerListedPriceRub: number,
|
||||
qualityMultiplier: number,
|
||||
): number
|
||||
{
|
||||
const baseSellChancePercent = this.ragfairConfig.sell.chance.base * qualityMultiplier;
|
||||
|
||||
const listedPriceAboveAverage = playerListedPriceRub > averageOfferPriceRub;
|
||||
// Get sell chance multiplier
|
||||
const multiplier = (listedPriceAboveAverage)
|
||||
? this.ragfairConfig.sell.chance.overpriced // Player price is over average listing price
|
||||
: this.getSellMultiplierWhenPlayerPriceIsBelowAverageListingPrice(averageOfferPriceRub, playerListedPriceRub);
|
||||
const multiplier = listedPriceAboveAverage ?
|
||||
this.ragfairConfig.sell.chance.overpriced // Player price is over average listing price
|
||||
:
|
||||
this.getSellMultiplierWhenPlayerPriceIsBelowAverageListingPrice(averageOfferPriceRub, playerListedPriceRub);
|
||||
|
||||
return Math.round(baseSellChancePercent * (averageOfferPriceRub / playerListedPriceRub * multiplier));
|
||||
}
|
||||
@ -49,11 +54,14 @@ export class RagfairSellHelper
|
||||
* @param averageOfferPriceRub Price of average offer in roubles
|
||||
* @returns percent value
|
||||
*/
|
||||
protected getSellMultiplierWhenPlayerPriceIsBelowAverageListingPrice(averageOfferPriceRub: number, playerListedPriceRub: number): number
|
||||
protected getSellMultiplierWhenPlayerPriceIsBelowAverageListingPrice(
|
||||
averageOfferPriceRub: number,
|
||||
playerListedPriceRub: number,
|
||||
): number
|
||||
{
|
||||
return (playerListedPriceRub < averageOfferPriceRub)
|
||||
? this.ragfairConfig.sell.chance.underpriced
|
||||
: 1;
|
||||
return (playerListedPriceRub < averageOfferPriceRub) ?
|
||||
this.ragfairConfig.sell.chance.underpriced :
|
||||
1;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -79,7 +87,9 @@ export class RagfairSellHelper
|
||||
// Value can sometimes be NaN for whatever reason, default to base chance if that happens
|
||||
if (Number.isNaN(sellChancePercent))
|
||||
{
|
||||
this.logger.warning(`Sell chance was not a number: ${sellChancePercent}, defaulting to ${this.ragfairConfig.sell.chance.base} %`);
|
||||
this.logger.warning(
|
||||
`Sell chance was not a number: ${sellChancePercent}, defaulting to ${this.ragfairConfig.sell.chance.base} %`,
|
||||
);
|
||||
sellChancePercent = this.ragfairConfig.sell.chance.base;
|
||||
}
|
||||
|
||||
@ -97,11 +107,14 @@ export class RagfairSellHelper
|
||||
if (this.randomUtil.getChance100(sellChancePercent))
|
||||
{
|
||||
// Passed roll check, item will be sold
|
||||
sellTime += Math.max(Math.round(chance / 100 * this.ragfairConfig.sell.time.max * 60), this.ragfairConfig.sell.time.min * 60);
|
||||
sellTime += Math.max(
|
||||
Math.round(chance / 100 * this.ragfairConfig.sell.time.max * 60),
|
||||
this.ragfairConfig.sell.time.min * 60,
|
||||
);
|
||||
|
||||
result.push({
|
||||
sellTime: sellTime,
|
||||
amount: boughtAmount
|
||||
amount: boughtAmount,
|
||||
});
|
||||
|
||||
this.logger.debug(`Offer will sell at: ${new Date(sellTime * 1000).toLocaleTimeString("en-US")}`);
|
||||
|
@ -48,7 +48,7 @@ export class RagfairServerHelper
|
||||
@inject("JsonUtil") protected jsonUtil: JsonUtil,
|
||||
@inject("MailSendService") protected mailSendService: MailSendService,
|
||||
@inject("ItemFilterService") protected itemFilterService: ItemFilterService,
|
||||
@inject("ConfigServer") protected configServer: ConfigServer
|
||||
@inject("ConfigServer") protected configServer: ConfigServer,
|
||||
)
|
||||
{
|
||||
this.ragfairConfig = this.configServer.getConfig(ConfigTypes.RAGFAIR);
|
||||
@ -95,7 +95,10 @@ export class RagfairServerHelper
|
||||
}
|
||||
|
||||
// Don't include damaged ammo packs
|
||||
if (this.ragfairConfig.dynamic.blacklist.damagedAmmoPacks && itemDetails[1]._parent === BaseClasses.AMMO_BOX && itemDetails[1]._name.includes("_damaged"))
|
||||
if (
|
||||
this.ragfairConfig.dynamic.blacklist.damagedAmmoPacks && itemDetails[1]._parent === BaseClasses.AMMO_BOX &&
|
||||
itemDetails[1]._name.includes("_damaged")
|
||||
)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@ -155,7 +158,7 @@ export class RagfairServerHelper
|
||||
MessageType.MESSAGE_WITH_ITEMS,
|
||||
RagfairServerHelper.goodsReturnedTemplate,
|
||||
returnedItems,
|
||||
this.timeUtil.getHoursAsSeconds(this.questConfig.redeemTime)
|
||||
this.timeUtil.getHoursAsSeconds(this.questConfig.redeemTime),
|
||||
);
|
||||
}
|
||||
|
||||
@ -171,7 +174,10 @@ export class RagfairServerHelper
|
||||
}
|
||||
|
||||
// Item Types to return one of
|
||||
if (isWeaponPreset || this.itemHelper.isOfBaseclasses(itemDetails[1]._id, this.ragfairConfig.dynamic.showAsSingleStack))
|
||||
if (
|
||||
isWeaponPreset ||
|
||||
this.itemHelper.isOfBaseclasses(itemDetails[1]._id, this.ragfairConfig.dynamic.showAsSingleStack)
|
||||
)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
@ -185,7 +191,9 @@ export class RagfairServerHelper
|
||||
return Math.round(this.randomUtil.getInt(config.nonStackableCount.min, config.nonStackableCount.max));
|
||||
}
|
||||
|
||||
const stackPercent = Math.round(this.randomUtil.getInt(config.stackablePercent.min, config.stackablePercent.max));
|
||||
const stackPercent = Math.round(
|
||||
this.randomUtil.getInt(config.stackablePercent.min, config.stackablePercent.max),
|
||||
);
|
||||
|
||||
return Math.round((maxStackCount / 100) * stackPercent);
|
||||
}
|
||||
@ -263,7 +271,9 @@ export class RagfairServerHelper
|
||||
{
|
||||
if (this.databaseServer.getTables().globals.ItemPresets[itemId]._items[0]._tpl === item._tpl)
|
||||
{
|
||||
const presetItems = this.jsonUtil.clone(this.databaseServer.getTables().globals.ItemPresets[itemId]._items);
|
||||
const presetItems = this.jsonUtil.clone(
|
||||
this.databaseServer.getTables().globals.ItemPresets[itemId]._items,
|
||||
);
|
||||
presets.push(this.reparentPresets(item, presetItems));
|
||||
}
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ export class RagfairSortHelper
|
||||
{
|
||||
constructor(
|
||||
@inject("DatabaseServer") protected databaseServer: DatabaseServer,
|
||||
@inject("LocaleService") protected localeService: LocaleService
|
||||
@inject("LocaleService") protected localeService: LocaleService,
|
||||
)
|
||||
{}
|
||||
|
||||
@ -75,9 +75,11 @@ export class RagfairSortHelper
|
||||
const nameA = locale[`${tplA} Name`] || tplA;
|
||||
const nameB = locale[`${tplB} Name`] || tplB;
|
||||
|
||||
return (nameA < nameB)
|
||||
? -1
|
||||
: (nameA > nameB) ? 1 : 0;
|
||||
return (nameA < nameB) ?
|
||||
-1 :
|
||||
(nameA > nameB) ?
|
||||
1 :
|
||||
0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -21,7 +21,7 @@ export class RepairHelper
|
||||
@inject("JsonUtil") protected jsonUtil: JsonUtil,
|
||||
@inject("RandomUtil") protected randomUtil: RandomUtil,
|
||||
@inject("DatabaseServer") protected databaseServer: DatabaseServer,
|
||||
@inject("ConfigServer") protected configServer: ConfigServer
|
||||
@inject("ConfigServer") protected configServer: ConfigServer,
|
||||
)
|
||||
{
|
||||
this.repairConfig = this.configServer.getConfig(ConfigTypes.REPAIR);
|
||||
@ -44,7 +44,7 @@ export class RepairHelper
|
||||
amountToRepair: number,
|
||||
useRepairKit: boolean,
|
||||
traderQualityMultipler: number,
|
||||
applyMaxDurabilityDegradation = true
|
||||
applyMaxDurabilityDegradation = true,
|
||||
): void
|
||||
{
|
||||
this.logger.debug(`Adding ${amountToRepair} to ${itemToRepairDetails._name} using kit: ${useRepairKit}`);
|
||||
@ -70,16 +70,26 @@ export class RepairHelper
|
||||
// Construct object to return
|
||||
itemToRepair.upd.Repairable = {
|
||||
Durability: newCurrentDurability,
|
||||
MaxDurability: newCurrentMaxDurability
|
||||
MaxDurability: newCurrentMaxDurability,
|
||||
};
|
||||
|
||||
// when modders set the repair coefficient to 0 it means that they dont want to lose durability on items
|
||||
// the code below generates a random degradation on the weapon durability
|
||||
if (applyMaxDurabilityDegradation)
|
||||
{
|
||||
const randomisedWearAmount = (isArmor)
|
||||
? this.getRandomisedArmorRepairDegradationValue(itemToRepairDetails._props.ArmorMaterial, useRepairKit, itemCurrentMaxDurability, traderQualityMultipler)
|
||||
: this.getRandomisedWeaponRepairDegradationValue(itemToRepairDetails._props, useRepairKit, itemCurrentMaxDurability, traderQualityMultipler);
|
||||
const randomisedWearAmount = isArmor ?
|
||||
this.getRandomisedArmorRepairDegradationValue(
|
||||
itemToRepairDetails._props.ArmorMaterial,
|
||||
useRepairKit,
|
||||
itemCurrentMaxDurability,
|
||||
traderQualityMultipler,
|
||||
) :
|
||||
this.getRandomisedWeaponRepairDegradationValue(
|
||||
itemToRepairDetails._props,
|
||||
useRepairKit,
|
||||
itemCurrentMaxDurability,
|
||||
traderQualityMultipler,
|
||||
);
|
||||
|
||||
// Apply wear to durability
|
||||
itemToRepair.upd.Repairable.MaxDurability -= randomisedWearAmount;
|
||||
@ -98,17 +108,22 @@ export class RepairHelper
|
||||
}
|
||||
}
|
||||
|
||||
protected getRandomisedArmorRepairDegradationValue(armorMaterial: string, isRepairKit: boolean, armorMax: number, traderQualityMultipler: number): number
|
||||
protected getRandomisedArmorRepairDegradationValue(
|
||||
armorMaterial: string,
|
||||
isRepairKit: boolean,
|
||||
armorMax: number,
|
||||
traderQualityMultipler: number,
|
||||
): number
|
||||
{
|
||||
const armorMaterialSettings = this.databaseServer.getTables().globals.config.ArmorMaterials[armorMaterial];
|
||||
|
||||
const minMultiplier = isRepairKit
|
||||
? armorMaterialSettings.MinRepairKitDegradation
|
||||
: armorMaterialSettings.MinRepairDegradation;
|
||||
const minMultiplier = isRepairKit ?
|
||||
armorMaterialSettings.MinRepairKitDegradation :
|
||||
armorMaterialSettings.MinRepairDegradation;
|
||||
|
||||
const maxMultiplier = isRepairKit
|
||||
? armorMaterialSettings.MaxRepairKitDegradation
|
||||
: armorMaterialSettings.MaxRepairDegradation;
|
||||
const maxMultiplier = isRepairKit ?
|
||||
armorMaterialSettings.MaxRepairKitDegradation :
|
||||
armorMaterialSettings.MaxRepairDegradation;
|
||||
|
||||
const duraLossPercent = this.randomUtil.getFloat(minMultiplier, maxMultiplier);
|
||||
const duraLossMultipliedByTraderMultiplier = (duraLossPercent * armorMax) * traderQualityMultipler;
|
||||
@ -116,14 +131,19 @@ export class RepairHelper
|
||||
return Number(duraLossMultipliedByTraderMultiplier.toFixed(2));
|
||||
}
|
||||
|
||||
protected getRandomisedWeaponRepairDegradationValue(itemProps: Props, isRepairKit: boolean, weaponMax: number, traderQualityMultipler: number): number
|
||||
protected getRandomisedWeaponRepairDegradationValue(
|
||||
itemProps: Props,
|
||||
isRepairKit: boolean,
|
||||
weaponMax: number,
|
||||
traderQualityMultipler: number,
|
||||
): number
|
||||
{
|
||||
const minRepairDeg = (isRepairKit)
|
||||
? itemProps.MinRepairKitDegradation
|
||||
: itemProps.MinRepairDegradation;
|
||||
let maxRepairDeg = (isRepairKit)
|
||||
? itemProps.MaxRepairKitDegradation
|
||||
: itemProps.MaxRepairDegradation;
|
||||
const minRepairDeg = isRepairKit ?
|
||||
itemProps.MinRepairKitDegradation :
|
||||
itemProps.MinRepairDegradation;
|
||||
let maxRepairDeg = isRepairKit ?
|
||||
itemProps.MaxRepairKitDegradation :
|
||||
itemProps.MaxRepairDegradation;
|
||||
|
||||
// WORKAROUND: Some items are always 0 when repairkit is true
|
||||
if (maxRepairDeg === 0)
|
||||
@ -151,5 +171,4 @@ export class RepairHelper
|
||||
|
||||
return parentNode._id === BaseClasses.WEAPON;
|
||||
}
|
||||
|
||||
}
|
@ -15,7 +15,7 @@ export class RepeatableQuestHelper
|
||||
constructor(
|
||||
@inject("MathUtil") protected mathUtil: MathUtil,
|
||||
@inject("JsonUtil") protected jsonUtil: JsonUtil,
|
||||
@inject("ConfigServer") protected configServer: ConfigServer
|
||||
@inject("ConfigServer") protected configServer: ConfigServer,
|
||||
)
|
||||
{
|
||||
this.questConfig = this.configServer.getConfig(ConfigTypes.QUEST);
|
||||
@ -27,9 +27,14 @@ export class RepeatableQuestHelper
|
||||
* @param repeatableConfig Main repeatable config
|
||||
* @returns IEliminationConfig
|
||||
*/
|
||||
public getEliminationConfigByPmcLevel(pmcLevel: number, repeatableConfig: IRepeatableQuestConfig): IEliminationConfig
|
||||
public getEliminationConfigByPmcLevel(
|
||||
pmcLevel: number,
|
||||
repeatableConfig: IRepeatableQuestConfig,
|
||||
): IEliminationConfig
|
||||
{
|
||||
return repeatableConfig.questConfig.Elimination.find(x => pmcLevel >= x.levelRange.min && pmcLevel <= x.levelRange.max);
|
||||
return repeatableConfig.questConfig.Elimination.find((x) =>
|
||||
pmcLevel >= x.levelRange.min && pmcLevel <= x.levelRange.max
|
||||
);
|
||||
}
|
||||
|
||||
public probabilityObjectArray<K, V>(configArrayInput: ProbabilityObject<K, V>[]): ProbabilityObjectArray<K, V>
|
||||
@ -38,7 +43,9 @@ export class RepeatableQuestHelper
|
||||
const probabilityArray = new ProbabilityObjectArray<K, V>(this.mathUtil, this.jsonUtil);
|
||||
for (const configObject of configArray)
|
||||
{
|
||||
probabilityArray.push(new ProbabilityObject(configObject.key, configObject.relativeProbability, configObject.data));
|
||||
probabilityArray.push(
|
||||
new ProbabilityObject(configObject.key, configObject.relativeProbability, configObject.data),
|
||||
);
|
||||
}
|
||||
return probabilityArray;
|
||||
}
|
||||
|
@ -5,24 +5,23 @@ import { Item } from "@spt-aki/models/eft/common/tables/IItem";
|
||||
|
||||
export interface OwnerInventoryItems
|
||||
{
|
||||
from: Item[]
|
||||
to: Item[]
|
||||
sameInventory: boolean,
|
||||
isMail: boolean
|
||||
from: Item[];
|
||||
to: Item[];
|
||||
sameInventory: boolean;
|
||||
isMail: boolean;
|
||||
}
|
||||
|
||||
@injectable()
|
||||
export class SecureContainerHelper
|
||||
{
|
||||
|
||||
constructor(
|
||||
@inject("ItemHelper") protected itemHelper: ItemHelper
|
||||
@inject("ItemHelper") protected itemHelper: ItemHelper,
|
||||
)
|
||||
{}
|
||||
|
||||
public getSecureContainerItems(items: Item[]): string[]
|
||||
{
|
||||
const secureContainer = items.find(x => x.slotId === "SecuredContainer");
|
||||
const secureContainer = items.find((x) => x.slotId === "SecuredContainer");
|
||||
|
||||
// No container found, drop out
|
||||
if (!secureContainer)
|
||||
@ -33,6 +32,6 @@ export class SecureContainerHelper
|
||||
const itemsInSecureContainer = this.itemHelper.findAndReturnChildrenByItems(items, secureContainer._id);
|
||||
|
||||
// Return all items returned and exclude the secure container item itself
|
||||
return itemsInSecureContainer.filter(x => x !== secureContainer._id);
|
||||
return itemsInSecureContainer.filter((x) => x !== secureContainer._id);
|
||||
}
|
||||
}
|
||||
|
@ -34,7 +34,7 @@ export class TradeHelper
|
||||
@inject("HttpResponseUtil") protected httpResponse: HttpResponseUtil,
|
||||
@inject("InventoryHelper") protected inventoryHelper: InventoryHelper,
|
||||
@inject("RagfairServer") protected ragfairServer: RagfairServer,
|
||||
@inject("ConfigServer") protected configServer: ConfigServer
|
||||
@inject("ConfigServer") protected configServer: ConfigServer,
|
||||
)
|
||||
{
|
||||
this.traderConfig = this.configServer.getConfig(ConfigTypes.TRADER);
|
||||
@ -49,7 +49,13 @@ export class TradeHelper
|
||||
* @param upd optional item details used when buying from flea
|
||||
* @returns
|
||||
*/
|
||||
public buyItem(pmcData: IPmcData, buyRequestData: IProcessBuyTradeRequestData, sessionID: string, foundInRaid: boolean, upd: Upd): IItemEventRouterResponse
|
||||
public buyItem(
|
||||
pmcData: IPmcData,
|
||||
buyRequestData: IProcessBuyTradeRequestData,
|
||||
sessionID: string,
|
||||
foundInRaid: boolean,
|
||||
upd: Upd,
|
||||
): IItemEventRouterResponse
|
||||
{
|
||||
let output = this.eventOutputHolder.getOutput(sessionID);
|
||||
|
||||
@ -58,10 +64,10 @@ export class TradeHelper
|
||||
{
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
item_id: buyRequestData.item_id,
|
||||
count: buyRequestData.count
|
||||
}
|
||||
count: buyRequestData.count,
|
||||
},
|
||||
],
|
||||
tid: buyRequestData.tid
|
||||
tid: buyRequestData.tid,
|
||||
};
|
||||
|
||||
const callback = () =>
|
||||
@ -71,13 +77,13 @@ export class TradeHelper
|
||||
if (isRagfair)
|
||||
{
|
||||
const allOffers = this.ragfairServer.getOffers();
|
||||
const offersWithItem = allOffers.find(x => x.items[0]._id === buyRequestData.item_id);
|
||||
const offersWithItem = allOffers.find((x) => x.items[0]._id === buyRequestData.item_id);
|
||||
itemPurchased = offersWithItem.items[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
const traderAssorts = this.traderHelper.getTraderAssortsByTraderId(buyRequestData.tid).items;
|
||||
itemPurchased = traderAssorts.find(x => x._id === buyRequestData.item_id);
|
||||
itemPurchased = traderAssorts.find((x) => x._id === buyRequestData.item_id);
|
||||
}
|
||||
|
||||
// Ensure purchase does not exceed trader item limit
|
||||
@ -130,7 +136,12 @@ export class TradeHelper
|
||||
* @param sessionID Session id
|
||||
* @returns IItemEventRouterResponse
|
||||
*/
|
||||
public sellItem(profileWithItemsToSell: IPmcData, profileToReceiveMoney: IPmcData, sellRequest: IProcessSellTradeRequestData, sessionID: string): IItemEventRouterResponse
|
||||
public sellItem(
|
||||
profileWithItemsToSell: IPmcData,
|
||||
profileToReceiveMoney: IPmcData,
|
||||
sellRequest: IProcessSellTradeRequestData,
|
||||
sessionID: string,
|
||||
): IItemEventRouterResponse
|
||||
{
|
||||
let output = this.eventOutputHolder.getOutput(sessionID);
|
||||
|
||||
@ -140,7 +151,7 @@ export class TradeHelper
|
||||
const itemIdToFind = itemToBeRemoved.id.replace(/\s+/g, ""); // Strip out whitespace
|
||||
|
||||
// Find item in player inventory, or show error to player if not found
|
||||
const matchingItemInInventory = profileWithItemsToSell.Inventory.items.find(x => x._id === itemIdToFind);
|
||||
const matchingItemInInventory = profileWithItemsToSell.Inventory.items.find((x) => x._id === itemIdToFind);
|
||||
if (!matchingItemInInventory)
|
||||
{
|
||||
const errorMessage = `Unable to sell item ${itemToBeRemoved.id}, cannot be found in player inventory`;
|
||||
@ -179,7 +190,9 @@ export class TradeHelper
|
||||
{
|
||||
if ((assortBeingPurchased.upd.BuyRestrictionCurrent + count) > assortBeingPurchased.upd?.BuyRestrictionMax)
|
||||
{
|
||||
throw new Error(`Unable to purchase ${count} items, this would exceed your purchase limit of ${assortBeingPurchased.upd.BuyRestrictionMax} from the trader this refresh`);
|
||||
throw new Error(
|
||||
`Unable to purchase ${count} items, this would exceed your purchase limit of ${assortBeingPurchased.upd.BuyRestrictionMax} from the trader this refresh`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
@ -29,7 +29,7 @@ export class TraderAssortHelper
|
||||
protected mergedQuestAssorts: Record<string, Record<string, string>> = {
|
||||
started: {},
|
||||
success: {},
|
||||
fail: {}
|
||||
fail: {},
|
||||
};
|
||||
protected createdMergedQuestAssorts = false;
|
||||
|
||||
@ -46,10 +46,11 @@ export class TraderAssortHelper
|
||||
@inject("RagfairOfferGenerator") protected ragfairOfferGenerator: RagfairOfferGenerator,
|
||||
@inject("TraderAssortService") protected traderAssortService: TraderAssortService,
|
||||
@inject("LocalisationService") protected localisationService: LocalisationService,
|
||||
@inject("TraderPurchasePersisterService") protected traderPurchasePersisterService: TraderPurchasePersisterService,
|
||||
@inject("TraderPurchasePersisterService") protected traderPurchasePersisterService:
|
||||
TraderPurchasePersisterService,
|
||||
@inject("TraderHelper") protected traderHelper: TraderHelper,
|
||||
@inject("FenceService") protected fenceService: FenceService,
|
||||
@inject("ConfigServer") protected configServer: ConfigServer
|
||||
@inject("ConfigServer") protected configServer: ConfigServer,
|
||||
)
|
||||
{
|
||||
this.traderConfig = this.configServer.getConfig(ConfigTypes.TRADER);
|
||||
@ -89,21 +90,28 @@ export class TraderAssortHelper
|
||||
trader.assort.nextResupply = trader.base.nextResupply;
|
||||
|
||||
// Adjust displayed assort counts based on values stored in profile
|
||||
const assortPurchasesfromTrader = this.traderPurchasePersisterService.getProfileTraderPurchases(sessionId, traderId);
|
||||
const assortPurchasesfromTrader = this.traderPurchasePersisterService.getProfileTraderPurchases(
|
||||
sessionId,
|
||||
traderId,
|
||||
);
|
||||
for (const assortId in assortPurchasesfromTrader)
|
||||
{
|
||||
// Find assort we want to update current buy count of
|
||||
const assortToAdjust = trader.assort.items.find(x => x._id === assortId);
|
||||
const assortToAdjust = trader.assort.items.find((x) => x._id === assortId);
|
||||
if (!assortToAdjust)
|
||||
{
|
||||
this.logger.debug(`Cannot find trader: ${trader.base.nickname} assort: ${assortId} to adjust BuyRestrictionCurrent value, skipping`);
|
||||
this.logger.debug(
|
||||
`Cannot find trader: ${trader.base.nickname} assort: ${assortId} to adjust BuyRestrictionCurrent value, skipping`,
|
||||
);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!assortToAdjust.upd)
|
||||
{
|
||||
this.logger.debug(`Unable to adjust assort ${assortToAdjust._id} item: ${assortToAdjust._tpl} BuyRestrictionCurrent value, assort has an undefined upd object`);
|
||||
this.logger.debug(
|
||||
`Unable to adjust assort ${assortToAdjust._id} item: ${assortToAdjust._tpl} BuyRestrictionCurrent value, assort has an undefined upd object`,
|
||||
);
|
||||
|
||||
continue;
|
||||
}
|
||||
@ -117,7 +125,13 @@ export class TraderAssortHelper
|
||||
this.hydrateMergedQuestAssorts();
|
||||
this.createdMergedQuestAssorts = true;
|
||||
}
|
||||
trader.assort = this.assortHelper.stripLockedQuestAssort(pmcProfile, traderId, trader.assort, this.mergedQuestAssorts, flea);
|
||||
trader.assort = this.assortHelper.stripLockedQuestAssort(
|
||||
pmcProfile,
|
||||
traderId,
|
||||
trader.assort,
|
||||
this.mergedQuestAssorts,
|
||||
flea,
|
||||
);
|
||||
|
||||
// Multiply price if multiplier is other than 1
|
||||
if (this.traderConfig.traderPriceMultipler !== 1)
|
||||
@ -234,7 +248,7 @@ export class TraderAssortHelper
|
||||
barter_scheme: {},
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
loyal_level_items: {},
|
||||
nextResupply: null
|
||||
nextResupply: null,
|
||||
};
|
||||
}
|
||||
}
|
@ -42,7 +42,7 @@ export class TraderHelper
|
||||
@inject("FenceService") protected fenceService: FenceService,
|
||||
@inject("TimeUtil") protected timeUtil: TimeUtil,
|
||||
@inject("RandomUtil") protected randomUtil: RandomUtil,
|
||||
@inject("ConfigServer") protected configServer: ConfigServer
|
||||
@inject("ConfigServer") protected configServer: ConfigServer,
|
||||
)
|
||||
{
|
||||
this.traderConfig = this.configServer.getConfig(ConfigTypes.TRADER);
|
||||
@ -87,9 +87,9 @@ export class TraderHelper
|
||||
*/
|
||||
public getTraderAssortsByTraderId(traderId: string): ITraderAssort
|
||||
{
|
||||
return traderId === Traders.FENCE
|
||||
? this.fenceService.getRawFenceAssorts()
|
||||
: this.databaseServer.getTables().traders[traderId].assort;
|
||||
return traderId === Traders.FENCE ?
|
||||
this.fenceService.getRawFenceAssorts() :
|
||||
this.databaseServer.getTables().traders[traderId].assort;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -109,7 +109,7 @@ export class TraderHelper
|
||||
}
|
||||
|
||||
// Find specific assort in traders data
|
||||
const purchasedAssort = traderAssorts.items.find(x => x._id === assortId);
|
||||
const purchasedAssort = traderAssorts.items.find((x) => x._id === assortId);
|
||||
if (!purchasedAssort)
|
||||
{
|
||||
this.logger.debug(`No assort ${assortId} on trader: ${traderId} found`);
|
||||
@ -130,7 +130,9 @@ export class TraderHelper
|
||||
{
|
||||
const account = this.saveServer.getProfile(sessionID);
|
||||
const pmcData = this.profileHelper.getPmcProfile(sessionID);
|
||||
const rawProfileTemplate: ProfileTraderTemplate = this.databaseServer.getTables().templates.profiles[account.info.edition][pmcData.Info.Side.toLowerCase()].trader;
|
||||
const rawProfileTemplate: ProfileTraderTemplate =
|
||||
this.databaseServer.getTables().templates.profiles[account.info.edition][pmcData.Info.Side.toLowerCase()]
|
||||
.trader;
|
||||
|
||||
pmcData.TradersInfo[traderID] = {
|
||||
disabled: false,
|
||||
@ -138,7 +140,7 @@ export class TraderHelper
|
||||
salesSum: rawProfileTemplate.initialSalesSum,
|
||||
standing: this.getStartingStanding(traderID, rawProfileTemplate),
|
||||
nextResupply: this.databaseServer.getTables().traders[traderID].base.nextResupply,
|
||||
unlocked: this.databaseServer.getTables().traders[traderID].base.unlockedByDefault
|
||||
unlocked: this.databaseServer.getTables().traders[traderID].base.unlockedByDefault,
|
||||
};
|
||||
|
||||
if (traderID === Traders.JAEGER)
|
||||
@ -197,9 +199,9 @@ export class TraderHelper
|
||||
{
|
||||
const newStanding = currentStanding + standingToAdd;
|
||||
|
||||
return newStanding < 0
|
||||
? 0
|
||||
: newStanding;
|
||||
return newStanding < 0 ?
|
||||
0 :
|
||||
newStanding;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -224,10 +226,12 @@ export class TraderHelper
|
||||
{
|
||||
const loyalty = loyaltyLevels[level];
|
||||
|
||||
if ((loyalty.minLevel <= pmcData.Info.Level
|
||||
&& loyalty.minSalesSum <= pmcData.TradersInfo[traderID].salesSum
|
||||
&& loyalty.minStanding <= pmcData.TradersInfo[traderID].standing)
|
||||
&& targetLevel < 4)
|
||||
if (
|
||||
(loyalty.minLevel <= pmcData.Info.Level &&
|
||||
loyalty.minSalesSum <= pmcData.TradersInfo[traderID].salesSum &&
|
||||
loyalty.minStanding <= pmcData.TradersInfo[traderID].standing) &&
|
||||
targetLevel < 4
|
||||
)
|
||||
{
|
||||
// level reached
|
||||
targetLevel++;
|
||||
@ -257,15 +261,20 @@ export class TraderHelper
|
||||
*/
|
||||
public getTraderUpdateSeconds(traderId: string): number
|
||||
{
|
||||
const traderDetails = this.traderConfig.updateTime.find(x => x.traderId === traderId);
|
||||
const traderDetails = this.traderConfig.updateTime.find((x) => x.traderId === traderId);
|
||||
if (!traderDetails)
|
||||
{
|
||||
this.logger.warning(this.localisationService.getText("trader-missing_trader_details_using_default_refresh_time", {traderId: traderId, updateTime: this.traderConfig.updateTimeDefault}));
|
||||
this.logger.warning(
|
||||
this.localisationService.getText("trader-missing_trader_details_using_default_refresh_time", {
|
||||
traderId: traderId,
|
||||
updateTime: this.traderConfig.updateTimeDefault,
|
||||
}),
|
||||
);
|
||||
this.traderConfig.updateTime.push( // create temporary entry to prevent logger spam
|
||||
{
|
||||
traderId: traderId,
|
||||
seconds: this.traderConfig.updateTimeDefault
|
||||
}
|
||||
seconds: this.traderConfig.updateTimeDefault,
|
||||
},
|
||||
);
|
||||
}
|
||||
else
|
||||
@ -298,7 +307,10 @@ export class TraderHelper
|
||||
* @param newPurchaseDetails New item assort id + count
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
public addTraderPurchasesToPlayerProfile(sessionID: string, newPurchaseDetails: { items: { item_id: string; count: number; }[]; tid: string; }): void
|
||||
public addTraderPurchasesToPlayerProfile(
|
||||
sessionID: string,
|
||||
newPurchaseDetails: {items: {item_id: string; count: number;}[]; tid: string;},
|
||||
): void
|
||||
{
|
||||
const profile = this.profileHelper.getFullProfile(sessionID);
|
||||
const traderId = newPurchaseDetails.tid;
|
||||
@ -320,10 +332,9 @@ export class TraderHelper
|
||||
const currentTime = this.timeUtil.getTimestamp();
|
||||
if (!profile.traderPurchases[traderId][purchasedItem.item_id])
|
||||
{
|
||||
profile.traderPurchases[traderId][purchasedItem.item_id] =
|
||||
{
|
||||
profile.traderPurchases[traderId][purchasedItem.item_id] = {
|
||||
count: purchasedItem.count,
|
||||
purchaseTimestamp: currentTime
|
||||
purchaseTimestamp: currentTime,
|
||||
};
|
||||
|
||||
continue;
|
||||
@ -369,15 +380,15 @@ export class TraderHelper
|
||||
}
|
||||
|
||||
// Get all item assorts that have parentid of hideout (base item and not a mod of other item)
|
||||
for (const item of traderAssorts.items.filter(x => x.parentId === "hideout"))
|
||||
for (const item of traderAssorts.items.filter((x) => x.parentId === "hideout"))
|
||||
{
|
||||
// Get barter scheme (contains cost of item)
|
||||
const barterScheme = traderAssorts.barter_scheme[item._id][0][0];
|
||||
|
||||
// Convert into roubles
|
||||
const roubleAmount = barterScheme._tpl === Money.ROUBLES
|
||||
? barterScheme.count
|
||||
: this.handbookHelper.inRUB(barterScheme.count, barterScheme._tpl);
|
||||
const roubleAmount = barterScheme._tpl === Money.ROUBLES ?
|
||||
barterScheme.count :
|
||||
this.handbookHelper.inRUB(barterScheme.count, barterScheme._tpl);
|
||||
|
||||
// Existing price smaller in dict than current iteration, overwrite
|
||||
if (this.highestTraderPriceItems[item._tpl] ?? 0 < roubleAmount)
|
||||
@ -409,7 +420,6 @@ export class TraderHelper
|
||||
return this.highestTraderBuyPriceItems[tpl];
|
||||
}
|
||||
|
||||
|
||||
// Find highest trader price for item
|
||||
for (const traderName in Traders)
|
||||
{
|
||||
@ -423,7 +433,9 @@ export class TraderHelper
|
||||
const traderBuyBackPricePercent = relevantLoyaltyData.buy_price_coef;
|
||||
|
||||
const itemHandbookPrice = this.handbookHelper.getTemplatePrice(tpl);
|
||||
const priceTraderBuysItemAt = Math.round(this.randomUtil.getPercentOfValue(traderBuyBackPricePercent, itemHandbookPrice));
|
||||
const priceTraderBuysItemAt = Math.round(
|
||||
this.randomUtil.getPercentOfValue(traderBuyBackPricePercent, itemHandbookPrice),
|
||||
);
|
||||
|
||||
// Set new item to 1 rouble as default
|
||||
if (!this.highestTraderBuyPriceItems[tpl])
|
||||
@ -449,7 +461,7 @@ export class TraderHelper
|
||||
*/
|
||||
public getTraderById(traderId: string): Traders
|
||||
{
|
||||
const keys = Object.keys(Traders).filter(x => Traders[x] === traderId);
|
||||
const keys = Object.keys(Traders).filter((x) => Traders[x] === traderId);
|
||||
|
||||
if (keys.length === 0)
|
||||
{
|
||||
|
@ -5,6 +5,6 @@ export class UtilityHelper
|
||||
{
|
||||
public arrayIntersect<T>(a: T[], b: T[]): T[]
|
||||
{
|
||||
return a.filter(x => b.includes(x));
|
||||
return a.filter((x) => b.includes(x));
|
||||
}
|
||||
}
|
@ -79,7 +79,7 @@ export class WeightedRandomHelper
|
||||
{
|
||||
return {
|
||||
item: items[itemIndex],
|
||||
index: itemIndex
|
||||
index: itemIndex,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user