2024-05-21 17:59:04 +00:00
|
|
|
import { RagfairAssortGenerator } from "@spt/generators/RagfairAssortGenerator";
|
2024-05-22 13:26:35 +01:00
|
|
|
import { BotHelper } from "@spt/helpers/BotHelper";
|
2024-05-21 17:59:04 +00:00
|
|
|
import { HandbookHelper } from "@spt/helpers/HandbookHelper";
|
|
|
|
import { ItemHelper } from "@spt/helpers/ItemHelper";
|
|
|
|
import { PaymentHelper } from "@spt/helpers/PaymentHelper";
|
|
|
|
import { PresetHelper } from "@spt/helpers/PresetHelper";
|
2024-05-22 13:26:35 +01:00
|
|
|
import { ProfileHelper } from "@spt/helpers/ProfileHelper";
|
2024-05-21 17:59:04 +00:00
|
|
|
import { RagfairServerHelper } from "@spt/helpers/RagfairServerHelper";
|
2024-09-24 12:47:29 +01:00
|
|
|
import { IItem } from "@spt/models/eft/common/tables/IItem";
|
2024-05-21 17:59:04 +00:00
|
|
|
import { ITemplateItem } from "@spt/models/eft/common/tables/ITemplateItem";
|
|
|
|
import { IBarterScheme } from "@spt/models/eft/common/tables/ITrader";
|
2024-10-19 12:43:38 +01:00
|
|
|
import { IOfferRequirement, IRagfairOffer, IRagfairOfferUser } from "@spt/models/eft/ragfair/IRagfairOffer";
|
2024-05-21 17:59:04 +00:00
|
|
|
import { BaseClasses } from "@spt/models/enums/BaseClasses";
|
|
|
|
import { ConfigTypes } from "@spt/models/enums/ConfigTypes";
|
|
|
|
import { MemberCategory } from "@spt/models/enums/MemberCategory";
|
|
|
|
import { Money } from "@spt/models/enums/Money";
|
2024-10-18 11:39:51 +01:00
|
|
|
import { IBotConfig } from "@spt/models/spt/config/IBotConfig";
|
2024-02-08 16:53:14 +00:00
|
|
|
import {
|
|
|
|
Condition,
|
|
|
|
IArmorPlateBlacklistSettings,
|
2024-10-19 10:58:25 +01:00
|
|
|
IBarterDetails,
|
2024-10-19 12:43:38 +01:00
|
|
|
IDynamic,
|
2024-02-08 16:53:14 +00:00
|
|
|
IRagfairConfig,
|
2024-05-21 17:59:04 +00:00
|
|
|
} from "@spt/models/spt/config/IRagfairConfig";
|
2024-10-19 10:58:25 +01:00
|
|
|
import { ITplWithFleaPrice } from "@spt/models/spt/ragfair/ITplWithFleaPrice";
|
2024-05-21 17:59:04 +00:00
|
|
|
import { ILogger } from "@spt/models/spt/utils/ILogger";
|
|
|
|
import { ConfigServer } from "@spt/servers/ConfigServer";
|
|
|
|
import { SaveServer } from "@spt/servers/SaveServer";
|
2024-05-28 22:24:52 +01:00
|
|
|
import { DatabaseService } from "@spt/services/DatabaseService";
|
2024-05-21 17:59:04 +00:00
|
|
|
import { FenceService } from "@spt/services/FenceService";
|
|
|
|
import { LocalisationService } from "@spt/services/LocalisationService";
|
|
|
|
import { RagfairOfferService } from "@spt/services/RagfairOfferService";
|
|
|
|
import { RagfairPriceService } from "@spt/services/RagfairPriceService";
|
|
|
|
import { HashUtil } from "@spt/utils/HashUtil";
|
|
|
|
import { RandomUtil } from "@spt/utils/RandomUtil";
|
|
|
|
import { TimeUtil } from "@spt/utils/TimeUtil";
|
2024-07-23 11:12:53 -04:00
|
|
|
import { ICloner } from "@spt/utils/cloners/ICloner";
|
|
|
|
import { inject, injectable } from "tsyringe";
|
2023-03-03 15:23:46 +00:00
|
|
|
|
|
|
|
@injectable()
|
2024-07-23 11:12:53 -04:00
|
|
|
export class RagfairOfferGenerator {
|
2023-03-03 15:23:46 +00:00
|
|
|
protected ragfairConfig: IRagfairConfig;
|
2024-10-18 11:39:51 +01:00
|
|
|
protected botConfig: IBotConfig;
|
2024-07-23 11:12:53 -04:00
|
|
|
protected allowedFleaPriceItemsForBarter: { tpl: string; price: number }[];
|
2023-03-03 15:23:46 +00:00
|
|
|
|
2023-12-19 23:52:39 +00:00
|
|
|
/** Internal counter to ensure each offer created has a unique value for its intId property */
|
|
|
|
protected offerCounter = 0;
|
|
|
|
|
2023-03-03 15:23:46 +00:00
|
|
|
constructor(
|
2024-05-28 14:04:20 +00:00
|
|
|
@inject("PrimaryLogger") protected logger: ILogger,
|
2023-03-03 15:23:46 +00:00
|
|
|
@inject("HashUtil") protected hashUtil: HashUtil,
|
|
|
|
@inject("RandomUtil") protected randomUtil: RandomUtil,
|
|
|
|
@inject("TimeUtil") protected timeUtil: TimeUtil,
|
2024-05-28 22:24:52 +01:00
|
|
|
@inject("DatabaseService") protected databaseService: DatabaseService,
|
2023-03-03 15:23:46 +00:00
|
|
|
@inject("RagfairServerHelper") protected ragfairServerHelper: RagfairServerHelper,
|
2024-05-22 13:26:35 +01:00
|
|
|
@inject("ProfileHelper") protected profileHelper: ProfileHelper,
|
2023-03-03 15:23:46 +00:00
|
|
|
@inject("HandbookHelper") protected handbookHelper: HandbookHelper,
|
2024-05-22 13:26:35 +01:00
|
|
|
@inject("BotHelper") protected botHelper: BotHelper,
|
2023-03-03 15:23:46 +00:00
|
|
|
@inject("SaveServer") protected saveServer: SaveServer,
|
|
|
|
@inject("PresetHelper") protected presetHelper: PresetHelper,
|
|
|
|
@inject("RagfairAssortGenerator") protected ragfairAssortGenerator: RagfairAssortGenerator,
|
|
|
|
@inject("RagfairOfferService") protected ragfairOfferService: RagfairOfferService,
|
|
|
|
@inject("RagfairPriceService") protected ragfairPriceService: RagfairPriceService,
|
|
|
|
@inject("LocalisationService") protected localisationService: LocalisationService,
|
|
|
|
@inject("PaymentHelper") protected paymentHelper: PaymentHelper,
|
|
|
|
@inject("FenceService") protected fenceService: FenceService,
|
|
|
|
@inject("ItemHelper") protected itemHelper: ItemHelper,
|
2023-11-16 21:42:06 +00:00
|
|
|
@inject("ConfigServer") protected configServer: ConfigServer,
|
2024-05-28 14:04:20 +00:00
|
|
|
@inject("PrimaryCloner") protected cloner: ICloner,
|
2024-07-23 11:12:53 -04:00
|
|
|
) {
|
2023-03-03 15:23:46 +00:00
|
|
|
this.ragfairConfig = this.configServer.getConfig(ConfigTypes.RAGFAIR);
|
2024-10-18 11:39:51 +01:00
|
|
|
this.botConfig = this.configServer.getConfig(ConfigTypes.BOT);
|
2023-03-03 15:23:46 +00:00
|
|
|
}
|
|
|
|
|
2023-07-25 14:04:21 +01:00
|
|
|
/**
|
|
|
|
* Create a flea offer and store it in the Ragfair server offers array
|
|
|
|
* @param userID Owner of the offer
|
|
|
|
* @param time Time offer is listed at
|
|
|
|
* @param items Items in the offer
|
|
|
|
* @param barterScheme Cost of item (currency or barter)
|
|
|
|
* @param loyalLevel Loyalty level needed to buy item
|
2023-10-10 11:03:20 +00:00
|
|
|
* @param sellInOnePiece Flags sellInOnePiece to be true
|
2024-05-22 12:51:04 +01:00
|
|
|
* @returns Created flea offer
|
2023-07-25 14:04:21 +01:00
|
|
|
*/
|
2024-05-22 12:51:04 +01:00
|
|
|
public createAndAddFleaOffer(
|
2023-11-16 21:42:06 +00:00
|
|
|
userID: string,
|
|
|
|
time: number,
|
2024-09-24 12:47:29 +01:00
|
|
|
items: IItem[],
|
2023-11-16 21:42:06 +00:00
|
|
|
barterScheme: IBarterScheme[],
|
|
|
|
loyalLevel: number,
|
|
|
|
sellInOnePiece = false,
|
2024-07-23 11:12:53 -04:00
|
|
|
): IRagfairOffer {
|
2023-10-10 11:03:20 +00:00
|
|
|
const offer = this.createOffer(userID, time, items, barterScheme, loyalLevel, sellInOnePiece);
|
2023-07-25 14:04:21 +01:00
|
|
|
this.ragfairOfferService.addOffer(offer);
|
|
|
|
|
|
|
|
return offer;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Create an offer object ready to send to ragfairOfferService.addOffer()
|
|
|
|
* @param userID Owner of the offer
|
|
|
|
* @param time Time offer is listed at
|
|
|
|
* @param items Items in the offer
|
|
|
|
* @param barterScheme Cost of item (currency or barter)
|
|
|
|
* @param loyalLevel Loyalty level needed to buy item
|
2024-07-19 13:45:34 +01:00
|
|
|
* @param isPackOffer Is offer being created flaged as a pack
|
2023-07-25 14:04:21 +01:00
|
|
|
* @returns IRagfairOffer
|
|
|
|
*/
|
2023-11-16 21:42:06 +00:00
|
|
|
protected createOffer(
|
|
|
|
userID: string,
|
|
|
|
time: number,
|
2024-09-24 12:47:29 +01:00
|
|
|
items: IItem[],
|
2023-11-16 21:42:06 +00:00
|
|
|
barterScheme: IBarterScheme[],
|
|
|
|
loyalLevel: number,
|
2024-07-19 13:45:34 +01:00
|
|
|
isPackOffer = false,
|
2024-07-23 11:12:53 -04:00
|
|
|
): IRagfairOffer {
|
2023-03-03 15:23:46 +00:00
|
|
|
const isTrader = this.ragfairServerHelper.isTrader(userID);
|
|
|
|
|
2024-07-23 11:12:53 -04:00
|
|
|
const offerRequirements = barterScheme.map((barter) => {
|
2024-10-19 12:43:38 +01:00
|
|
|
const offerRequirement: IOfferRequirement = {
|
2024-07-20 21:45:42 +00:00
|
|
|
_tpl: barter._tpl,
|
|
|
|
count: +barter.count.toFixed(2),
|
|
|
|
onlyFunctional: barter.onlyFunctional ?? false,
|
|
|
|
};
|
|
|
|
|
|
|
|
// Dogtags define level and side
|
2024-07-23 11:12:53 -04:00
|
|
|
if (barter.level !== undefined) {
|
2024-07-20 21:45:42 +00:00
|
|
|
offerRequirement.level = barter.level;
|
|
|
|
offerRequirement.side = barter.side;
|
|
|
|
}
|
|
|
|
|
|
|
|
return offerRequirement;
|
|
|
|
});
|
2023-03-03 15:23:46 +00:00
|
|
|
|
2024-06-30 21:39:58 +01:00
|
|
|
// Clone to avoid modifying original array
|
2024-05-13 17:58:17 +00:00
|
|
|
const itemsClone = this.cloner.clone(items);
|
2024-07-19 13:45:34 +01:00
|
|
|
const itemStackCount = itemsClone[0].upd?.StackObjectsCount ?? 1;
|
2024-01-16 10:26:48 +00:00
|
|
|
|
2024-06-30 21:39:58 +01:00
|
|
|
// Hydrate ammo boxes with cartridges + ensure only 1 item is present (ammo box)
|
|
|
|
// On offer refresh dont re-add cartridges to ammo box that already has cartridges
|
2024-07-23 11:12:53 -04:00
|
|
|
if (this.itemHelper.isOfBaseclass(itemsClone[0]._tpl, BaseClasses.AMMO_BOX) && itemsClone.length === 1) {
|
2024-06-30 21:39:58 +01:00
|
|
|
this.itemHelper.addCartridgesToAmmoBox(itemsClone, this.itemHelper.getItem(items[0]._tpl)[1]);
|
2024-01-16 10:26:48 +00:00
|
|
|
}
|
|
|
|
|
2024-07-19 13:45:34 +01:00
|
|
|
const roubleListingPrice = Math.round(this.convertOfferRequirementsIntoRoubles(offerRequirements));
|
|
|
|
const singleItemListingPrice = isPackOffer ? roubleListingPrice / itemStackCount : roubleListingPrice;
|
2023-03-03 15:23:46 +00:00
|
|
|
|
|
|
|
const offer: IRagfairOffer = {
|
2023-10-10 11:03:20 +00:00
|
|
|
_id: this.hashUtil.generate(),
|
2023-12-19 23:52:39 +00:00
|
|
|
intId: this.offerCounter,
|
2024-05-22 13:26:35 +01:00
|
|
|
user: this.createUserDataForFleaOffer(userID, isTrader),
|
2023-03-03 15:23:46 +00:00
|
|
|
root: items[0]._id,
|
2024-01-16 10:26:48 +00:00
|
|
|
items: itemsClone,
|
2024-03-18 16:23:04 +00:00
|
|
|
itemsCost: Math.round(this.handbookHelper.getTemplatePrice(items[0]._tpl)), // Handbook price
|
2023-03-03 15:23:46 +00:00
|
|
|
requirements: offerRequirements,
|
2024-07-24 09:26:08 +01:00
|
|
|
requirementsCost: Math.round(singleItemListingPrice),
|
2024-07-19 13:45:34 +01:00
|
|
|
summaryCost: roubleListingPrice,
|
2023-03-03 15:23:46 +00:00
|
|
|
startTime: time,
|
|
|
|
endTime: this.getOfferEndTime(userID, time),
|
|
|
|
loyaltyLevel: loyalLevel,
|
2024-07-19 13:45:34 +01:00
|
|
|
sellInOnePiece: isPackOffer,
|
2023-03-03 15:23:46 +00:00
|
|
|
locked: false,
|
|
|
|
};
|
|
|
|
|
2023-12-19 23:52:39 +00:00
|
|
|
this.offerCounter++;
|
|
|
|
|
2023-03-03 15:23:46 +00:00
|
|
|
return offer;
|
|
|
|
}
|
|
|
|
|
2024-05-22 13:26:35 +01:00
|
|
|
/**
|
|
|
|
* Create the user object stored inside each flea offer object
|
|
|
|
* @param userID user creating the offer
|
|
|
|
* @param isTrader Is the user creating the offer a trader
|
|
|
|
* @returns IRagfairOfferUser
|
|
|
|
*/
|
2024-12-03 10:00:43 +00:00
|
|
|
protected createUserDataForFleaOffer(userID: string, isTrader: boolean): IRagfairOfferUser {
|
2024-05-22 13:26:35 +01:00
|
|
|
// Trader offer
|
2024-07-23 11:12:53 -04:00
|
|
|
if (isTrader) {
|
2024-05-22 13:26:35 +01:00
|
|
|
return {
|
|
|
|
id: userID,
|
|
|
|
memberType: MemberCategory.TRADER,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2024-05-27 20:06:07 +00:00
|
|
|
const isPlayerOffer = this.profileHelper.isPlayer(userID);
|
2024-07-23 11:12:53 -04:00
|
|
|
if (isPlayerOffer) {
|
2024-07-23 17:30:20 +01:00
|
|
|
const playerProfile = this.profileHelper.getPmcProfile(userID);
|
2024-05-22 13:26:35 +01:00
|
|
|
return {
|
|
|
|
id: playerProfile._id,
|
2024-07-19 13:45:34 +01:00
|
|
|
memberType: playerProfile.Info.MemberCategory,
|
|
|
|
selectedMemberCategory: playerProfile.Info.SelectedMemberCategory,
|
2024-05-22 13:26:35 +01:00
|
|
|
nickname: playerProfile.Info.Nickname,
|
2024-07-19 13:45:34 +01:00
|
|
|
rating: playerProfile.RagfairInfo.rating ?? 0,
|
2024-05-22 13:26:35 +01:00
|
|
|
isRatingGrowing: playerProfile.RagfairInfo.isRatingGrowing,
|
2024-05-27 20:06:07 +00:00
|
|
|
avatar: undefined,
|
2024-05-22 13:26:35 +01:00
|
|
|
aid: playerProfile.aid,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2024-10-18 11:39:51 +01:00
|
|
|
// Fake pmc offer
|
2024-05-22 13:26:35 +01:00
|
|
|
return {
|
|
|
|
id: userID,
|
|
|
|
memberType: MemberCategory.DEFAULT,
|
2024-10-18 11:39:51 +01:00
|
|
|
nickname: this.botHelper.getPmcNicknameOfMaxLength(this.botConfig.botNameLengthLimit),
|
2024-05-22 13:26:35 +01:00
|
|
|
rating: this.randomUtil.getFloat(
|
|
|
|
this.ragfairConfig.dynamic.rating.min,
|
2024-07-23 11:12:53 -04:00
|
|
|
this.ragfairConfig.dynamic.rating.max,
|
|
|
|
),
|
2024-05-22 13:26:35 +01:00
|
|
|
isRatingGrowing: this.randomUtil.getBool(),
|
2024-05-27 20:06:07 +00:00
|
|
|
avatar: undefined,
|
2024-05-22 13:26:35 +01:00
|
|
|
aid: this.hashUtil.generateAccountId(),
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2023-03-03 15:23:46 +00:00
|
|
|
/**
|
|
|
|
* Calculate the offer price that's listed on the flea listing
|
|
|
|
* @param offerRequirements barter requirements for offer
|
|
|
|
* @returns rouble cost of offer
|
|
|
|
*/
|
2024-10-19 12:43:38 +01:00
|
|
|
protected convertOfferRequirementsIntoRoubles(offerRequirements: IOfferRequirement[]): number {
|
2023-03-03 15:23:46 +00:00
|
|
|
let roublePrice = 0;
|
2024-07-23 11:12:53 -04:00
|
|
|
for (const requirement of offerRequirements) {
|
2023-03-03 15:23:46 +00:00
|
|
|
roublePrice += this.paymentHelper.isMoneyTpl(requirement._tpl)
|
|
|
|
? Math.round(this.calculateRoublePrice(requirement.count, requirement._tpl))
|
|
|
|
: this.ragfairPriceService.getFleaPriceForItem(requirement._tpl) * requirement.count; // get flea price for barter offer items
|
|
|
|
}
|
|
|
|
|
|
|
|
return roublePrice;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get avatar url from trader table in db
|
|
|
|
* @param isTrader Is user we're getting avatar for a trader
|
|
|
|
* @param userId persons id to get avatar of
|
|
|
|
* @returns url of avatar
|
|
|
|
*/
|
2024-07-23 11:12:53 -04:00
|
|
|
protected getAvatarUrl(isTrader: boolean, userId: string): string {
|
|
|
|
if (isTrader) {
|
2024-05-28 22:24:52 +01:00
|
|
|
return this.databaseService.getTrader(userId).base.avatar;
|
2023-03-03 15:23:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return "/files/trader/avatar/unknown.jpg";
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Convert a count of currency into roubles
|
|
|
|
* @param currencyCount amount of currency to convert into roubles
|
|
|
|
* @param currencyType Type of currency (euro/dollar/rouble)
|
|
|
|
* @returns count of roubles
|
|
|
|
*/
|
2024-07-23 11:12:53 -04:00
|
|
|
protected calculateRoublePrice(currencyCount: number, currencyType: string): number {
|
|
|
|
if (currencyType === Money.ROUBLES) {
|
2023-03-03 15:23:46 +00:00
|
|
|
return currencyCount;
|
|
|
|
}
|
2024-01-14 21:12:56 +00:00
|
|
|
|
|
|
|
return this.handbookHelper.inRUB(currencyCount, currencyType);
|
2023-03-03 15:23:46 +00:00
|
|
|
}
|
|
|
|
|
2023-07-25 14:04:21 +01:00
|
|
|
/**
|
|
|
|
* Check userId, if its a player, return their pmc _id, otherwise return userId parameter
|
|
|
|
* @param userId Users Id to check
|
|
|
|
* @returns Users Id
|
|
|
|
*/
|
2024-07-23 11:12:53 -04:00
|
|
|
protected getTraderId(userId: string): string {
|
|
|
|
if (this.profileHelper.isPlayer(userId)) {
|
2023-07-25 14:04:21 +01:00
|
|
|
return this.saveServer.getProfile(userId).characters.pmc._id;
|
2023-03-03 15:23:46 +00:00
|
|
|
}
|
|
|
|
|
2023-07-25 14:04:21 +01:00
|
|
|
return userId;
|
2023-03-03 15:23:46 +00:00
|
|
|
}
|
|
|
|
|
2023-07-25 14:04:21 +01:00
|
|
|
/**
|
|
|
|
* Get a flea trading rating for the passed in user
|
|
|
|
* @param userId User to get flea rating of
|
|
|
|
* @returns Flea rating value
|
|
|
|
*/
|
2024-07-23 11:12:53 -04:00
|
|
|
protected getRating(userId: string): number {
|
|
|
|
if (this.profileHelper.isPlayer(userId)) {
|
2023-07-25 14:04:21 +01:00
|
|
|
// Player offer
|
|
|
|
return this.saveServer.getProfile(userId).characters.pmc.RagfairInfo.rating;
|
2023-03-03 15:23:46 +00:00
|
|
|
}
|
|
|
|
|
2024-07-23 11:12:53 -04:00
|
|
|
if (this.ragfairServerHelper.isTrader(userId)) {
|
2023-07-25 14:04:21 +01:00
|
|
|
// Trader offer
|
2023-03-03 15:23:46 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2023-07-25 14:04:21 +01:00
|
|
|
// Generated pmc offer
|
2023-03-03 15:23:46 +00:00
|
|
|
return this.randomUtil.getFloat(this.ragfairConfig.dynamic.rating.min, this.ragfairConfig.dynamic.rating.max);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Is the offers user rating growing
|
|
|
|
* @param userID user to check rating of
|
|
|
|
* @returns true if its growing
|
|
|
|
*/
|
2024-07-23 11:12:53 -04:00
|
|
|
protected getRatingGrowing(userID: string): boolean {
|
|
|
|
if (this.profileHelper.isPlayer(userID)) {
|
2023-03-03 15:23:46 +00:00
|
|
|
// player offer
|
|
|
|
return this.saveServer.getProfile(userID).characters.pmc.RagfairInfo.isRatingGrowing;
|
|
|
|
}
|
|
|
|
|
2024-07-23 11:12:53 -04:00
|
|
|
if (this.ragfairServerHelper.isTrader(userID)) {
|
2023-03-03 15:23:46 +00:00
|
|
|
// trader offer
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2023-11-16 21:42:06 +00:00
|
|
|
// generated offer
|
2023-03-03 15:23:46 +00:00
|
|
|
// 50/50 growing/falling
|
|
|
|
return this.randomUtil.getBool();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get number of section until offer should expire
|
|
|
|
* @param userID Id of the offer owner
|
|
|
|
* @param time Time the offer is posted
|
|
|
|
* @returns number of seconds until offer expires
|
|
|
|
*/
|
2024-07-23 11:12:53 -04:00
|
|
|
protected getOfferEndTime(userID: string, time: number): number {
|
|
|
|
if (this.profileHelper.isPlayer(userID)) {
|
2023-12-16 15:11:11 +00:00
|
|
|
// Player offer = current time + offerDurationTimeInHour;
|
2024-07-23 11:12:53 -04:00
|
|
|
const offerDurationTimeHours = this.databaseService.getGlobals().config.RagFair.offerDurationTimeInHour;
|
2023-12-21 09:12:15 +00:00
|
|
|
return this.timeUtil.getTimestamp() + Math.round(offerDurationTimeHours * TimeUtil.ONE_HOUR_AS_SECONDS);
|
2023-03-03 15:23:46 +00:00
|
|
|
}
|
|
|
|
|
2024-07-23 11:12:53 -04:00
|
|
|
if (this.ragfairServerHelper.isTrader(userID)) {
|
2023-03-03 15:23:46 +00:00
|
|
|
// Trader offer
|
2024-05-28 22:24:52 +01:00
|
|
|
return this.databaseService.getTrader(userID).base.nextResupply;
|
2023-03-03 15:23:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Generated fake-player offer
|
2023-11-16 21:42:06 +00:00
|
|
|
return Math.round(
|
2024-07-23 11:12:53 -04:00
|
|
|
time +
|
|
|
|
this.randomUtil.getInt(
|
|
|
|
this.ragfairConfig.dynamic.endTimeSeconds.min,
|
|
|
|
this.ragfairConfig.dynamic.endTimeSeconds.max,
|
|
|
|
),
|
2023-11-16 21:42:06 +00:00
|
|
|
);
|
2023-03-03 15:23:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Create multiple offers for items by using a unique list of items we've generated previously
|
|
|
|
* @param expiredOffers optional, expired offers to regenerate
|
|
|
|
*/
|
2024-09-24 12:47:29 +01:00
|
|
|
public async generateDynamicOffers(expiredOffers?: IItem[][]): Promise<void> {
|
2024-06-30 21:39:58 +01:00
|
|
|
const replacingExpiredOffers = Boolean(expiredOffers?.length);
|
2023-03-03 15:23:46 +00:00
|
|
|
|
|
|
|
// get assort items from param if they exist, otherwise grab freshly generated assorts
|
2024-09-24 12:47:29 +01:00
|
|
|
const assortItemsToProcess: IItem[][] = replacingExpiredOffers
|
2024-07-23 17:30:20 +01:00
|
|
|
? expiredOffers
|
2023-03-03 15:23:46 +00:00
|
|
|
: this.ragfairAssortGenerator.getAssortItems();
|
|
|
|
|
2024-06-30 21:39:58 +01:00
|
|
|
// Create offers for each item set concurrently
|
2024-07-23 11:12:53 -04:00
|
|
|
await Promise.all(
|
|
|
|
assortItemsToProcess.map((assortItemWithChildren) =>
|
|
|
|
this.createOffersFromAssort(assortItemWithChildren, replacingExpiredOffers, this.ragfairConfig.dynamic),
|
|
|
|
),
|
|
|
|
);
|
2023-03-03 15:23:46 +00:00
|
|
|
}
|
2023-07-25 14:04:21 +01:00
|
|
|
|
|
|
|
/**
|
2024-01-23 14:17:01 +00:00
|
|
|
* @param assortItemWithChildren Item with its children to process into offers
|
|
|
|
* @param isExpiredOffer is an expired offer
|
2023-07-25 14:04:21 +01:00
|
|
|
* @param config Ragfair dynamic config
|
|
|
|
*/
|
2024-01-23 14:17:01 +00:00
|
|
|
protected async createOffersFromAssort(
|
2024-09-24 12:47:29 +01:00
|
|
|
assortItemWithChildren: IItem[],
|
2024-01-23 14:17:01 +00:00
|
|
|
isExpiredOffer: boolean,
|
2024-10-19 12:43:38 +01:00
|
|
|
config: IDynamic,
|
2024-07-23 11:12:53 -04:00
|
|
|
): Promise<void> {
|
2024-10-22 21:00:46 +01:00
|
|
|
const itemToSellDetails = this.itemHelper.getItem(assortItemWithChildren[0]._tpl);
|
2024-01-23 14:17:01 +00:00
|
|
|
const isPreset = this.presetHelper.isPreset(assortItemWithChildren[0].upd.sptPresetId);
|
2023-03-03 15:23:46 +00:00
|
|
|
|
|
|
|
// Only perform checks on newly generated items, skip expired items being refreshed
|
2024-10-22 21:00:46 +01:00
|
|
|
if (!(isExpiredOffer || this.ragfairServerHelper.isItemValidRagfairItem(itemToSellDetails))) {
|
2023-03-03 15:23:46 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2024-01-23 11:42:47 +00:00
|
|
|
// Armor presets can hold plates above the allowed flea level, remove if necessary
|
2024-07-23 11:12:53 -04:00
|
|
|
if (isPreset && this.ragfairConfig.dynamic.blacklist.enableBsgList) {
|
2024-02-08 16:53:14 +00:00
|
|
|
this.removeBannedPlatesFromPreset(assortItemWithChildren, this.ragfairConfig.dynamic.blacklist.armorPlate);
|
2024-01-23 11:42:47 +00:00
|
|
|
}
|
|
|
|
|
2023-03-03 15:23:46 +00:00
|
|
|
// Get number of offers to create
|
2024-01-23 14:17:01 +00:00
|
|
|
// Limit to 1 offer when processing expired - like-for-like replacement
|
|
|
|
const offerCount = isExpiredOffer
|
2023-03-03 15:23:46 +00:00
|
|
|
? 1
|
|
|
|
: Math.round(this.randomUtil.getInt(config.offerItemCount.min, config.offerItemCount.max));
|
|
|
|
|
|
|
|
// Store all functions to create offers for this item and pass into Promise.all to run async
|
|
|
|
const assortSingleOfferProcesses = [];
|
2024-07-23 11:12:53 -04:00
|
|
|
for (let index = 0; index < offerCount; index++) {
|
2024-02-13 08:34:55 +00:00
|
|
|
// Clone the item so we don't have shared references and generate new item IDs
|
2024-05-13 17:58:17 +00:00
|
|
|
const clonedAssort = this.cloner.clone(assortItemWithChildren);
|
2024-02-13 08:34:55 +00:00
|
|
|
this.itemHelper.reparentItemAndChildren(clonedAssort[0], clonedAssort);
|
2024-02-02 13:54:07 -05:00
|
|
|
|
2024-02-13 08:34:55 +00:00
|
|
|
// Clear unnecessary properties
|
|
|
|
delete clonedAssort[0].parentId;
|
|
|
|
delete clonedAssort[0].slotId;
|
2024-01-14 21:12:56 +00:00
|
|
|
|
2024-10-22 21:00:46 +01:00
|
|
|
assortSingleOfferProcesses.push(
|
|
|
|
this.createSingleOfferForItem(this.hashUtil.generate(), clonedAssort, isPreset, itemToSellDetails[1]),
|
|
|
|
);
|
2023-03-03 15:23:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
await Promise.all(assortSingleOfferProcesses);
|
|
|
|
}
|
|
|
|
|
2024-01-23 11:42:47 +00:00
|
|
|
/**
|
|
|
|
* iterate over an items chidren and look for plates above desired level and remove them
|
|
|
|
* @param presetWithChildren preset to check for plates
|
2024-02-08 16:53:14 +00:00
|
|
|
* @param plateSettings Settings
|
2024-01-23 11:42:47 +00:00
|
|
|
* @returns True if plate removed
|
|
|
|
*/
|
2024-02-08 16:53:14 +00:00
|
|
|
protected removeBannedPlatesFromPreset(
|
2024-09-24 12:47:29 +01:00
|
|
|
presetWithChildren: IItem[],
|
2024-02-08 16:53:14 +00:00
|
|
|
plateSettings: IArmorPlateBlacklistSettings,
|
2024-07-23 11:12:53 -04:00
|
|
|
): boolean {
|
|
|
|
if (!this.itemHelper.armorItemCanHoldMods(presetWithChildren[0]._tpl)) {
|
2024-01-23 11:42:47 +00:00
|
|
|
// Cant hold armor inserts, skip
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2024-05-17 15:32:41 -04:00
|
|
|
const plateSlots = presetWithChildren.filter((item) =>
|
2024-05-07 23:57:08 -04:00
|
|
|
this.itemHelper.getRemovablePlateSlotIds().includes(item.slotId?.toLowerCase()),
|
2024-02-02 13:54:07 -05:00
|
|
|
);
|
2024-07-23 11:12:53 -04:00
|
|
|
if (plateSlots.length === 0) {
|
2024-02-08 16:53:14 +00:00
|
|
|
// Has no plate slots e.g. "front_plate", exit
|
2024-01-23 11:42:47 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
let removedPlate = false;
|
2024-07-23 11:12:53 -04:00
|
|
|
for (const plateSlot of plateSlots) {
|
2024-02-08 16:53:14 +00:00
|
|
|
const plateDetails = this.itemHelper.getItem(plateSlot._tpl)[1];
|
2024-07-23 11:12:53 -04:00
|
|
|
if (plateSettings.ignoreSlots.includes(plateSlot.slotId.toLowerCase())) {
|
2024-02-08 16:53:14 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
const plateArmorLevel = Number.parseInt(<string>plateDetails._props.armorClass) ?? 0;
|
2024-07-23 11:12:53 -04:00
|
|
|
if (plateArmorLevel > plateSettings.maxProtectionLevel) {
|
2024-01-23 11:42:47 +00:00
|
|
|
presetWithChildren.splice(presetWithChildren.indexOf(plateSlot), 1);
|
|
|
|
removedPlate = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-02-02 13:54:07 -05:00
|
|
|
return removedPlate;
|
2024-01-23 11:42:47 +00:00
|
|
|
}
|
|
|
|
|
2023-03-03 15:23:46 +00:00
|
|
|
/**
|
|
|
|
* Create one flea offer for a specific item
|
2024-10-22 21:00:46 +01:00
|
|
|
* @param sellerId Id of seller
|
2024-01-12 21:59:30 +00:00
|
|
|
* @param itemWithChildren Item to create offer for
|
2023-03-03 15:23:46 +00:00
|
|
|
* @param isPreset Is item a weapon preset
|
2024-10-22 21:00:46 +01:00
|
|
|
* @param itemToSellDetails Raw db item details
|
2023-07-25 14:04:21 +01:00
|
|
|
* @returns Item array
|
2023-03-03 15:23:46 +00:00
|
|
|
*/
|
2023-11-16 21:42:06 +00:00
|
|
|
protected async createSingleOfferForItem(
|
2024-10-22 21:00:46 +01:00
|
|
|
sellerId: string,
|
2024-09-24 12:47:29 +01:00
|
|
|
itemWithChildren: IItem[],
|
2023-11-16 21:42:06 +00:00
|
|
|
isPreset: boolean,
|
2024-10-22 21:00:46 +01:00
|
|
|
itemToSellDetails: ITemplateItem,
|
2024-07-23 11:12:53 -04:00
|
|
|
): Promise<void> {
|
2023-10-10 11:03:20 +00:00
|
|
|
// Set stack size to random value
|
2024-02-02 13:54:07 -05:00
|
|
|
itemWithChildren[0].upd.StackObjectsCount = this.ragfairServerHelper.calculateDynamicStackCount(
|
|
|
|
itemWithChildren[0]._tpl,
|
|
|
|
isPreset,
|
|
|
|
);
|
2023-11-16 21:42:06 +00:00
|
|
|
|
2023-03-03 15:23:46 +00:00
|
|
|
const isBarterOffer = this.randomUtil.getChance100(this.ragfairConfig.dynamic.barter.chancePercent);
|
2024-07-23 11:12:53 -04:00
|
|
|
const isPackOffer =
|
|
|
|
this.randomUtil.getChance100(this.ragfairConfig.dynamic.pack.chancePercent) &&
|
|
|
|
!isBarterOffer &&
|
|
|
|
itemWithChildren.length === 1 &&
|
|
|
|
this.itemHelper.isOfBaseclasses(
|
2024-05-17 15:32:41 -04:00
|
|
|
itemWithChildren[0]._tpl,
|
|
|
|
this.ragfairConfig.dynamic.pack.itemTypeWhitelist,
|
|
|
|
);
|
2024-02-14 11:12:20 +00:00
|
|
|
|
|
|
|
// Remove removable plates if % check passes
|
2024-07-23 11:12:53 -04:00
|
|
|
if (this.itemHelper.armorItemCanHoldMods(itemWithChildren[0]._tpl)) {
|
2024-02-14 11:12:20 +00:00
|
|
|
const armorConfig = this.ragfairConfig.dynamic.armor;
|
|
|
|
|
|
|
|
const shouldRemovePlates = this.randomUtil.getChance100(armorConfig.removeRemovablePlateChance);
|
2024-07-23 11:12:53 -04:00
|
|
|
if (shouldRemovePlates && this.itemHelper.armorItemHasRemovablePlateSlots(itemWithChildren[0]._tpl)) {
|
2024-05-17 15:32:41 -04:00
|
|
|
const offerItemPlatesToRemove = itemWithChildren.filter((item) =>
|
2024-05-07 23:57:08 -04:00
|
|
|
armorConfig.plateSlotIdToRemovePool.includes(item.slotId?.toLowerCase()),
|
2024-02-14 11:12:20 +00:00
|
|
|
);
|
|
|
|
|
2024-07-23 11:12:53 -04:00
|
|
|
for (const plateItem of offerItemPlatesToRemove) {
|
2024-02-14 11:12:20 +00:00
|
|
|
itemWithChildren.splice(itemWithChildren.indexOf(plateItem), 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-10-10 11:03:20 +00:00
|
|
|
let barterScheme: IBarterScheme[];
|
2024-07-23 11:12:53 -04:00
|
|
|
if (isPackOffer) {
|
2023-10-10 11:03:20 +00:00
|
|
|
// Set pack size
|
2023-11-16 21:42:06 +00:00
|
|
|
const stackSize = this.randomUtil.getInt(
|
|
|
|
this.ragfairConfig.dynamic.pack.itemCountMin,
|
|
|
|
this.ragfairConfig.dynamic.pack.itemCountMax,
|
|
|
|
);
|
2024-01-12 21:59:30 +00:00
|
|
|
itemWithChildren[0].upd.StackObjectsCount = stackSize;
|
2023-03-03 15:23:46 +00:00
|
|
|
|
2023-10-10 11:03:20 +00:00
|
|
|
// Don't randomise pack items
|
2024-01-12 21:59:30 +00:00
|
|
|
barterScheme = this.createCurrencyBarterScheme(itemWithChildren, isPackOffer, stackSize);
|
2024-07-23 11:12:53 -04:00
|
|
|
} else if (isBarterOffer) {
|
2023-10-10 11:03:20 +00:00
|
|
|
// Apply randomised properties
|
2024-10-22 21:00:46 +01:00
|
|
|
this.randomiseOfferItemUpdProperties(sellerId, itemWithChildren, itemToSellDetails);
|
2024-10-19 10:58:25 +01:00
|
|
|
barterScheme = this.createBarterBarterScheme(itemWithChildren, this.ragfairConfig.dynamic.barter);
|
2024-10-22 20:51:22 +01:00
|
|
|
if (this.ragfairConfig.dynamic.barter.makeSingleStackOnly) {
|
|
|
|
itemWithChildren[0].upd.StackObjectsCount = 1;
|
|
|
|
}
|
2024-07-23 11:12:53 -04:00
|
|
|
} else {
|
2023-10-10 11:03:20 +00:00
|
|
|
// Apply randomised properties
|
2024-10-22 21:00:46 +01:00
|
|
|
this.randomiseOfferItemUpdProperties(sellerId, itemWithChildren, itemToSellDetails);
|
2024-01-12 21:59:30 +00:00
|
|
|
barterScheme = this.createCurrencyBarterScheme(itemWithChildren, isPackOffer);
|
2023-10-10 11:03:20 +00:00
|
|
|
}
|
2023-03-03 15:23:46 +00:00
|
|
|
|
2024-05-22 12:51:04 +01:00
|
|
|
const offer = this.createAndAddFleaOffer(
|
2024-10-22 21:00:46 +01:00
|
|
|
sellerId,
|
2023-03-03 15:23:46 +00:00
|
|
|
this.timeUtil.getTimestamp(),
|
2024-01-12 21:59:30 +00:00
|
|
|
itemWithChildren,
|
2023-03-03 15:23:46 +00:00
|
|
|
barterScheme,
|
|
|
|
1,
|
2023-11-16 21:42:06 +00:00
|
|
|
isPreset || isPackOffer,
|
|
|
|
); // sellAsOnePiece
|
2023-03-03 15:23:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Generate trader offers on flea using the traders assort data
|
|
|
|
* @param traderID Trader to generate offers for
|
|
|
|
*/
|
2024-07-23 11:12:53 -04:00
|
|
|
public generateFleaOffersForTrader(traderID: string): void {
|
2024-06-30 21:39:58 +01:00
|
|
|
// Purge
|
2023-03-03 15:23:46 +00:00
|
|
|
this.ragfairOfferService.removeAllOffersByTrader(traderID);
|
|
|
|
|
|
|
|
const time = this.timeUtil.getTimestamp();
|
2024-05-28 22:24:52 +01:00
|
|
|
const trader = this.databaseService.getTrader(traderID);
|
2023-03-03 15:23:46 +00:00
|
|
|
const assorts = trader.assort;
|
|
|
|
|
|
|
|
// Trader assorts / assort items are missing
|
2024-07-23 11:12:53 -04:00
|
|
|
if (!assorts?.items?.length) {
|
2023-11-16 21:42:06 +00:00
|
|
|
this.logger.error(
|
|
|
|
this.localisationService.getText(
|
|
|
|
"ragfair-no_trader_assorts_cant_generate_flea_offers",
|
|
|
|
trader.base.nickname,
|
|
|
|
),
|
|
|
|
);
|
2023-03-03 15:23:46 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2024-06-30 21:39:58 +01:00
|
|
|
const blacklist = this.ragfairConfig.dynamic.blacklist;
|
2024-07-23 11:12:53 -04:00
|
|
|
for (const item of assorts.items) {
|
2024-06-30 21:39:58 +01:00
|
|
|
// We only want to process 'base/root' items, no children
|
2024-07-23 11:12:53 -04:00
|
|
|
if (item.slotId !== "hideout") {
|
2023-03-03 15:23:46 +00:00
|
|
|
// skip mod items
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2023-10-10 11:03:20 +00:00
|
|
|
// Run blacklist check on trader offers
|
2024-07-23 11:12:53 -04:00
|
|
|
if (blacklist.traderItems) {
|
2023-03-03 15:23:46 +00:00
|
|
|
const itemDetails = this.itemHelper.getItem(item._tpl);
|
2024-07-23 11:12:53 -04:00
|
|
|
if (!itemDetails[0]) {
|
2023-03-03 15:23:46 +00:00
|
|
|
this.logger.warning(this.localisationService.getText("ragfair-tpl_not_a_valid_item", item._tpl));
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Don't include items that BSG has blacklisted from flea
|
2024-07-23 11:12:53 -04:00
|
|
|
if (blacklist.enableBsgList && !itemDetails[1]._props.CanSellOnRagfair) {
|
2023-03-03 15:23:46 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const isPreset = this.presetHelper.isPreset(item._id);
|
2024-09-24 12:47:29 +01:00
|
|
|
const items: IItem[] = isPreset
|
2023-03-03 15:23:46 +00:00
|
|
|
? this.ragfairServerHelper.getPresetItems(item)
|
|
|
|
: [...[item], ...this.itemHelper.findAndReturnChildrenByAssort(item._id, assorts.items)];
|
|
|
|
|
|
|
|
const barterScheme = assorts.barter_scheme[item._id];
|
2024-07-23 11:12:53 -04:00
|
|
|
if (!barterScheme) {
|
2023-11-16 21:42:06 +00:00
|
|
|
this.logger.warning(
|
|
|
|
this.localisationService.getText("ragfair-missing_barter_scheme", {
|
|
|
|
itemId: item._id,
|
|
|
|
tpl: item._tpl,
|
|
|
|
name: trader.base.nickname,
|
|
|
|
}),
|
|
|
|
);
|
2023-03-03 15:23:46 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
const barterSchemeItems = assorts.barter_scheme[item._id][0];
|
|
|
|
const loyalLevel = assorts.loyal_level_items[item._id];
|
|
|
|
|
2024-05-22 12:51:04 +01:00
|
|
|
const offer = this.createAndAddFleaOffer(traderID, time, items, barterSchemeItems, loyalLevel, false);
|
2023-03-03 15:23:46 +00:00
|
|
|
|
|
|
|
// Refresh complete, reset flag to false
|
|
|
|
trader.base.refreshTraderRagfairOffers = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get array of an item with its mods + condition properties (e.g durability)
|
|
|
|
* Apply randomisation adjustments to condition if item base is found in ragfair.json/dynamic/condition
|
|
|
|
* @param userID id of owner of item
|
2024-01-29 11:22:04 +00:00
|
|
|
* @param itemWithMods Item and mods, get condition of first item (only first array item is modified)
|
2023-03-03 15:23:46 +00:00
|
|
|
* @param itemDetails db details of first item
|
|
|
|
*/
|
2024-09-24 12:47:29 +01:00
|
|
|
protected randomiseOfferItemUpdProperties(userID: string, itemWithMods: IItem[], itemDetails: ITemplateItem): void {
|
2023-11-16 21:42:06 +00:00
|
|
|
// Add any missing properties to first item in array
|
2024-01-29 11:22:04 +00:00
|
|
|
this.addMissingConditions(itemWithMods[0]);
|
2023-03-03 15:23:46 +00:00
|
|
|
|
2024-07-23 11:12:53 -04:00
|
|
|
if (!(this.profileHelper.isPlayer(userID) || this.ragfairServerHelper.isTrader(userID))) {
|
2023-03-03 15:23:46 +00:00
|
|
|
const parentId = this.getDynamicConditionIdForTpl(itemDetails._id);
|
2024-07-23 11:12:53 -04:00
|
|
|
if (!parentId) {
|
2023-03-03 15:23:46 +00:00
|
|
|
// No condition details found, don't proceed with modifying item conditions
|
2024-01-29 11:22:04 +00:00
|
|
|
return;
|
2023-03-03 15:23:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Roll random chance to randomise item condition
|
2024-07-23 11:12:53 -04:00
|
|
|
if (this.randomUtil.getChance100(this.ragfairConfig.dynamic.condition[parentId].conditionChance * 100)) {
|
2024-01-12 21:59:30 +00:00
|
|
|
this.randomiseItemCondition(parentId, itemWithMods, itemDetails);
|
2023-03-03 15:23:46 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the relevant condition id if item tpl matches in ragfair.json/condition
|
|
|
|
* @param tpl Item to look for matching condition object
|
|
|
|
* @returns condition id
|
|
|
|
*/
|
2024-07-23 11:12:53 -04:00
|
|
|
protected getDynamicConditionIdForTpl(tpl: string): string | undefined {
|
2023-03-03 15:23:46 +00:00
|
|
|
// Get keys from condition config dictionary
|
|
|
|
const configConditions = Object.keys(this.ragfairConfig.dynamic.condition);
|
2024-07-23 11:12:53 -04:00
|
|
|
for (const baseClass of configConditions) {
|
|
|
|
if (this.itemHelper.isOfBaseclass(tpl, baseClass)) {
|
2023-03-03 15:23:46 +00:00
|
|
|
return baseClass;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return undefined;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Alter an items condition based on its item base type
|
|
|
|
* @param conditionSettingsId also the parentId of item being altered
|
2024-01-12 21:59:30 +00:00
|
|
|
* @param itemWithMods Item to adjust condition details of
|
2023-03-03 15:23:46 +00:00
|
|
|
* @param itemDetails db item details of first item in array
|
|
|
|
*/
|
2024-02-02 13:54:07 -05:00
|
|
|
protected randomiseItemCondition(
|
|
|
|
conditionSettingsId: string,
|
2024-09-24 12:47:29 +01:00
|
|
|
itemWithMods: IItem[],
|
2024-02-02 13:54:07 -05:00
|
|
|
itemDetails: ITemplateItem,
|
2024-07-23 11:12:53 -04:00
|
|
|
): void {
|
2024-01-12 21:59:30 +00:00
|
|
|
const rootItem = itemWithMods[0];
|
|
|
|
|
2024-02-08 14:52:09 +00:00
|
|
|
const itemConditionValues: Condition = this.ragfairConfig.dynamic.condition[conditionSettingsId];
|
|
|
|
const maxMultiplier = this.randomUtil.getFloat(itemConditionValues.max.min, itemConditionValues.max.max);
|
|
|
|
const currentMultiplier = this.randomUtil.getFloat(
|
|
|
|
itemConditionValues.current.min,
|
|
|
|
itemConditionValues.current.max,
|
|
|
|
);
|
2023-03-03 15:23:46 +00:00
|
|
|
|
2024-01-14 22:28:53 +00:00
|
|
|
// Randomise armor + plates + armor related things
|
2024-02-02 13:54:07 -05:00
|
|
|
if (
|
2024-07-23 11:12:53 -04:00
|
|
|
this.itemHelper.armorItemCanHoldMods(rootItem._tpl) ||
|
|
|
|
this.itemHelper.isOfBaseclasses(rootItem._tpl, [BaseClasses.ARMOR_PLATE, BaseClasses.ARMORED_EQUIPMENT])
|
|
|
|
) {
|
2024-02-08 14:52:09 +00:00
|
|
|
this.randomiseArmorDurabilityValues(itemWithMods, currentMultiplier, maxMultiplier);
|
2023-03-03 15:23:46 +00:00
|
|
|
|
2024-01-13 20:47:59 +00:00
|
|
|
// Add hits to visor
|
2024-05-17 15:32:41 -04:00
|
|
|
const visorMod = itemWithMods.find(
|
|
|
|
(item) => item.parentId === BaseClasses.ARMORED_EQUIPMENT && item.slotId === "mod_equipment_000",
|
2024-02-02 13:54:07 -05:00
|
|
|
);
|
2024-07-23 11:12:53 -04:00
|
|
|
if (this.randomUtil.getChance100(25) && visorMod) {
|
2024-03-07 09:18:39 +00:00
|
|
|
this.itemHelper.addUpdObjectToItem(visorMod);
|
2024-01-13 20:47:59 +00:00
|
|
|
|
2024-02-02 13:54:07 -05:00
|
|
|
visorMod.upd.FaceShield = { Hits: this.randomUtil.getInt(1, 3) };
|
2024-01-13 20:47:59 +00:00
|
|
|
}
|
|
|
|
|
2024-01-12 21:59:30 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Randomise Weapons
|
2024-07-23 11:12:53 -04:00
|
|
|
if (this.itemHelper.isOfBaseclass(itemDetails._id, BaseClasses.WEAPON)) {
|
2024-02-08 14:52:09 +00:00
|
|
|
this.randomiseWeaponDurability(itemWithMods[0], itemDetails, maxMultiplier, currentMultiplier);
|
2023-10-10 11:03:20 +00:00
|
|
|
|
|
|
|
return;
|
2023-03-03 15:23:46 +00:00
|
|
|
}
|
|
|
|
|
2024-07-23 11:12:53 -04:00
|
|
|
if (rootItem.upd.MedKit) {
|
2024-06-30 21:39:58 +01:00
|
|
|
// Randomize health
|
2024-02-08 14:52:09 +00:00
|
|
|
rootItem.upd.MedKit.HpResource = Math.round(rootItem.upd.MedKit.HpResource * maxMultiplier) || 1;
|
2023-10-10 11:03:20 +00:00
|
|
|
|
|
|
|
return;
|
2023-03-03 15:23:46 +00:00
|
|
|
}
|
|
|
|
|
2024-07-23 11:12:53 -04:00
|
|
|
if (rootItem.upd.Key && itemDetails._props.MaximumNumberOfUsage > 1) {
|
2024-06-30 21:39:58 +01:00
|
|
|
// Randomize key uses
|
2024-07-23 11:12:53 -04:00
|
|
|
rootItem.upd.Key.NumberOfUsages =
|
|
|
|
Math.round(itemDetails._props.MaximumNumberOfUsage * (1 - maxMultiplier)) || 0;
|
2023-10-10 11:03:20 +00:00
|
|
|
|
|
|
|
return;
|
2023-03-03 15:23:46 +00:00
|
|
|
}
|
|
|
|
|
2024-07-23 11:12:53 -04:00
|
|
|
if (rootItem.upd.FoodDrink) {
|
2023-03-03 15:23:46 +00:00
|
|
|
// randomize food/drink value
|
2024-02-08 14:52:09 +00:00
|
|
|
rootItem.upd.FoodDrink.HpPercent = Math.round(itemDetails._props.MaxResource * maxMultiplier) || 1;
|
2023-10-10 11:03:20 +00:00
|
|
|
|
|
|
|
return;
|
2023-03-03 15:23:46 +00:00
|
|
|
}
|
|
|
|
|
2024-07-23 11:12:53 -04:00
|
|
|
if (rootItem.upd.RepairKit) {
|
2023-03-03 15:23:46 +00:00
|
|
|
// randomize repair kit (armor/weapon) uses
|
2024-02-08 14:52:09 +00:00
|
|
|
rootItem.upd.RepairKit.Resource = Math.round(itemDetails._props.MaxRepairResource * maxMultiplier) || 1;
|
2023-10-10 11:03:20 +00:00
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2024-07-23 11:12:53 -04:00
|
|
|
if (this.itemHelper.isOfBaseclass(itemDetails._id, BaseClasses.FUEL)) {
|
2023-10-10 11:03:20 +00:00
|
|
|
const totalCapacity = itemDetails._props.MaxResource;
|
2024-02-08 14:52:09 +00:00
|
|
|
const remainingFuel = Math.round(totalCapacity * maxMultiplier);
|
2024-01-12 21:59:30 +00:00
|
|
|
rootItem.upd.Resource = { UnitsConsumed: totalCapacity - remainingFuel, Value: remainingFuel };
|
2023-03-03 15:23:46 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Adjust an items durability/maxDurability value
|
2024-02-08 14:52:09 +00:00
|
|
|
* @param item item (weapon/armor) to Adjust
|
|
|
|
* @param itemDbDetails Weapon details from db
|
|
|
|
* @param maxMultiplier Value to multiply max durability by
|
|
|
|
* @param currentMultiplier Value to multiply current durability by
|
2023-03-03 15:23:46 +00:00
|
|
|
*/
|
2024-02-08 14:52:09 +00:00
|
|
|
protected randomiseWeaponDurability(
|
2024-09-24 12:47:29 +01:00
|
|
|
item: IItem,
|
2024-02-08 14:52:09 +00:00
|
|
|
itemDbDetails: ITemplateItem,
|
|
|
|
maxMultiplier: number,
|
|
|
|
currentMultiplier: number,
|
2024-07-23 11:12:53 -04:00
|
|
|
): void {
|
2024-06-30 21:39:58 +01:00
|
|
|
// Max
|
|
|
|
const baseMaxDurability = itemDbDetails._props.MaxDurability;
|
|
|
|
const lowestMaxDurability = this.randomUtil.getFloat(maxMultiplier, 1) * baseMaxDurability;
|
2024-07-23 11:12:53 -04:00
|
|
|
const chosenMaxDurability = Math.round(this.randomUtil.getFloat(lowestMaxDurability, baseMaxDurability));
|
2023-03-03 15:23:46 +00:00
|
|
|
|
2024-06-30 21:39:58 +01:00
|
|
|
// Current
|
2024-02-08 14:52:09 +00:00
|
|
|
const lowestCurrentDurability = this.randomUtil.getFloat(currentMultiplier, 1) * chosenMaxDurability;
|
|
|
|
const chosenCurrentDurability = Math.round(
|
|
|
|
this.randomUtil.getFloat(lowestCurrentDurability, chosenMaxDurability),
|
|
|
|
);
|
2023-03-03 15:23:46 +00:00
|
|
|
|
2024-02-08 14:52:09 +00:00
|
|
|
item.upd.Repairable.Durability = chosenCurrentDurability || 1; // Never let value become 0
|
|
|
|
item.upd.Repairable.MaxDurability = chosenMaxDurability;
|
2023-03-03 15:23:46 +00:00
|
|
|
}
|
|
|
|
|
2024-01-13 09:15:11 +00:00
|
|
|
/**
|
|
|
|
* Randomise the durabiltiy values for an armors plates and soft inserts
|
|
|
|
* @param armorWithMods Armor item with its child mods
|
2024-02-08 14:52:09 +00:00
|
|
|
* @param currentMultiplier Chosen multipler to use for current durability value
|
|
|
|
* @param maxMultiplier Chosen multipler to use for max durability value
|
2024-01-13 09:15:11 +00:00
|
|
|
*/
|
2024-02-08 14:52:09 +00:00
|
|
|
protected randomiseArmorDurabilityValues(
|
2024-09-24 12:47:29 +01:00
|
|
|
armorWithMods: IItem[],
|
2024-02-08 14:52:09 +00:00
|
|
|
currentMultiplier: number,
|
|
|
|
maxMultiplier: number,
|
2024-07-23 11:12:53 -04:00
|
|
|
): void {
|
|
|
|
for (const armorItem of armorWithMods) {
|
2024-02-08 14:52:09 +00:00
|
|
|
const itemDbDetails = this.itemHelper.getItem(armorItem._tpl)[1];
|
2024-07-23 11:12:53 -04:00
|
|
|
if (Number.parseInt(<string>itemDbDetails._props.armorClass) > 1) {
|
2024-03-07 09:18:39 +00:00
|
|
|
this.itemHelper.addUpdObjectToItem(armorItem);
|
2024-01-13 09:15:11 +00:00
|
|
|
|
2024-06-30 21:39:58 +01:00
|
|
|
const baseMaxDurability = itemDbDetails._props.MaxDurability;
|
2024-07-23 11:12:53 -04:00
|
|
|
const lowestMaxDurability = this.randomUtil.getFloat(maxMultiplier, 1) * baseMaxDurability;
|
2024-02-08 14:52:09 +00:00
|
|
|
const chosenMaxDurability = Math.round(
|
2024-06-30 21:39:58 +01:00
|
|
|
this.randomUtil.getFloat(lowestMaxDurability, baseMaxDurability),
|
2024-01-13 09:15:11 +00:00
|
|
|
);
|
2024-02-08 14:52:09 +00:00
|
|
|
|
|
|
|
const lowestCurrentDurability = this.randomUtil.getFloat(currentMultiplier, 1) * chosenMaxDurability;
|
|
|
|
const chosenCurrentDurability = Math.round(
|
|
|
|
this.randomUtil.getFloat(lowestCurrentDurability, chosenMaxDurability),
|
2024-01-13 09:15:11 +00:00
|
|
|
);
|
2024-02-08 14:52:09 +00:00
|
|
|
|
|
|
|
armorItem.upd.Repairable = {
|
|
|
|
Durability: chosenCurrentDurability || 1, // Never let value become 0
|
|
|
|
MaxDurability: chosenMaxDurability,
|
2024-01-13 09:15:11 +00:00
|
|
|
};
|
2024-01-12 21:59:30 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-03-03 15:23:46 +00:00
|
|
|
/**
|
|
|
|
* Add missing conditions to an item if needed
|
|
|
|
* Durabiltiy for repairable items
|
|
|
|
* HpResource for medical items
|
|
|
|
* @param item item to add conditions to
|
|
|
|
*/
|
2024-09-24 12:47:29 +01:00
|
|
|
protected addMissingConditions(item: IItem): void {
|
2023-03-03 15:23:46 +00:00
|
|
|
const props = this.itemHelper.getItem(item._tpl)[1]._props;
|
2023-11-16 21:42:06 +00:00
|
|
|
const isRepairable = "Durability" in props;
|
|
|
|
const isMedkit = "MaxHpResource" in props;
|
|
|
|
const isKey = "MaximumNumberOfUsage" in props;
|
|
|
|
const isConsumable = props.MaxResource > 1 && "foodUseTime" in props;
|
|
|
|
const isRepairKit = "MaxRepairResource" in props;
|
2023-03-03 15:23:46 +00:00
|
|
|
|
2024-07-23 11:12:53 -04:00
|
|
|
if (isRepairable && props.Durability > 0) {
|
2023-11-16 21:42:06 +00:00
|
|
|
item.upd.Repairable = { Durability: props.Durability, MaxDurability: props.Durability };
|
2024-06-30 21:39:58 +01:00
|
|
|
|
|
|
|
return;
|
2023-03-03 15:23:46 +00:00
|
|
|
}
|
|
|
|
|
2024-07-23 11:12:53 -04:00
|
|
|
if (isMedkit && props.MaxHpResource > 0) {
|
2023-11-16 21:42:06 +00:00
|
|
|
item.upd.MedKit = { HpResource: props.MaxHpResource };
|
2024-06-30 21:39:58 +01:00
|
|
|
|
|
|
|
return;
|
2023-03-03 15:23:46 +00:00
|
|
|
}
|
|
|
|
|
2024-07-23 11:12:53 -04:00
|
|
|
if (isKey) {
|
2023-11-16 21:42:06 +00:00
|
|
|
item.upd.Key = { NumberOfUsages: 0 };
|
2024-06-30 21:39:58 +01:00
|
|
|
|
|
|
|
return;
|
2023-03-03 15:23:46 +00:00
|
|
|
}
|
|
|
|
|
2024-06-30 21:39:58 +01:00
|
|
|
// Food/drink
|
2024-07-23 11:12:53 -04:00
|
|
|
if (isConsumable) {
|
2023-11-16 21:42:06 +00:00
|
|
|
item.upd.FoodDrink = { HpPercent: props.MaxResource };
|
2024-06-30 21:39:58 +01:00
|
|
|
|
|
|
|
return;
|
2023-03-03 15:23:46 +00:00
|
|
|
}
|
|
|
|
|
2024-07-23 11:12:53 -04:00
|
|
|
if (isRepairKit) {
|
2023-11-16 21:42:06 +00:00
|
|
|
item.upd.RepairKit = { Resource: props.MaxRepairResource };
|
2023-03-03 15:23:46 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Create a barter-based barter scheme, if not possible, fall back to making barter scheme currency based
|
|
|
|
* @param offerItems Items for sale in offer
|
2024-10-19 10:58:25 +01:00
|
|
|
* @param barterConfig Barter config from ragfairConfig.dynamic.barter
|
2023-07-25 14:04:21 +01:00
|
|
|
* @returns Barter scheme
|
2023-03-03 15:23:46 +00:00
|
|
|
*/
|
2024-10-19 10:58:25 +01:00
|
|
|
protected createBarterBarterScheme(offerItems: IItem[], barterConfig: IBarterDetails): IBarterScheme[] {
|
|
|
|
// Get flea price of item being sold
|
|
|
|
const priceOfOfferItem = this.ragfairPriceService.getDynamicOfferPriceForOffer(
|
2023-11-16 21:42:06 +00:00
|
|
|
offerItems,
|
|
|
|
Money.ROUBLES,
|
|
|
|
false,
|
|
|
|
);
|
2023-03-03 15:23:46 +00:00
|
|
|
|
|
|
|
// Dont make items under a designated rouble value into barter offers
|
2024-10-19 10:58:25 +01:00
|
|
|
if (priceOfOfferItem < barterConfig.minRoubleCostToBecomeBarter) {
|
2023-10-10 11:03:20 +00:00
|
|
|
return this.createCurrencyBarterScheme(offerItems, false);
|
2023-03-03 15:23:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Get a randomised number of barter items to list offer for
|
2024-10-19 10:58:25 +01:00
|
|
|
const barterItemCount = this.randomUtil.getInt(barterConfig.itemCountMin, barterConfig.itemCountMax);
|
2023-03-03 15:23:46 +00:00
|
|
|
|
|
|
|
// Get desired cost of individual item offer will be listed for e.g. offer = 15k, item count = 3, desired item cost = 5k
|
2024-10-19 10:58:25 +01:00
|
|
|
const desiredItemCostRouble = Math.round(priceOfOfferItem / barterItemCount);
|
2023-03-03 15:23:46 +00:00
|
|
|
|
2024-10-19 10:58:25 +01:00
|
|
|
// Rouble amount to go above/below when looking for an item (Wiggle cost of item a little)
|
|
|
|
const offerCostVarianceRoubles = (desiredItemCostRouble * barterConfig.priceRangeVariancePercent) / 100;
|
2023-03-03 15:23:46 +00:00
|
|
|
|
2024-10-19 10:58:25 +01:00
|
|
|
// Dict of items and their flea price (cached on first use)
|
|
|
|
const itemFleaPrices = this.getFleaPricesAsArray();
|
2023-03-03 15:23:46 +00:00
|
|
|
|
|
|
|
// Filter possible barters to items that match the price range + not itself
|
2024-10-19 10:58:25 +01:00
|
|
|
const itemsInsidePriceBounds = itemFleaPrices.filter(
|
|
|
|
(itemAndPrice) =>
|
|
|
|
itemAndPrice.price >= desiredItemCostRouble - offerCostVarianceRoubles &&
|
|
|
|
itemAndPrice.price <= desiredItemCostRouble + offerCostVarianceRoubles &&
|
|
|
|
itemAndPrice.tpl !== offerItems[0]._tpl, // Don't allow the item being sold to be chosen
|
2023-11-16 21:42:06 +00:00
|
|
|
);
|
2023-03-03 15:23:46 +00:00
|
|
|
|
|
|
|
// No items on flea have a matching price, fall back to currency
|
2024-10-19 10:58:25 +01:00
|
|
|
if (itemsInsidePriceBounds.length === 0) {
|
2023-10-10 11:03:20 +00:00
|
|
|
return this.createCurrencyBarterScheme(offerItems, false);
|
2023-03-03 15:23:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Choose random item from price-filtered flea items
|
2024-10-19 10:58:25 +01:00
|
|
|
const randomItem = this.randomUtil.getArrayValue(itemsInsidePriceBounds);
|
2023-03-03 15:23:46 +00:00
|
|
|
|
2023-11-16 21:42:06 +00:00
|
|
|
return [{ count: barterItemCount, _tpl: randomItem.tpl }];
|
2023-03-03 15:23:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get an array of flea prices + item tpl, cached in generator class inside `allowedFleaPriceItemsForBarter`
|
|
|
|
* @returns array with tpl/price values
|
|
|
|
*/
|
2024-10-19 10:58:25 +01:00
|
|
|
protected getFleaPricesAsArray(): ITplWithFleaPrice[] {
|
2023-03-03 15:23:46 +00:00
|
|
|
// Generate if needed
|
2024-07-23 11:12:53 -04:00
|
|
|
if (!this.allowedFleaPriceItemsForBarter) {
|
2024-05-28 22:24:52 +01:00
|
|
|
const fleaPrices = this.databaseService.getPrices();
|
2023-04-06 15:37:35 +01:00
|
|
|
|
2024-06-30 21:39:58 +01:00
|
|
|
// Only get prices for items that also exist in items.json
|
|
|
|
const filteredFleaItems = Object.entries(fleaPrices)
|
|
|
|
.map(([tpl, price]) => ({ tpl: tpl, price: price }))
|
|
|
|
.filter((item) => this.itemHelper.getItem(item.tpl)[0]);
|
2023-04-06 15:37:35 +01:00
|
|
|
|
2024-06-30 21:39:58 +01:00
|
|
|
const itemTypeBlacklist = this.ragfairConfig.dynamic.barter.itemTypeBlacklist;
|
|
|
|
this.allowedFleaPriceItemsForBarter = filteredFleaItems.filter(
|
|
|
|
(item) => !this.itemHelper.isOfBaseclasses(item.tpl, itemTypeBlacklist),
|
2023-11-16 21:42:06 +00:00
|
|
|
);
|
2023-03-03 15:23:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return this.allowedFleaPriceItemsForBarter;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Create a random currency-based barter scheme for an array of items
|
2024-01-12 21:59:30 +00:00
|
|
|
* @param offerWithChildren Items on offer
|
2023-10-10 11:03:20 +00:00
|
|
|
* @param isPackOffer Is the barter scheme being created for a pack offer
|
|
|
|
* @param multipler What to multiply the resulting price by
|
2023-03-03 15:23:46 +00:00
|
|
|
* @returns Barter scheme for offer
|
|
|
|
*/
|
2024-02-02 13:54:07 -05:00
|
|
|
protected createCurrencyBarterScheme(
|
2024-09-24 12:47:29 +01:00
|
|
|
offerWithChildren: IItem[],
|
2024-02-02 13:54:07 -05:00
|
|
|
isPackOffer: boolean,
|
|
|
|
multipler = 1,
|
2024-07-23 11:12:53 -04:00
|
|
|
): IBarterScheme[] {
|
2023-03-03 15:23:46 +00:00
|
|
|
const currency = this.ragfairServerHelper.getDynamicOfferCurrency();
|
2024-07-23 11:12:53 -04:00
|
|
|
const price =
|
|
|
|
this.ragfairPriceService.getDynamicOfferPriceForOffer(offerWithChildren, currency, isPackOffer) * multipler;
|
2023-03-03 15:23:46 +00:00
|
|
|
|
2023-11-16 21:42:06 +00:00
|
|
|
return [{ count: price, _tpl: currency }];
|
2023-03-03 15:23:46 +00:00
|
|
|
}
|
2023-11-16 21:42:06 +00:00
|
|
|
}
|