2023-03-03 16:23:46 +01:00
|
|
|
import { inject, injectable } from "tsyringe";
|
|
|
|
|
2023-10-19 19:21:17 +02:00
|
|
|
import { DialogueHelper } from "@spt-aki/helpers/DialogueHelper";
|
|
|
|
import { ItemHelper } from "@spt-aki/helpers/ItemHelper";
|
|
|
|
import { ProfileHelper } from "@spt-aki/helpers/ProfileHelper";
|
|
|
|
import { TraderHelper } from "@spt-aki/helpers/TraderHelper";
|
|
|
|
import { Item } from "@spt-aki/models/eft/common/tables/IItem";
|
|
|
|
import { ITemplateItem } from "@spt-aki/models/eft/common/tables/ITemplateItem";
|
|
|
|
import { BaseClasses } from "@spt-aki/models/enums/BaseClasses";
|
|
|
|
import { ConfigTypes } from "@spt-aki/models/enums/ConfigTypes";
|
|
|
|
import { MemberCategory } from "@spt-aki/models/enums/MemberCategory";
|
|
|
|
import { MessageType } from "@spt-aki/models/enums/MessageType";
|
|
|
|
import { Traders } from "@spt-aki/models/enums/Traders";
|
|
|
|
import { IQuestConfig } from "@spt-aki/models/spt/config/IQuestConfig";
|
|
|
|
import { IRagfairConfig } from "@spt-aki/models/spt/config/IRagfairConfig";
|
2023-12-14 16:47:01 +01:00
|
|
|
import { ILogger } from "@spt-aki/models/spt/utils/ILogger";
|
2023-10-19 19:21:17 +02:00
|
|
|
import { ConfigServer } from "@spt-aki/servers/ConfigServer";
|
|
|
|
import { DatabaseServer } from "@spt-aki/servers/DatabaseServer";
|
|
|
|
import { SaveServer } from "@spt-aki/servers/SaveServer";
|
|
|
|
import { ItemFilterService } from "@spt-aki/services/ItemFilterService";
|
|
|
|
import { LocaleService } from "@spt-aki/services/LocaleService";
|
|
|
|
import { MailSendService } from "@spt-aki/services/MailSendService";
|
|
|
|
import { HashUtil } from "@spt-aki/utils/HashUtil";
|
|
|
|
import { JsonUtil } from "@spt-aki/utils/JsonUtil";
|
|
|
|
import { RandomUtil } from "@spt-aki/utils/RandomUtil";
|
|
|
|
import { TimeUtil } from "@spt-aki/utils/TimeUtil";
|
2023-03-03 16:23:46 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Helper class for common ragfair server actions
|
|
|
|
*/
|
|
|
|
@injectable()
|
|
|
|
export class RagfairServerHelper
|
|
|
|
{
|
|
|
|
protected ragfairConfig: IRagfairConfig;
|
|
|
|
protected questConfig: IQuestConfig;
|
2023-05-29 17:05:19 +02:00
|
|
|
protected static goodsReturnedTemplate = "5bdabfe486f7743e1665df6e 0"; // Your item was not sold
|
2023-03-03 16:23:46 +01:00
|
|
|
|
|
|
|
constructor(
|
2023-12-14 16:47:01 +01:00
|
|
|
@inject("WinstonLogger") protected logger: ILogger,
|
2023-03-03 16:23:46 +01:00
|
|
|
@inject("RandomUtil") protected randomUtil: RandomUtil,
|
|
|
|
@inject("HashUtil") protected hashUtil: HashUtil,
|
2023-07-22 14:37:40 +02:00
|
|
|
@inject("TimeUtil") protected timeUtil: TimeUtil,
|
2023-03-03 16:23:46 +01:00
|
|
|
@inject("SaveServer") protected saveServer: SaveServer,
|
|
|
|
@inject("DatabaseServer") protected databaseServer: DatabaseServer,
|
|
|
|
@inject("ProfileHelper") protected profileHelper: ProfileHelper,
|
|
|
|
@inject("ItemHelper") protected itemHelper: ItemHelper,
|
|
|
|
@inject("LocaleService") protected localeService: LocaleService,
|
|
|
|
@inject("DialogueHelper") protected dialogueHelper: DialogueHelper,
|
2023-07-23 11:35:42 +02:00
|
|
|
@inject("TraderHelper") protected traderHelper: TraderHelper,
|
2023-03-03 16:23:46 +01:00
|
|
|
@inject("JsonUtil") protected jsonUtil: JsonUtil,
|
2023-07-22 14:13:01 +02:00
|
|
|
@inject("MailSendService") protected mailSendService: MailSendService,
|
2023-03-03 16:23:46 +01:00
|
|
|
@inject("ItemFilterService") protected itemFilterService: ItemFilterService,
|
2023-11-16 22:42:06 +01:00
|
|
|
@inject("ConfigServer") protected configServer: ConfigServer,
|
2023-03-03 16:23:46 +01:00
|
|
|
)
|
|
|
|
{
|
|
|
|
this.ragfairConfig = this.configServer.getConfig(ConfigTypes.RAGFAIR);
|
|
|
|
this.questConfig = this.configServer.getConfig(ConfigTypes.QUEST);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Is item valid / on blacklist / quest item
|
2023-11-16 22:42:06 +01:00
|
|
|
* @param itemDetails
|
2023-03-03 16:23:46 +01:00
|
|
|
* @returns boolean
|
|
|
|
*/
|
|
|
|
public isItemValidRagfairItem(itemDetails: [boolean, ITemplateItem]): boolean
|
|
|
|
{
|
|
|
|
const blacklistConfig = this.ragfairConfig.dynamic.blacklist;
|
|
|
|
|
|
|
|
// Skip invalid items
|
|
|
|
if (!itemDetails[0])
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Skip blacklisted items
|
|
|
|
if (this.itemFilterService.isItemBlacklisted(itemDetails[1]._id))
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Skip bsg blacklisted items
|
|
|
|
if (blacklistConfig.enableBsgList && !itemDetails[1]._props.CanSellOnRagfair)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Skip custom blacklisted items
|
2023-07-22 14:13:01 +02:00
|
|
|
if (this.isItemOnCustomFleaBlacklist(itemDetails[1]._id))
|
2023-03-03 16:23:46 +01:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Skip quest items
|
|
|
|
if (blacklistConfig.enableQuestList && this.itemHelper.isQuestItem(itemDetails[1]._id))
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2023-06-30 15:00:08 +02:00
|
|
|
// Don't include damaged ammo packs
|
2023-11-16 22:42:06 +01:00
|
|
|
if (
|
|
|
|
this.ragfairConfig.dynamic.blacklist.damagedAmmoPacks && itemDetails[1]._parent === BaseClasses.AMMO_BOX
|
|
|
|
&& itemDetails[1]._name.includes("_damaged")
|
|
|
|
)
|
2023-06-30 15:00:08 +02:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2023-03-03 16:23:46 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2023-07-22 14:13:01 +02:00
|
|
|
/**
|
|
|
|
* Is supplied item tpl on the ragfair custom blacklist from configs/ragfair.json/dynamic
|
|
|
|
* @param itemTemplateId Item tpl to check is blacklisted
|
|
|
|
* @returns True if its blacklsited
|
|
|
|
*/
|
2023-07-22 14:14:40 +02:00
|
|
|
protected isItemOnCustomFleaBlacklist(itemTemplateId: string): boolean
|
2023-03-03 16:23:46 +01:00
|
|
|
{
|
|
|
|
if (!this.itemHelper.isValidItem(itemTemplateId))
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return this.ragfairConfig.dynamic.blacklist.custom.includes(itemTemplateId);
|
|
|
|
}
|
|
|
|
|
2023-07-22 14:13:01 +02:00
|
|
|
/**
|
|
|
|
* is supplied id a trader
|
2023-11-16 22:42:06 +01:00
|
|
|
* @param traderId
|
2023-07-22 14:13:01 +02:00
|
|
|
* @returns True if id was a trader
|
|
|
|
*/
|
|
|
|
public isTrader(traderId: string): boolean
|
2023-03-03 16:23:46 +01:00
|
|
|
{
|
2023-07-22 14:13:01 +02:00
|
|
|
return traderId in this.databaseServer.getTables().traders;
|
2023-03-03 16:23:46 +01:00
|
|
|
}
|
|
|
|
|
2023-07-22 14:13:01 +02:00
|
|
|
/**
|
|
|
|
* Is this user id the logged in player
|
|
|
|
* @param userId Id to test
|
|
|
|
* @returns True is the current player
|
|
|
|
*/
|
|
|
|
public isPlayer(userId: string): boolean
|
2023-03-03 16:23:46 +01:00
|
|
|
{
|
2023-07-22 14:13:01 +02:00
|
|
|
if (this.profileHelper.getPmcProfile(userId) !== undefined)
|
2023-03-03 16:23:46 +01:00
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2023-07-22 14:13:01 +02:00
|
|
|
/**
|
|
|
|
* Send items back to player
|
|
|
|
* @param sessionID Player to send items to
|
|
|
|
* @param returnedItems Items to send to player
|
|
|
|
*/
|
|
|
|
public returnItems(sessionID: string, returnedItems: Item[]): void
|
2023-03-03 16:23:46 +01:00
|
|
|
{
|
2023-07-22 14:13:01 +02:00
|
|
|
this.mailSendService.sendLocalisedNpcMessageToPlayer(
|
|
|
|
sessionID,
|
2023-07-23 11:35:42 +02:00
|
|
|
this.traderHelper.getTraderById(Traders.RAGMAN),
|
2023-07-22 14:13:01 +02:00
|
|
|
MessageType.MESSAGE_WITH_ITEMS,
|
2023-07-23 11:35:42 +02:00
|
|
|
RagfairServerHelper.goodsReturnedTemplate,
|
2023-07-22 14:13:01 +02:00
|
|
|
returnedItems,
|
2023-11-16 22:42:06 +01:00
|
|
|
this.timeUtil.getHoursAsSeconds(this.questConfig.redeemTime),
|
2023-07-22 14:13:01 +02:00
|
|
|
);
|
2023-03-03 16:23:46 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
public calculateDynamicStackCount(tplId: string, isWeaponPreset: boolean): number
|
|
|
|
{
|
|
|
|
const config = this.ragfairConfig.dynamic;
|
|
|
|
|
|
|
|
// Lookup item details - check if item not found
|
|
|
|
const itemDetails = this.itemHelper.getItem(tplId);
|
|
|
|
if (!itemDetails[0])
|
|
|
|
{
|
|
|
|
throw new Error(`Item with tpl ${tplId} not found. Unable to generate a dynamic stack count.`);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Item Types to return one of
|
2023-11-16 22:42:06 +01:00
|
|
|
if (
|
|
|
|
isWeaponPreset
|
|
|
|
|| this.itemHelper.isOfBaseclasses(itemDetails[1]._id, this.ragfairConfig.dynamic.showAsSingleStack)
|
|
|
|
)
|
2023-03-03 16:23:46 +01:00
|
|
|
{
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get max stack count
|
|
|
|
const maxStackCount = itemDetails[1]._props.StackMaxSize;
|
|
|
|
|
|
|
|
// non-stackable - use different values to calculate stack size
|
|
|
|
if (!maxStackCount || maxStackCount === 1)
|
|
|
|
{
|
|
|
|
return Math.round(this.randomUtil.getInt(config.nonStackableCount.min, config.nonStackableCount.max));
|
|
|
|
}
|
|
|
|
|
2023-11-16 22:42:06 +01:00
|
|
|
const stackPercent = Math.round(
|
|
|
|
this.randomUtil.getInt(config.stackablePercent.min, config.stackablePercent.max),
|
|
|
|
);
|
2023-03-03 16:23:46 +01:00
|
|
|
|
|
|
|
return Math.round((maxStackCount / 100) * stackPercent);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Choose a currency at random with bias
|
|
|
|
* @returns currency tpl
|
|
|
|
*/
|
|
|
|
public getDynamicOfferCurrency(): string
|
|
|
|
{
|
|
|
|
const currencies = this.ragfairConfig.dynamic.currencies;
|
|
|
|
const bias: string[] = [];
|
|
|
|
|
|
|
|
for (const item in currencies)
|
|
|
|
{
|
|
|
|
for (let i = 0; i < currencies[item]; i++)
|
|
|
|
{
|
|
|
|
bias.push(item);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return bias[Math.floor(Math.random() * bias.length)];
|
|
|
|
}
|
|
|
|
|
|
|
|
public getMemberType(userID: string): MemberCategory
|
|
|
|
{
|
|
|
|
if (this.isPlayer(userID))
|
|
|
|
{
|
|
|
|
// player offer
|
|
|
|
return this.saveServer.getProfile(userID).characters.pmc.Info.AccountType;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this.isTrader(userID))
|
|
|
|
{
|
|
|
|
// trader offer
|
|
|
|
return MemberCategory.TRADER;
|
|
|
|
}
|
|
|
|
|
|
|
|
// generated offer
|
|
|
|
return MemberCategory.DEFAULT;
|
|
|
|
}
|
|
|
|
|
2023-12-14 16:47:01 +01:00
|
|
|
/**
|
|
|
|
* Get a player or traders nickname from their profile by their user id
|
|
|
|
* @param userID Sessionid/userid
|
|
|
|
* @returns Nickname of individual
|
|
|
|
*/
|
2023-03-03 16:23:46 +01:00
|
|
|
public getNickname(userID: string): string
|
|
|
|
{
|
|
|
|
if (this.isPlayer(userID))
|
|
|
|
{
|
|
|
|
// player offer
|
|
|
|
return this.saveServer.getProfile(userID).characters.pmc.Info.Nickname;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this.isTrader(userID))
|
|
|
|
{
|
|
|
|
// trader offer
|
|
|
|
return this.databaseServer.getTables().traders[userID].base.nickname;
|
|
|
|
}
|
|
|
|
|
|
|
|
// generated offer
|
2023-04-23 00:41:04 +02:00
|
|
|
// recurivse if name is longer than max characters allowed (15 characters)
|
2023-03-03 16:23:46 +01:00
|
|
|
const type = (this.randomUtil.getInt(0, 1) === 0) ? "usec" : "bear";
|
|
|
|
const name = this.randomUtil.getStringArrayValue(this.databaseServer.getTables().bots.types[type].firstName);
|
|
|
|
return (name.length > 15) ? this.getNickname(userID) : name;
|
|
|
|
}
|
|
|
|
|
2023-12-14 16:47:01 +01:00
|
|
|
/**
|
|
|
|
* Given a preset id from globals.json, return an array of items[] with unique ids
|
|
|
|
* @param item Preset item
|
|
|
|
* @returns Array of weapon and its children
|
|
|
|
*/
|
|
|
|
public getPresetItems(item: Item): Item[]
|
2023-03-03 16:23:46 +01:00
|
|
|
{
|
|
|
|
const preset = this.jsonUtil.clone(this.databaseServer.getTables().globals.ItemPresets[item._id]._items);
|
|
|
|
return this.reparentPresets(item, preset);
|
|
|
|
}
|
|
|
|
|
2023-12-14 16:47:01 +01:00
|
|
|
/**
|
|
|
|
* Possible bug, returns all items associated with an items tpl, could be multiple presets from globals.json
|
|
|
|
* @param item Preset item
|
|
|
|
* @returns
|
|
|
|
*/
|
2023-03-03 16:23:46 +01:00
|
|
|
public getPresetItemsByTpl(item: Item): Item[]
|
|
|
|
{
|
|
|
|
const presets = [];
|
|
|
|
for (const itemId in this.databaseServer.getTables().globals.ItemPresets)
|
|
|
|
{
|
|
|
|
if (this.databaseServer.getTables().globals.ItemPresets[itemId]._items[0]._tpl === item._tpl)
|
|
|
|
{
|
2023-11-16 22:42:06 +01:00
|
|
|
const presetItems = this.jsonUtil.clone(
|
|
|
|
this.databaseServer.getTables().globals.ItemPresets[itemId]._items,
|
|
|
|
);
|
2023-03-03 16:23:46 +01:00
|
|
|
presets.push(this.reparentPresets(item, presetItems));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return presets;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2023-12-14 16:47:01 +01:00
|
|
|
* Generate new unique ids for child items while preserving hierarchy
|
|
|
|
* @param rootItem Base/primary item of preset
|
|
|
|
* @param preset Primary item + children of primary item
|
2023-03-03 16:23:46 +01:00
|
|
|
* @returns Item array with new IDs
|
|
|
|
*/
|
2023-12-14 16:47:01 +01:00
|
|
|
public reparentPresets(rootItem: Item, preset: Item[]): Item[]
|
2023-03-03 16:23:46 +01:00
|
|
|
{
|
|
|
|
const oldRootId = preset[0]._id;
|
|
|
|
const idMappings = {};
|
|
|
|
|
2023-12-14 16:47:01 +01:00
|
|
|
idMappings[oldRootId] = rootItem._id;
|
2023-03-03 16:23:46 +01:00
|
|
|
|
|
|
|
for (const mod of preset)
|
|
|
|
{
|
|
|
|
if (idMappings[mod._id] === undefined)
|
|
|
|
{
|
|
|
|
idMappings[mod._id] = this.hashUtil.generate();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mod.parentId !== undefined && idMappings[mod.parentId] === undefined)
|
|
|
|
{
|
|
|
|
idMappings[mod.parentId] = this.hashUtil.generate();
|
|
|
|
}
|
|
|
|
|
2023-11-16 22:42:06 +01:00
|
|
|
mod._id = idMappings[mod._id];
|
2023-03-03 16:23:46 +01:00
|
|
|
|
|
|
|
if (mod.parentId !== undefined)
|
|
|
|
{
|
2023-11-16 22:42:06 +01:00
|
|
|
mod.parentId = idMappings[mod.parentId];
|
2023-03-03 16:23:46 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-12-14 16:47:01 +01:00
|
|
|
// Force item's details into first location of presetItems
|
|
|
|
if (preset[0]._tpl !== rootItem._tpl)
|
|
|
|
{
|
|
|
|
this.logger.warning(`Reassigning root item from ${preset[0]._tpl} to ${rootItem._tpl}`);
|
|
|
|
}
|
|
|
|
|
|
|
|
preset[0] = rootItem;
|
2023-03-03 16:23:46 +01:00
|
|
|
|
|
|
|
return preset;
|
|
|
|
}
|
2023-11-16 22:42:06 +01:00
|
|
|
}
|