2023-03-03 15:23:46 +00:00
|
|
|
import { inject, injectable } from "tsyringe";
|
|
|
|
|
2023-10-19 17:21:17 +00:00
|
|
|
import { RagfairAssortGenerator } from "@spt-aki/generators/RagfairAssortGenerator";
|
|
|
|
import { HandbookHelper } from "@spt-aki/helpers/HandbookHelper";
|
|
|
|
import { ItemHelper } from "@spt-aki/helpers/ItemHelper";
|
|
|
|
import { PaymentHelper } from "@spt-aki/helpers/PaymentHelper";
|
|
|
|
import { PresetHelper } from "@spt-aki/helpers/PresetHelper";
|
|
|
|
import { RagfairServerHelper } from "@spt-aki/helpers/RagfairServerHelper";
|
|
|
|
import { Item } from "@spt-aki/models/eft/common/tables/IItem";
|
|
|
|
import { ITemplateItem } from "@spt-aki/models/eft/common/tables/ITemplateItem";
|
|
|
|
import { IBarterScheme } from "@spt-aki/models/eft/common/tables/ITrader";
|
|
|
|
import { IRagfairOffer, OfferRequirement } from "@spt-aki/models/eft/ragfair/IRagfairOffer";
|
|
|
|
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 { Money } from "@spt-aki/models/enums/Money";
|
|
|
|
import { Dynamic, IRagfairConfig } from "@spt-aki/models/spt/config/IRagfairConfig";
|
|
|
|
import { ILogger } from "@spt-aki/models/spt/utils/ILogger";
|
|
|
|
import { ConfigServer } from "@spt-aki/servers/ConfigServer";
|
|
|
|
import { DatabaseServer } from "@spt-aki/servers/DatabaseServer";
|
|
|
|
import { SaveServer } from "@spt-aki/servers/SaveServer";
|
|
|
|
import { FenceService } from "@spt-aki/services/FenceService";
|
|
|
|
import { LocalisationService } from "@spt-aki/services/LocalisationService";
|
|
|
|
import { RagfairOfferService } from "@spt-aki/services/RagfairOfferService";
|
|
|
|
import { RagfairPriceService } from "@spt-aki/services/RagfairPriceService";
|
|
|
|
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 15:23:46 +00:00
|
|
|
|
|
|
|
@injectable()
|
|
|
|
export class RagfairOfferGenerator
|
|
|
|
{
|
|
|
|
protected ragfairConfig: IRagfairConfig;
|
|
|
|
protected allowedFleaPriceItemsForBarter: { tpl: string; price: number; }[];
|
|
|
|
|
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(
|
|
|
|
@inject("WinstonLogger") protected logger: ILogger,
|
|
|
|
@inject("JsonUtil") protected jsonUtil: JsonUtil,
|
|
|
|
@inject("HashUtil") protected hashUtil: HashUtil,
|
|
|
|
@inject("RandomUtil") protected randomUtil: RandomUtil,
|
|
|
|
@inject("TimeUtil") protected timeUtil: TimeUtil,
|
|
|
|
@inject("DatabaseServer") protected databaseServer: DatabaseServer,
|
|
|
|
@inject("RagfairServerHelper") protected ragfairServerHelper: RagfairServerHelper,
|
|
|
|
@inject("HandbookHelper") protected handbookHelper: HandbookHelper,
|
|
|
|
@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,
|
2023-03-03 15:23:46 +00:00
|
|
|
)
|
|
|
|
{
|
|
|
|
this.ragfairConfig = this.configServer.getConfig(ConfigTypes.RAGFAIR);
|
|
|
|
}
|
|
|
|
|
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
|
2023-07-25 14:04:21 +01:00
|
|
|
* @returns IRagfairOffer
|
|
|
|
*/
|
2023-11-16 21:42:06 +00:00
|
|
|
public createFleaOffer(
|
|
|
|
userID: string,
|
|
|
|
time: number,
|
|
|
|
items: Item[],
|
|
|
|
barterScheme: IBarterScheme[],
|
|
|
|
loyalLevel: number,
|
|
|
|
sellInOnePiece = false,
|
|
|
|
): IRagfairOffer
|
2023-07-25 14:04:21 +01:00
|
|
|
{
|
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
|
|
|
|
* @param sellInOnePiece Set StackObjectsCount to 1
|
|
|
|
* @returns IRagfairOffer
|
|
|
|
*/
|
2023-11-16 21:42:06 +00:00
|
|
|
protected createOffer(
|
|
|
|
userID: string,
|
|
|
|
time: number,
|
|
|
|
items: Item[],
|
|
|
|
barterScheme: IBarterScheme[],
|
|
|
|
loyalLevel: number,
|
|
|
|
sellInOnePiece = false,
|
|
|
|
): IRagfairOffer
|
2023-03-03 15:23:46 +00:00
|
|
|
{
|
|
|
|
const isTrader = this.ragfairServerHelper.isTrader(userID);
|
|
|
|
|
|
|
|
const offerRequirements: OfferRequirement[] = [];
|
|
|
|
for (const barter of barterScheme)
|
|
|
|
{
|
|
|
|
const requirement: OfferRequirement = {
|
|
|
|
_tpl: barter._tpl,
|
2023-10-10 11:03:20 +00:00
|
|
|
count: +barter.count.toFixed(2),
|
2023-11-16 21:42:06 +00:00
|
|
|
onlyFunctional: barter.onlyFunctional ?? false,
|
2023-03-03 15:23:46 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
offerRequirements.push(requirement);
|
|
|
|
}
|
|
|
|
|
2023-11-16 21:42:06 +00:00
|
|
|
const itemCount = items.filter((x) => x.slotId === "hideout").length;
|
2023-10-10 11:03:20 +00:00
|
|
|
const roublePrice = Math.round(this.convertOfferRequirementsIntoRoubles(offerRequirements));
|
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,
|
2023-03-03 15:23:46 +00:00
|
|
|
user: {
|
|
|
|
id: this.getTraderId(userID),
|
|
|
|
memberType: (userID === "ragfair")
|
|
|
|
? MemberCategory.DEFAULT
|
|
|
|
: this.ragfairServerHelper.getMemberType(userID),
|
|
|
|
nickname: this.ragfairServerHelper.getNickname(userID),
|
|
|
|
rating: this.getRating(userID),
|
|
|
|
isRatingGrowing: this.getRatingGrowing(userID),
|
2023-11-16 21:42:06 +00:00
|
|
|
avatar: this.getAvatarUrl(isTrader, userID),
|
2023-03-03 15:23:46 +00:00
|
|
|
},
|
|
|
|
root: items[0]._id,
|
|
|
|
items: this.jsonUtil.clone(items),
|
|
|
|
requirements: offerRequirements,
|
|
|
|
requirementsCost: roublePrice,
|
2023-10-10 11:03:20 +00:00
|
|
|
itemsCost: Math.round(this.handbookHelper.getTemplatePrice(items[0]._tpl)), // Handbook price
|
2023-03-03 15:23:46 +00:00
|
|
|
summaryCost: roublePrice,
|
|
|
|
startTime: time,
|
|
|
|
endTime: this.getOfferEndTime(userID, time),
|
|
|
|
loyaltyLevel: loyalLevel,
|
|
|
|
sellInOnePiece: sellInOnePiece,
|
|
|
|
priority: false,
|
|
|
|
locked: false,
|
|
|
|
unlimitedCount: false,
|
|
|
|
notAvailable: false,
|
2023-11-16 21:42:06 +00:00
|
|
|
CurrentItemCount: itemCount,
|
2023-03-03 15:23:46 +00:00
|
|
|
};
|
|
|
|
|
2023-12-19 23:52:39 +00:00
|
|
|
this.offerCounter++;
|
|
|
|
|
2023-03-03 15:23:46 +00:00
|
|
|
return offer;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Calculate the offer price that's listed on the flea listing
|
|
|
|
* @param offerRequirements barter requirements for offer
|
|
|
|
* @returns rouble cost of offer
|
|
|
|
*/
|
2023-10-10 11:03:20 +00:00
|
|
|
protected convertOfferRequirementsIntoRoubles(offerRequirements: OfferRequirement[]): number
|
2023-03-03 15:23:46 +00:00
|
|
|
{
|
|
|
|
let roublePrice = 0;
|
|
|
|
for (const requirement of offerRequirements)
|
|
|
|
{
|
|
|
|
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
|
|
|
|
*/
|
|
|
|
protected getAvatarUrl(isTrader: boolean, userId: string): string
|
|
|
|
{
|
|
|
|
if (isTrader)
|
|
|
|
{
|
|
|
|
return this.databaseServer.getTables().traders[userId].base.avatar;
|
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
|
*/
|
|
|
|
protected calculateRoublePrice(currencyCount: number, currencyType: string): number
|
|
|
|
{
|
|
|
|
if (currencyType === Money.ROUBLES)
|
|
|
|
{
|
|
|
|
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
|
|
|
|
*/
|
|
|
|
protected getTraderId(userId: string): string
|
2023-03-03 15:23:46 +00:00
|
|
|
{
|
2023-07-25 14:04:21 +01:00
|
|
|
if (this.ragfairServerHelper.isPlayer(userId))
|
2023-03-03 15:23:46 +00:00
|
|
|
{
|
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
|
|
|
|
*/
|
|
|
|
protected getRating(userId: string): number
|
2023-03-03 15:23:46 +00:00
|
|
|
{
|
2023-07-25 14:04:21 +01:00
|
|
|
if (this.ragfairServerHelper.isPlayer(userId))
|
2023-03-03 15:23:46 +00:00
|
|
|
{
|
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
|
|
|
}
|
|
|
|
|
2023-07-25 14:04:21 +01:00
|
|
|
if (this.ragfairServerHelper.isTrader(userId))
|
2023-03-03 15:23:46 +00:00
|
|
|
{
|
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
|
|
|
|
*/
|
|
|
|
protected getRatingGrowing(userID: string): boolean
|
|
|
|
{
|
|
|
|
if (this.ragfairServerHelper.isPlayer(userID))
|
|
|
|
{
|
|
|
|
// player offer
|
|
|
|
return this.saveServer.getProfile(userID).characters.pmc.RagfairInfo.isRatingGrowing;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this.ragfairServerHelper.isTrader(userID))
|
|
|
|
{
|
|
|
|
// 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
|
|
|
|
*/
|
|
|
|
protected getOfferEndTime(userID: string, time: number): number
|
|
|
|
{
|
|
|
|
if (this.ragfairServerHelper.isPlayer(userID))
|
|
|
|
{
|
2023-12-16 15:11:11 +00:00
|
|
|
// Player offer = current time + offerDurationTimeInHour;
|
|
|
|
const offerDurationTimeHours = this.databaseServer.getTables().globals.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
|
|
|
}
|
|
|
|
|
|
|
|
if (this.ragfairServerHelper.isTrader(userID))
|
|
|
|
{
|
|
|
|
// Trader offer
|
|
|
|
return this.databaseServer.getTables().traders[userID].base.nextResupply;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Generated fake-player offer
|
2023-11-16 21:42:06 +00:00
|
|
|
return Math.round(
|
|
|
|
time
|
|
|
|
+ this.randomUtil.getInt(
|
|
|
|
this.ragfairConfig.dynamic.endTimeSeconds.min,
|
|
|
|
this.ragfairConfig.dynamic.endTimeSeconds.max,
|
|
|
|
),
|
|
|
|
);
|
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
|
|
|
|
*/
|
|
|
|
public async generateDynamicOffers(expiredOffers: Item[] = null): Promise<void>
|
|
|
|
{
|
|
|
|
const config = this.ragfairConfig.dynamic;
|
|
|
|
|
|
|
|
// get assort items from param if they exist, otherwise grab freshly generated assorts
|
2023-11-16 21:42:06 +00:00
|
|
|
const assortItemsToProcess: Item[] = expiredOffers
|
2023-03-03 15:23:46 +00:00
|
|
|
? expiredOffers
|
|
|
|
: this.ragfairAssortGenerator.getAssortItems();
|
|
|
|
|
|
|
|
// Store all functions to create an offer for every item and pass into Promise.all to run async
|
|
|
|
const assorOffersForItemsProcesses = [];
|
|
|
|
for (const assortItemIndex in assortItemsToProcess)
|
|
|
|
{
|
2023-11-16 21:42:06 +00:00
|
|
|
assorOffersForItemsProcesses.push(
|
|
|
|
this.createOffersForItems(assortItemIndex, assortItemsToProcess, expiredOffers, config),
|
|
|
|
);
|
2023-03-03 15:23:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
await Promise.all(assorOffersForItemsProcesses);
|
|
|
|
}
|
2023-07-25 14:04:21 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @param assortItemIndex Index of assort item
|
|
|
|
* @param assortItemsToProcess Item array containing index
|
|
|
|
* @param expiredOffers Currently expired offers on flea
|
|
|
|
* @param config Ragfair dynamic config
|
|
|
|
*/
|
2023-11-16 21:42:06 +00:00
|
|
|
protected async createOffersForItems(
|
|
|
|
assortItemIndex: string,
|
|
|
|
assortItemsToProcess: Item[],
|
|
|
|
expiredOffers: Item[],
|
|
|
|
config: Dynamic,
|
|
|
|
): Promise<void>
|
2023-03-03 15:23:46 +00:00
|
|
|
{
|
|
|
|
const assortItem = assortItemsToProcess[assortItemIndex];
|
|
|
|
const itemDetails = this.itemHelper.getItem(assortItem._tpl);
|
|
|
|
|
|
|
|
const isPreset = this.presetHelper.isPreset(assortItem._id);
|
|
|
|
|
|
|
|
// Only perform checks on newly generated items, skip expired items being refreshed
|
|
|
|
if (!(expiredOffers || this.ragfairServerHelper.isItemValidRagfairItem(itemDetails)))
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get item + sub-items if preset, otherwise just get item
|
2024-01-14 21:12:56 +00:00
|
|
|
const itemWithChildren: Item[] = isPreset
|
2023-03-03 15:23:46 +00:00
|
|
|
? this.ragfairServerHelper.getPresetItems(assortItem)
|
2023-11-16 21:42:06 +00:00
|
|
|
: [
|
|
|
|
...[assortItem],
|
2024-01-14 21:12:56 +00:00
|
|
|
...this.itemHelper.findAndReturnChildrenByAssort(assortItem._id, this.ragfairAssortGenerator.getAssortItems(),
|
2023-11-16 21:42:06 +00:00
|
|
|
),
|
|
|
|
];
|
2023-03-03 15:23:46 +00:00
|
|
|
|
|
|
|
// Get number of offers to create
|
|
|
|
// Limit to 1 offer when processing expired
|
2023-11-16 21:42:06 +00:00
|
|
|
const offerCount = expiredOffers
|
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 = [];
|
|
|
|
for (let index = 0; index < offerCount; index++)
|
|
|
|
{
|
2024-01-14 21:12:56 +00:00
|
|
|
delete itemWithChildren[0].parentId;
|
|
|
|
delete itemWithChildren[0].slotId;
|
|
|
|
|
|
|
|
if (!isPreset)
|
|
|
|
{
|
|
|
|
// presets get unique id generated during getPresetItems() earlier
|
|
|
|
itemWithChildren[0]._id = this.hashUtil.generate();
|
|
|
|
}
|
|
|
|
|
|
|
|
delete itemWithChildren[0].parentId;
|
|
|
|
|
|
|
|
assortSingleOfferProcesses.push(this.createSingleOfferForItem(itemWithChildren, isPreset, itemDetails));
|
2023-03-03 15:23:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
await Promise.all(assortSingleOfferProcesses);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Create one flea offer for a specific item
|
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
|
|
|
|
* @param itemDetails 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-01-12 21:59:30 +00:00
|
|
|
itemWithChildren: Item[],
|
2023-11-16 21:42:06 +00:00
|
|
|
isPreset: boolean,
|
|
|
|
itemDetails: [boolean, ITemplateItem],
|
|
|
|
): Promise<void>
|
2023-03-03 15:23:46 +00:00
|
|
|
{
|
2023-10-10 11:03:20 +00:00
|
|
|
// Set stack size to random value
|
2024-01-12 21:59:30 +00: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);
|
2023-11-16 21:42:06 +00:00
|
|
|
const isPackOffer = this.randomUtil.getChance100(this.ragfairConfig.dynamic.pack.chancePercent)
|
2023-10-10 11:03:20 +00:00
|
|
|
&& !isBarterOffer
|
2024-01-12 21:59:30 +00:00
|
|
|
&& itemWithChildren.length === 1
|
|
|
|
&& this.itemHelper.isOfBaseclasses(itemWithChildren[0]._tpl, this.ragfairConfig.dynamic.pack.itemTypeWhitelist);
|
2023-10-10 11:03:20 +00:00
|
|
|
const randomUserId = this.hashUtil.generate();
|
2023-03-03 15:23:46 +00:00
|
|
|
|
2023-10-10 11:03:20 +00:00
|
|
|
let barterScheme: IBarterScheme[];
|
|
|
|
if (isPackOffer)
|
|
|
|
{
|
|
|
|
// 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);
|
2023-10-10 11:03:20 +00:00
|
|
|
}
|
|
|
|
else if (isBarterOffer)
|
|
|
|
{
|
|
|
|
// Apply randomised properties
|
2024-01-12 21:59:30 +00:00
|
|
|
itemWithChildren = this.randomiseItemUpdProperties(randomUserId, itemWithChildren, itemDetails[1]);
|
|
|
|
barterScheme = this.createBarterBarterScheme(itemWithChildren);
|
2023-10-10 11:03:20 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Apply randomised properties
|
2024-01-12 21:59:30 +00:00
|
|
|
itemWithChildren = this.randomiseItemUpdProperties(randomUserId, itemWithChildren, itemDetails[1]);
|
|
|
|
barterScheme = this.createCurrencyBarterScheme(itemWithChildren, isPackOffer);
|
2023-10-10 11:03:20 +00:00
|
|
|
}
|
2023-03-03 15:23:46 +00:00
|
|
|
|
|
|
|
const offer = this.createFleaOffer(
|
2023-10-10 11:03:20 +00:00
|
|
|
randomUserId,
|
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
|
|
|
|
*/
|
|
|
|
public generateFleaOffersForTrader(traderID: string): void
|
|
|
|
{
|
|
|
|
// Ensure old offers don't exist
|
|
|
|
this.ragfairOfferService.removeAllOffersByTrader(traderID);
|
|
|
|
|
|
|
|
// Add trader offers
|
|
|
|
const time = this.timeUtil.getTimestamp();
|
|
|
|
const trader = this.databaseServer.getTables().traders[traderID];
|
|
|
|
const assorts = trader.assort;
|
|
|
|
|
|
|
|
// Trader assorts / assort items are missing
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (const item of assorts.items)
|
|
|
|
{
|
2023-10-10 11:03:20 +00:00
|
|
|
// We only want to process 'base' items, no children
|
2023-03-03 15:23:46 +00:00
|
|
|
if (item.slotId !== "hideout")
|
|
|
|
{
|
|
|
|
// skip mod items
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2023-10-10 11:03:20 +00:00
|
|
|
// Run blacklist check on trader offers
|
2023-03-03 15:23:46 +00:00
|
|
|
if (this.ragfairConfig.dynamic.blacklist.traderItems)
|
|
|
|
{
|
|
|
|
const itemDetails = this.itemHelper.getItem(item._tpl);
|
|
|
|
if (!itemDetails[0])
|
|
|
|
{
|
|
|
|
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
|
|
|
|
if (this.ragfairConfig.dynamic.blacklist.enableBsgList && !itemDetails[1]._props.CanSellOnRagfair)
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const isPreset = this.presetHelper.isPreset(item._id);
|
2023-11-16 21:42:06 +00:00
|
|
|
const items: Item[] = 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];
|
|
|
|
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];
|
|
|
|
|
2023-10-10 11:03:20 +00:00
|
|
|
const offer = this.createFleaOffer(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
|
|
|
|
* @param itemWithMods Item and mods, get condition of first item (only first array item is used)
|
|
|
|
* @param itemDetails db details of first item
|
2023-11-16 21:42:06 +00:00
|
|
|
* @returns
|
2023-03-03 15:23:46 +00:00
|
|
|
*/
|
2023-10-10 11:03:20 +00:00
|
|
|
protected randomiseItemUpdProperties(userID: string, itemWithMods: Item[], itemDetails: ITemplateItem): Item[]
|
2023-03-03 15:23:46 +00:00
|
|
|
{
|
2023-11-16 21:42:06 +00:00
|
|
|
// Add any missing properties to first item in array
|
2023-03-03 15:23:46 +00:00
|
|
|
itemWithMods[0] = this.addMissingConditions(itemWithMods[0]);
|
|
|
|
|
|
|
|
if (!(this.ragfairServerHelper.isPlayer(userID) || this.ragfairServerHelper.isTrader(userID)))
|
|
|
|
{
|
|
|
|
const parentId = this.getDynamicConditionIdForTpl(itemDetails._id);
|
|
|
|
if (!parentId)
|
|
|
|
{
|
|
|
|
// No condition details found, don't proceed with modifying item conditions
|
|
|
|
return itemWithMods;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Roll random chance to randomise item condition
|
|
|
|
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
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return itemWithMods;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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
|
|
|
|
*/
|
|
|
|
protected getDynamicConditionIdForTpl(tpl: string): string
|
|
|
|
{
|
|
|
|
// Get keys from condition config dictionary
|
|
|
|
const configConditions = Object.keys(this.ragfairConfig.dynamic.condition);
|
2023-11-16 21:42:06 +00:00
|
|
|
for (const baseClass of configConditions)
|
2023-03-03 15:23:46 +00:00
|
|
|
{
|
2023-11-16 21:42:06 +00:00
|
|
|
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-01-12 21:59:30 +00:00
|
|
|
protected randomiseItemCondition(conditionSettingsId: string, itemWithMods: Item[], itemDetails: ITemplateItem): void
|
2023-03-03 15:23:46 +00:00
|
|
|
{
|
2024-01-12 21:59:30 +00:00
|
|
|
const rootItem = itemWithMods[0];
|
|
|
|
|
|
|
|
const itemConditionValues = this.ragfairConfig.dynamic.condition[conditionSettingsId];
|
2023-11-16 21:42:06 +00:00
|
|
|
const multiplier = this.randomUtil.getFloat(
|
2024-01-12 21:59:30 +00:00
|
|
|
itemConditionValues.min,
|
|
|
|
itemConditionValues.max,
|
2023-11-16 21:42:06 +00:00
|
|
|
);
|
2023-03-03 15:23:46 +00:00
|
|
|
|
2024-01-14 22:28:53 +00:00
|
|
|
// Randomise armor + plates + armor related things
|
|
|
|
if (this.itemHelper.armorItemCanHoldMods(rootItem._tpl)
|
|
|
|
|| this.itemHelper.isOfBaseclasses(rootItem._tpl, [BaseClasses.ARMOR_PLATE, BaseClasses.ARMORED_EQUIPMENT]))
|
2023-03-03 15:23:46 +00:00
|
|
|
{
|
2024-01-13 09:15:11 +00:00
|
|
|
// Chance to not adjust armor
|
2024-01-13 20:47:59 +00:00
|
|
|
if (!this.randomUtil.getChance100(this.ragfairConfig.dynamic.condition[BaseClasses.ARMORED_EQUIPMENT].conditionChance * 100))
|
2024-01-13 09:15:11 +00:00
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2024-01-12 21:59:30 +00:00
|
|
|
this.randomiseArmorDurabilityValues(itemWithMods);
|
2023-03-03 15:23:46 +00:00
|
|
|
|
2024-01-13 20:47:59 +00:00
|
|
|
// Add hits to visor
|
|
|
|
const visorMod = itemWithMods.find(item => item.parentId === BaseClasses.ARMORED_EQUIPMENT && item.slotId === "mod_equipment_000");
|
|
|
|
if (this.randomUtil.getChance100(25)
|
|
|
|
&& visorMod)
|
|
|
|
{
|
|
|
|
if (!visorMod.upd)
|
|
|
|
{
|
|
|
|
visorMod.upd = {};
|
|
|
|
}
|
|
|
|
|
|
|
|
visorMod.upd.FaceShield = {
|
|
|
|
Hits: this.randomUtil.getInt(1,3)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-01-12 21:59:30 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Randomise Weapons
|
|
|
|
if (this.itemHelper.isOfBaseclass(itemDetails._id, BaseClasses.WEAPON))
|
|
|
|
{
|
|
|
|
this.randomiseWeaponDurabilityValues(itemWithMods[0], multiplier);
|
2023-10-10 11:03:20 +00:00
|
|
|
|
|
|
|
return;
|
2023-03-03 15:23:46 +00:00
|
|
|
}
|
|
|
|
|
2024-01-12 21:59:30 +00:00
|
|
|
if (rootItem.upd.MedKit)
|
2023-03-03 15:23:46 +00:00
|
|
|
{
|
|
|
|
// randomize health
|
2024-01-12 21:59:30 +00:00
|
|
|
rootItem.upd.MedKit.HpResource = Math.round(rootItem.upd.MedKit.HpResource * multiplier) || 1;
|
2023-10-10 11:03:20 +00:00
|
|
|
|
|
|
|
return;
|
2023-03-03 15:23:46 +00:00
|
|
|
}
|
|
|
|
|
2024-01-12 21:59:30 +00:00
|
|
|
if (rootItem.upd.Key && itemDetails._props.MaximumNumberOfUsage > 1)
|
2023-03-03 15:23:46 +00:00
|
|
|
{
|
|
|
|
// randomize key uses
|
2024-01-12 21:59:30 +00:00
|
|
|
rootItem.upd.Key.NumberOfUsages = Math.round(itemDetails._props.MaximumNumberOfUsage * (1 - multiplier)) || 0;
|
2023-10-10 11:03:20 +00:00
|
|
|
|
|
|
|
return;
|
2023-03-03 15:23:46 +00:00
|
|
|
}
|
|
|
|
|
2024-01-12 21:59:30 +00:00
|
|
|
if (rootItem.upd.FoodDrink)
|
2023-03-03 15:23:46 +00:00
|
|
|
{
|
|
|
|
// randomize food/drink value
|
2024-01-12 21:59:30 +00:00
|
|
|
rootItem.upd.FoodDrink.HpPercent = Math.round(itemDetails._props.MaxResource * multiplier) || 1;
|
2023-10-10 11:03:20 +00:00
|
|
|
|
|
|
|
return;
|
2023-03-03 15:23:46 +00:00
|
|
|
}
|
|
|
|
|
2024-01-12 21:59:30 +00:00
|
|
|
if (rootItem.upd.RepairKit)
|
2023-03-03 15:23:46 +00:00
|
|
|
{
|
|
|
|
// randomize repair kit (armor/weapon) uses
|
2024-01-12 21:59:30 +00:00
|
|
|
rootItem.upd.RepairKit.Resource = Math.round(itemDetails._props.MaxRepairResource * multiplier) || 1;
|
2023-10-10 11:03:20 +00:00
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this.itemHelper.isOfBaseclass(itemDetails._id, BaseClasses.FUEL))
|
|
|
|
{
|
|
|
|
const totalCapacity = itemDetails._props.MaxResource;
|
|
|
|
const remainingFuel = Math.round(totalCapacity * multiplier);
|
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
|
|
|
|
* @param item item (weapon/armor) to adjust
|
|
|
|
* @param multiplier Value to multiple durability by
|
|
|
|
*/
|
2024-01-12 21:59:30 +00:00
|
|
|
protected randomiseWeaponDurabilityValues(item: Item, multiplier: number): void
|
2023-03-03 15:23:46 +00:00
|
|
|
{
|
|
|
|
item.upd.Repairable.Durability = Math.round(item.upd.Repairable.Durability * multiplier) || 1;
|
|
|
|
|
|
|
|
// randomize max durability, store to a temporary value so we can still compare the max durability
|
2023-11-16 21:42:06 +00:00
|
|
|
let tempMaxDurability =
|
|
|
|
Math.round(
|
|
|
|
this.randomUtil.getFloat(item.upd.Repairable.Durability - 5, item.upd.Repairable.MaxDurability + 5),
|
|
|
|
) || item.upd.Repairable.Durability;
|
2023-03-03 15:23:46 +00:00
|
|
|
|
|
|
|
// clamp values to max/current
|
|
|
|
if (tempMaxDurability >= item.upd.Repairable.MaxDurability)
|
|
|
|
{
|
|
|
|
tempMaxDurability = item.upd.Repairable.MaxDurability;
|
|
|
|
}
|
|
|
|
if (tempMaxDurability < item.upd.Repairable.Durability)
|
|
|
|
{
|
|
|
|
tempMaxDurability = item.upd.Repairable.Durability;
|
|
|
|
}
|
|
|
|
|
2024-01-12 21:59:30 +00:00
|
|
|
// after clamping, assign to the item's properties
|
2023-03-03 15:23:46 +00:00
|
|
|
item.upd.Repairable.MaxDurability = tempMaxDurability;
|
|
|
|
}
|
|
|
|
|
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-01-12 21:59:30 +00:00
|
|
|
protected randomiseArmorDurabilityValues(armorWithMods: Item[]): void
|
|
|
|
{
|
2024-01-13 09:15:11 +00:00
|
|
|
const itemDurabilityConfigDict = this.ragfairConfig.dynamic.condition;
|
|
|
|
const childMultiplerValues = {};
|
|
|
|
for (const item of armorWithMods)
|
|
|
|
{
|
|
|
|
const itemDbDetails = this.itemHelper.getItem(item._tpl)[1];
|
|
|
|
if ((parseInt(<string>itemDbDetails._props.armorClass)) > 0)
|
2024-01-12 21:59:30 +00:00
|
|
|
{
|
2024-01-13 09:15:11 +00:00
|
|
|
if (!item.upd)
|
2024-01-12 21:59:30 +00:00
|
|
|
{
|
2024-01-13 09:15:11 +00:00
|
|
|
item.upd = {};
|
2024-01-12 21:59:30 +00:00
|
|
|
}
|
2024-01-13 09:15:11 +00:00
|
|
|
|
|
|
|
// Store mod types durabiltiy multiplier for later use in current/max durability value calculation
|
|
|
|
if (!childMultiplerValues[itemDbDetails._parent])
|
|
|
|
{
|
|
|
|
childMultiplerValues[itemDbDetails._parent] = this.randomUtil.getFloat(
|
|
|
|
itemDurabilityConfigDict[itemDbDetails._parent].min,
|
|
|
|
itemDurabilityConfigDict[itemDbDetails._parent].max,
|
|
|
|
) / itemDurabilityConfigDict[itemDbDetails._parent].max;
|
|
|
|
}
|
|
|
|
|
|
|
|
const modMultipler = childMultiplerValues[itemDbDetails._parent];
|
|
|
|
const maxDurability = Math.round(
|
|
|
|
this.randomUtil.getFloat(itemDbDetails._props.MaxDurability * this.randomUtil.getFloat(modMultipler, 1), itemDbDetails._props.MaxDurability),
|
|
|
|
);
|
|
|
|
const durability = Math.round(
|
|
|
|
this.randomUtil.getFloat(maxDurability * this.randomUtil.getFloat(modMultipler, 1), maxDurability),
|
|
|
|
);
|
|
|
|
item.upd.Repairable = {
|
|
|
|
Durability: durability || 1, // Never let value become 0
|
|
|
|
MaxDurability: maxDurability
|
|
|
|
};
|
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
|
|
|
|
* @returns Item with conditions added
|
|
|
|
*/
|
|
|
|
protected addMissingConditions(item: Item): Item
|
|
|
|
{
|
|
|
|
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
|
|
|
|
|
|
|
if (isRepairable && props.Durability > 0)
|
|
|
|
{
|
2023-11-16 21:42:06 +00:00
|
|
|
item.upd.Repairable = { Durability: props.Durability, MaxDurability: props.Durability };
|
2023-03-03 15:23:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (isMedkit && props.MaxHpResource > 0)
|
|
|
|
{
|
2023-11-16 21:42:06 +00:00
|
|
|
item.upd.MedKit = { HpResource: props.MaxHpResource };
|
2023-03-03 15:23:46 +00:00
|
|
|
}
|
|
|
|
|
2023-11-16 21:42:06 +00:00
|
|
|
if (isKey)
|
2023-03-03 15:23:46 +00:00
|
|
|
{
|
2023-11-16 21:42:06 +00:00
|
|
|
item.upd.Key = { NumberOfUsages: 0 };
|
2023-03-03 15:23:46 +00:00
|
|
|
}
|
|
|
|
|
2023-11-16 21:42:06 +00:00
|
|
|
if (isConsumable)
|
2023-03-03 15:23:46 +00:00
|
|
|
{
|
2023-11-16 21:42:06 +00:00
|
|
|
item.upd.FoodDrink = { HpPercent: props.MaxResource };
|
2023-03-03 15:23:46 +00:00
|
|
|
}
|
|
|
|
|
2023-11-16 21:42:06 +00:00
|
|
|
if (isRepairKit)
|
2023-03-03 15:23:46 +00:00
|
|
|
{
|
2023-11-16 21:42:06 +00:00
|
|
|
item.upd.RepairKit = { Resource: props.MaxRepairResource };
|
2023-03-03 15:23:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return item;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Create a barter-based barter scheme, if not possible, fall back to making barter scheme currency based
|
|
|
|
* @param offerItems Items for sale in offer
|
2023-07-25 14:04:21 +01:00
|
|
|
* @returns Barter scheme
|
2023-03-03 15:23:46 +00:00
|
|
|
*/
|
2023-10-10 11:03:20 +00:00
|
|
|
protected createBarterBarterScheme(offerItems: Item[]): IBarterScheme[]
|
2023-03-03 15:23:46 +00:00
|
|
|
{
|
2023-10-10 11:03:20 +00:00
|
|
|
// get flea price of item being sold
|
2023-11-16 21:42:06 +00:00
|
|
|
const priceOfItemOffer = this.ragfairPriceService.getDynamicOfferPriceForOffer(
|
|
|
|
offerItems,
|
|
|
|
Money.ROUBLES,
|
|
|
|
false,
|
|
|
|
);
|
2023-03-03 15:23:46 +00:00
|
|
|
|
|
|
|
// Dont make items under a designated rouble value into barter offers
|
|
|
|
if (priceOfItemOffer < this.ragfairConfig.dynamic.barter.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
|
2023-11-16 21:42:06 +00:00
|
|
|
const barterItemCount = this.randomUtil.getInt(
|
|
|
|
this.ragfairConfig.dynamic.barter.itemCountMin,
|
|
|
|
this.ragfairConfig.dynamic.barter.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
|
|
|
|
const desiredItemCost = Math.round(priceOfItemOffer / barterItemCount);
|
|
|
|
|
|
|
|
// amount to go above/below when looking for an item (Wiggle cost of item a little)
|
|
|
|
const offerCostVariance = desiredItemCost * this.ragfairConfig.dynamic.barter.priceRangeVariancePercent / 100;
|
|
|
|
|
|
|
|
const fleaPrices = this.getFleaPricesAsArray();
|
|
|
|
|
|
|
|
// Filter possible barters to items that match the price range + not itself
|
2023-11-16 21:42:06 +00:00
|
|
|
const filtered = fleaPrices.filter((x) =>
|
|
|
|
x.price >= desiredItemCost - offerCostVariance && x.price <= desiredItemCost + offerCostVariance
|
|
|
|
&& x.tpl !== offerItems[0]._tpl
|
|
|
|
);
|
2023-03-03 15:23:46 +00:00
|
|
|
|
|
|
|
// No items on flea have a matching price, fall back to currency
|
|
|
|
if (filtered.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
|
|
|
|
const randomItem = this.randomUtil.getArrayValue(filtered);
|
|
|
|
|
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
|
|
|
|
*/
|
|
|
|
protected getFleaPricesAsArray(): { tpl: string; price: number; }[]
|
|
|
|
{
|
|
|
|
// Generate if needed
|
|
|
|
if (!this.allowedFleaPriceItemsForBarter)
|
|
|
|
{
|
|
|
|
const fleaPrices = this.databaseServer.getTables().templates.prices;
|
|
|
|
const fleaArray = Object.entries(fleaPrices).map(([tpl, price]) => ({ tpl: tpl, price: price }));
|
2023-04-06 15:37:35 +01:00
|
|
|
|
|
|
|
// Only get item prices for items that also exist in items.json
|
2023-11-16 21:42:06 +00:00
|
|
|
const filteredItems = fleaArray.filter((x) => this.itemHelper.getItem(x.tpl)[0]);
|
2023-04-06 15:37:35 +01:00
|
|
|
|
2023-11-16 21:42:06 +00:00
|
|
|
this.allowedFleaPriceItemsForBarter = filteredItems.filter((x) =>
|
|
|
|
!this.itemHelper.isOfBaseclasses(x.tpl, this.ragfairConfig.dynamic.barter.itemTypeBlacklist)
|
|
|
|
);
|
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-01-12 21:59:30 +00:00
|
|
|
protected createCurrencyBarterScheme(offerWithChildren: Item[], isPackOffer: boolean, multipler = 1): IBarterScheme[]
|
2023-03-03 15:23:46 +00:00
|
|
|
{
|
|
|
|
const currency = this.ragfairServerHelper.getDynamicOfferCurrency();
|
2024-01-12 21:59:30 +00:00
|
|
|
const price = this.ragfairPriceService.getDynamicOfferPriceForOffer(offerWithChildren, currency, isPackOffer)
|
2023-11-16 21:42:06 +00:00
|
|
|
* 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
|
|
|
}
|