Files
SPT-Server-Build/Libraries/Core/Helpers/RagfairOfferHelper.cs
T
2025-01-26 19:46:22 +00:00

883 lines
32 KiB
C#

using System.Runtime.InteropServices.JavaScript;
using SptCommon.Annotations;
using Core.Models.Eft.Common;
using Core.Models.Eft.Common.Tables;
using Core.Models.Eft.ItemEvent;
using Core.Models.Eft.Profile;
using Core.Models.Eft.Ragfair;
using Core.Models.Enums;
using Core.Models.Spt.Config;
using Core.Models.Utils;
using Core.Routers;
using Core.Servers;
using Core.Services;
using Core.Utils;
using SptCommon.Extensions;
namespace Core.Helpers;
[Injectable]
public class RagfairOfferHelper(
ISptLogger<RagfairOfferHelper> logger,
TimeUtil timeUtil,
HashUtil hashUtil,
EventOutputHolder eventOutputHolder,
DatabaseService databaseService,
TraderHelper traderHelper,
SaveServer saveServer,
ItemHelper itemHelper,
BotHelper botHelper,
PaymentHelper paymentHelper,
PresetHelper presetHelper,
ProfileHelper profileHelper,
QuestHelper questHelper,
RagfairServerHelper ragfairServerHelper,
RagfairSortHelper ragfairSortHelper,
RagfairHelper ragfairHelper,
RagfairOfferService ragfairOfferService,
RagfairRequiredItemsService ragfairRequiredItemsService,
LocaleService localeService,
LocalisationService localisationService,
MailSendService mailSendService,
ConfigServer configServer
)
{
protected static string goodSoldTemplate = "5bdabfb886f7743e152e867e 0"; // Your {soldItem} {itemCount} items were bought by {buyerNickname}.
protected RagfairConfig ragfairConfig = configServer.GetConfig<RagfairConfig>();
protected QuestConfig questConfig = configServer.GetConfig<QuestConfig>();
protected BotConfig botConfig = configServer.GetConfig<BotConfig>();
/**
* Passthrough to ragfairOfferService.getOffers(), get flea offers a player should see
* @param searchRequest Data from client
* @param itemsToAdd ragfairHelper.filterCategories()
* @param traderAssorts Trader assorts
* @param pmcData Player profile
* @returns Offers the player should see
*/
public List<RagfairOffer> GetValidOffers(
SearchRequestData searchRequest,
List<string> itemsToAdd,
Dictionary<string, TraderAssort> traderAssorts,
PmcData pmcData
)
{
var playerIsFleaBanned = profileHelper.PlayerIsFleaBanned(pmcData);
var tieredFlea = ragfairConfig.TieredFlea;
var tieredFleaLimitTypes = tieredFlea.UnlocksType.Keys;
return ragfairOfferService.GetOffers().Where((offer) => {
if (!PassesSearchFilterCriteria(searchRequest, offer, pmcData)) {
return false;
}
var isDisplayable = IsDisplayableOffer(
searchRequest,
itemsToAdd,
traderAssorts,
offer,
pmcData,
playerIsFleaBanned
);
if (!isDisplayable) {
return false;
}
// Not trader offer + tiered flea enabled
if (tieredFlea.Enabled && !OfferIsFromTrader(offer)) {
CheckAndLockOfferFromPlayerTieredFlea(tieredFlea, offer, tieredFleaLimitTypes, pmcData.Info.Level);
}
return true;
});
}
/**
* Disable offer if item is flagged by tiered flea config
* @param tieredFlea Tiered flea settings from ragfair config
* @param offer Ragfair offer to check
* @param tieredFleaLimitTypes Dict of item types with player level to be viewable
* @param playerLevel Level of player viewing offer
*/
protected void CheckAndLockOfferFromPlayerTieredFlea(
TieredFlea tieredFlea,
RagfairOffer offer,
List<string> tieredFleaLimitTypes,
int playerLevel
)
{
var offerItemTpl = offer.Items[0].Template;
if (tieredFlea?.AmmoTplUnlocks != null && itemHelper.IsOfBaseclass(offerItemTpl, BaseClasses.AMMO)) {
var unlockLevel = tieredFlea.AmmoTplUnlocks[offerItemTpl];
if (unlockLevel != null && playerLevel < unlockLevel) {
offer.Locked = true;
return;
}
}
// Check for a direct level requirement for the offer item
var itemLevelRequirement = tieredFlea.UnlocksTpl[offerItemTpl];
if (itemLevelRequirement != null) {
if (playerLevel < itemLevelRequirement) {
offer.Locked = true;
return;
}
}
// Optimisation - Ensure the item has at least one of the limited base types
if (itemHelper.IsOfBaseclasses(offerItemTpl, tieredFleaLimitTypes)) {
// Loop over all flea types to find the matching one
foreach (var tieredItemType in tieredFleaLimitTypes) {
if (itemHelper.IsOfBaseclass(offerItemTpl, tieredItemType)) {
if (playerLevel < tieredFlea.UnlocksType[tieredItemType]) {
offer.Locked = true;
}
break;
}
}
}
}
/**
* Get matching offers that require the desired item and filter out offers from non traders if player is below ragfair unlock level
* @param searchRequest Search request from client
* @param pmcDataPlayer profile
* @returns Matching IRagfairOffer objects
*/
public List<RagfairOffer> GetOffersThatRequireItem(SearchRequestData searchRequest, PmcData pmcData)
{
// Get all offers that requre the desired item and filter out offers from non traders if player below ragifar unlock
var requiredOffers = ragfairRequiredItemsService.GetRequiredItemsById(searchRequest.NeededSearchId);
var tieredFlea = ragfairConfig.TieredFlea;
var tieredFleaLimitTypes = tieredFlea.UnlocksType.Keys.ToList();
return requiredOffers.Where(offer => {
if (!PassesSearchFilterCriteria(searchRequest, offer, pmcData)) {
return false;
}
if (tieredFlea.Enabled && !OfferIsFromTrader(offer)) {
CheckAndLockOfferFromPlayerTieredFlea(tieredFlea, offer, tieredFleaLimitTypes, pmcData.Info.Level.Value);
}
return true;
});
}
/**
* Get offers from flea/traders specifically when building weapon preset
* @param searchRequest Search request data
* @param itemsToAdd string array of item tpls to search for
* @param traderAssorts All trader assorts player can access/buy
* @param pmcData Player profile
* @returns IRagfairOffer array
*/
public List<RagfairOffer> GetOffersForBuild(
SearchRequestData searchRequest,
List<string> itemsToAdd,
Dictionary<string, TraderAssort> traderAssorts,
PmcData pmcData
)
{
var offersMap = new Dictionary<string, List<RagfairOffer>>();
var offersToReturn = new List<RagfairOffer>();
var playerIsFleaBanned = profileHelper.PlayerIsFleaBanned(pmcData);
var tieredFlea = ragfairConfig.TieredFlea;
var tieredFleaLimitTypes = tieredFlea.UnlocksType.Keys.ToList();
foreach (var desiredItemTpl in searchRequest.BuildItems.Keys) {
var matchingOffers = ragfairOfferService.GetOffersOfType(desiredItemTpl);
if (matchingOffers == null) {
// No offers found for this item, skip
continue;
}
foreach (var offer in matchingOffers) {
// Dont show pack offers
if (offer.SellInOnePiece ?? false) {
continue;
}
if (!PassesSearchFilterCriteria(searchRequest, offer, pmcData)) {
continue;
}
if (!IsDisplayableOffer(
searchRequest,
itemsToAdd,
traderAssorts,
offer,
pmcData,
playerIsFleaBanned)
) {
continue;
}
if (OfferIsFromTrader(offer)) {
if (TraderBuyRestrictionReached(offer)) {
continue;
}
if (TraderOutOfStock(offer)) {
continue;
}
if (TraderOfferItemQuestLocked(offer, traderAssorts)) {
continue;
}
if (TraderOfferLockedBehindLoyaltyLevel(offer, pmcData)) {
continue;
}
}
// Tiered flea and not trader offer
if (tieredFlea.Enabled && !OfferIsFromTrader(offer)) {
CheckAndLockOfferFromPlayerTieredFlea(
tieredFlea,
offer,
tieredFleaLimitTypes,
pmcData.Info.Level.Value
);
// Do not add offer to build if user does not have access to it
if (offer.Locked ?? false) {
continue;
}
}
var key = offer.Items[0].Template;
if (!offersMap.ContainsKey(key)) {
offersMap.Add(key, []);
}
offersMap[key].Add(offer);
}
}
// Get best offer for each item to show on screen
foreach (var possibleOffers in offersMap.Values) {
// Remove offers with locked = true (quest locked) when > 1 possible offers
// single trader item = shows greyed out
// multiple offers for item = is greyed out
if (possibleOffers.Count > 1) {
var lockedOffers = GetLoyaltyLockedOffers(possibleOffers, pmcData);
// Exclude locked offers + above loyalty locked offers if at least 1 was found
possibleOffers = possibleOffers.Where((offer) => !(offer.Locked || lockedOffers.includes(offer._id)));
// Exclude trader offers over their buy restriction limit
possibleOffers = getOffersInsideBuyRestrictionLimits(possibleOffers);
}
// Sort offers by price and pick the best
var offer = ragfairSortHelper.sortOffers(possibleOffers, RagfairSort.PRICE, 0)[0];
offersToReturn.push(offer);
}
return offersToReturn;
}
/**
* Get offers that have not exceeded buy limits
* @param possibleOffers offers to process
* @returns Offers
*/
protected List<RagfairOffer> GetOffersInsideBuyRestrictionLimits(List<RagfairOffer> possibleOffers) {
// Check offer has buy limit + is from trader + current buy count is at or over max
return possibleOffers.Where((offer) => {
if (
offer.BuyRestrictionMax != null &&
OfferIsFromTrader(offer) &&
offer.BuyRestrictionCurrent >= offer.BuyRestrictionMax
) {
if (offer.BuyRestrictionCurrent >= offer.BuyRestrictionMax) {
return false;
}
}
// Doesnt have buy limits, retrun offer
return true;
});
}
/**
* Check if offer is from trader standing the player does not have
* @param offer Offer to check
* @param pmcProfile Player profile
* @returns True if item is locked, false if item is purchaseable
*/
protected bool TraderOfferLockedBehindLoyaltyLevel(RagfairOffer offer, PmcData pmcProfile)
{
var userTraderSettings = pmcProfile.TradersInfo[offer.User.Id];
return userTraderSettings.LoyaltyLevel < offer.LoyaltyLevel;
}
/**
* Check if offer item is quest locked for current player by looking at sptQuestLocked property in traders barter_scheme
* @param offer Offer to check is quest locked
* @param traderAssorts all trader assorts for player
* @returns true if quest locked
*/
public bool TraderOfferItemQuestLocked(RagfairOffer offer, Dictionary<string, TraderAssort> traderAssorts)
{
return offer.Items?.Any(
i =>
traderAssorts[offer.User.Id]
.BarterScheme[i.Id]
?.Any((bs1) => bs1?.Any((bs2) => bs2.SptQuestLocked ?? false) ?? false) ??
false
) ?? false;
}
/**
* Has trader offer ran out of stock to sell to player
* @param offer Offer to check stock of
* @returns true if out of stock
*/
protected bool TraderOutOfStock(RagfairOffer offer)
{
if (offer?.Items?.Count == 0) {
return true;
}
return offer.Items[0]?.Upd?.StackObjectsCount == 0;
}
/**
* Check if trader offers' BuyRestrictionMax value has been reached
* @param offer Offer to check restriction properties of
* @returns true if restriction reached, false if no restrictions/not reached
*/
protected bool TraderBuyRestrictionReached(RagfairOffer offer)
{
var traderAssorts = traderHelper.GetTraderAssortsByTraderId(offer.User.Id).Items;
// Find item being purchased from traders assorts
var assortData = traderAssorts.FirstOrDefault(item => item.Id == offer.Items[0].Id);
// No trader assort data
if (assortData == null) {
logger.Warning($"Unable to find trader: {offer.User.Nickname} assort for item: {itemHelper.GetItemName(offer.Items[0].Template)} {offer.Items[0].Template}, cannot check if buy restriction reached");
return false;
}
if (assortData.Upd == null) {
return false;
}
// No restriction values
// Can't use !assortData.upd.BuyRestrictionX as value could be 0
var assortUpd = assortData.Upd;
if (assortUpd.BuyRestrictionMax == null || assortUpd.BuyRestrictionCurrent == null) {
return false;
}
// Current equals max, limit reached
if (assortUpd.BuyRestrictionCurrent >= assortUpd.BuyRestrictionMax) {
return true;
}
return false;
}
/**
* Get an array of flea offers that are inaccessible to player due to their inadequate loyalty level
* @param offers Offers to check
* @param pmcProfile Players profile with trader loyalty levels
* @returns Array of offer ids player cannot see
*/
protected List<string> GetLoyaltyLockedOffers(List<RagfairOffer> offers, PmcData pmcProfile)
{
var loyaltyLockedOffers = new List<string>();
foreach (var offer in offers.Where((offer) => OfferIsFromTrader(offer))) {
var traderDetails = pmcProfile.TradersInfo[offer.User.Id];
if (traderDetails.LoyaltyLevel < offer.LoyaltyLevel) {
loyaltyLockedOffers.Add(offer.Id);
}
}
return loyaltyLockedOffers;
}
/**
* Process all player-listed flea offers for a desired profile
* @param sessionID Session id to process offers for
* @returns true = complete
*/
public bool ProcessOffersOnProfile(string sessionID)
{
var timestamp = timeUtil.GetTimeStamp();
var profileOffers = GetProfileOffers(sessionID);
// No offers, don't do anything
if (!profileOffers?.length) {
return true;
}
foreach (var offer in profileOffers.values()) {
if (offer.sellResult?.length > 0 && timestamp >= offer.sellResult[0].sellTime) {
// Checks first item, first is spliced out of array after being processed
// Item sold
var totalItemsCount = 1;
var boughtAmount = 1;
if (!offer.sellInOnePiece) {
// offer.items.reduce((sum, item) => sum + item.upd?.StackObjectsCount ?? 0, 0);
totalItemsCount = getTotalStackCountSize([offer.items]);
boughtAmount = offer.sellResult[0].amount;
}
var ratingToAdd = (offer.summaryCost / totalItemsCount) * boughtAmount;
increaseProfileRagfairRating(saveServer.getProfile(sessionID), ratingToAdd);
completeOffer(sessionID, offer, boughtAmount);
offer.sellResult.splice(0, 1); // Remove the sell result object now its been processed
}
}
return true;
}
/**
* Count up all rootitem StackObjectsCount properties of an array of items
* @param itemsInInventoryToList items to sum up
* @returns Total stack count
*/
public int GetTotalStackCountSize(List<List<Item>> itemsInInventoryToList)
{
var total = 0D;
foreach (var itemAndChildren in itemsInInventoryToList) {
// Only count the root items stack count in total
var rootItem = itemAndChildren[0];
total += rootItem.Upd?.StackObjectsCount ?? 1;
}
return (int) total;
}
/**
* Add amount to players ragfair rating
* @param sessionId Profile to update
* @param amountToIncrementBy Raw amount to add to players ragfair rating (excluding the reputation gain multiplier)
*/
public void IncreaseProfileRagfairRating(SptProfile profile, double? amountToIncrementBy)
{
var ragfairGlobalsConfig = databaseService.GetGlobals().Configuration.RagFair;
profile.CharacterData.PmcData.RagfairInfo.IsRatingGrowing = true;
if (amountToIncrementBy == null) {
logger.Warning($"Unable to increment ragfair rating, value was not a number: {amountToIncrementBy}");
return;
}
profile.CharacterData.PmcData.RagfairInfo.Rating +=
(ragfairGlobalsConfig.RatingIncreaseCount / ragfairGlobalsConfig.RatingSumForIncrease) *
amountToIncrementBy;
}
/**
* Return all offers a player has listed on a desired profile
* @param sessionID Session id
* @returns Array of ragfair offers
*/
protected List<RagfairOffer> GetProfileOffers(string sessionID)
{
var profile = profileHelper.GetPmcProfile(sessionID);
if (profile.RagfairInfo == null || profile.RagfairInfo.Offers == null) {
return [];
}
return profile.RagfairInfo.Offers;
}
/**
* Delete an offer from a desired profile and from ragfair offers
* @param sessionID Session id of profile to delete offer from
* @param offerId Id of offer to delete
*/
protected void DeleteOfferById(string sessionID, string offerId)
{
var profileRagfairInfo = saveServer.GetProfile(sessionID).CharacterData.PmcData.RagfairInfo;
var index = profileRagfairInfo.Offers.FindIndex((o) => o.Id == offerId);
profileRagfairInfo.Offers.Splice(index, 1);
// Also delete from ragfair
ragfairOfferService.RemoveOfferById(offerId);
}
/**
* Complete the selling of players' offer
* @param offerOwnerSessionId Session id
* @param offer Sold offer details
* @param boughtAmount Amount item was purchased for
* @returns IItemEventRouterResponse
*/
public ItemEventRouterResponse CompleteOffer(
string offerOwnerSessionId,
RagfairOffer offer,
int boughtAmount
)
{
var itemTpl = offer.Items[0].Template;
var paymentItemsToSendToPlayer = new List<Item>();
var offerStackCount = (int) offer.Items[0].Upd.StackObjectsCount;
var sellerProfile = profileHelper.GetPmcProfile(offerOwnerSessionId);
// Pack or ALL items of a multi-offer were bought - remove entire ofer
if (offer.SellInOnePiece ?? false || boughtAmount == offerStackCount) {
DeleteOfferById(offerOwnerSessionId, offer.Id);
} else {
var offerRootItem = offer.Items[0];
// Reduce offer root items stack count
offerRootItem.Upd.StackObjectsCount -= boughtAmount;
}
// Assemble payment to send to seller now offer was purchased
foreach (var requirement in offer.Requirements) {
// Create an item template item
var requestedItem = new Item {
Id = hashUtil.Generate(),
Template = requirement.Template,
Upd = new Upd() { StackObjectsCount = requirement.Count * boughtAmount }
};
var stacks = itemHelper.SplitStack(requestedItem);
foreach (var item in stacks) {
var outItems = new List<Item>() {item};
// TODO - is this code used?, may have been when adding barters to flea was still possible for player
if (requirement.OnlyFunctional ?? false) {
var presetItems = ragfairServerHelper.GetPresetItemsByTpl(item);
if (presetItems.Count != 0) {
outItems.Add(presetItems[0]);
}
}
paymentItemsToSendToPlayer = [..paymentItemsToSendToPlayer, ..outItems];
}
}
var ragfairDetails = {
offerId: offer._id,
count: offer.sellInOnePiece ? offerStackCount : boughtAmount, // pack-offers NEED to to be the full item count otherwise it only removes 1 from the pack, leaving phantom offer on client ui
handbookId: itemTpl,
};
mailSendService.SendDirectNpcMessageToPlayer(
offerOwnerSessionId,
traderHelper.GetTraderById(Traders.RAGMAN),
MessageType.FLEAMARKET_MESSAGE,
GetLocalisedOfferSoldMessage(itemTpl, boughtAmount),
paymentItemsToSendToPlayer,
timeUtil.GetHoursAsSeconds(questHelper.GetMailItemRedeemTimeHoursForProfile(sellerProfile)),
null,
ragfairDetails,
);
// Adjust sellers sell sum values
sellerProfile.RagfairInfo.sellSum ||= 0;
sellerProfile.RagfairInfo.sellSum += offer.summaryCost;
return eventOutputHolder.getOutput(offerOwnerSessionId);
}
/**
* Get a localised message for when players offer has sold on flea
* @param itemTpl Item sold
* @param boughtAmount How many were purchased
* @returns Localised message text
*/
protected getLocalisedOfferSoldMessage(itemTpl: string, boughtAmount: number): string {
// Generate a message to inform that item was sold
var globalLocales = localeService.getLocaleDb();
var soldMessageLocaleGuid = globalLocales[RagfairOfferHelper.goodSoldTemplate];
if (!soldMessageLocaleGuid) {
logger.error(
localisationService.getText(
"ragfair-unable_to_find_locale_by_key",
RagfairOfferHelper.goodSoldTemplate,
),
);
}
// Used to replace tokens in sold message sent to player
var tplVars: ISystemData = {
soldItem: globalLocales["${itemTpl} Name"] || itemTpl,
buyerNickname: botHelper.getPmcNicknameOfMaxLength(botConfig.botNameLengthLimit),
itemCount: boughtAmount,
};
var offerSoldMessageText = soldMessageLocaleGuid.replace(/{\w+}/g, (matched) => {
return tplVars[matched.replace(/{|}/g, "")];
});
return offerSoldMessageText.replace(/"/g, "");
}
/**
* Check an offer passes the various search criteria the player requested
* @param searchRequest Client search request
* @param offer Offer to check
* @param pmcData Player profile
* @returns True if offer passes criteria
*/
protected passesSearchFilterCriteria(
searchRequest: ISearchRequestData,
offer: IRagfairOffer,
pmcData: IPmcData,
): boolean {
var isDefaultUserOffer = offer.user.memberType === MemberCategory.DEFAULT;
var offerRootItem = offer.items[0];
var moneyTypeTpl = offer.requirements[0]._tpl;
var isTraderOffer = offerIsFromTrader(offer);
if (pmcData.Info.Level < databaseService.getGlobals().config.RagFair.minUserLevel && isDefaultUserOffer) {
// Skip item if player is < global unlock level (default is 15) and item is from a dynamically generated source
return false;
}
if (searchRequest.offerOwnerType === OfferOwnerType.TRADEROWNERTYPE && !isTraderOffer) {
// don't include player offers
return false;
}
if (searchRequest.offerOwnerType === OfferOwnerType.PLAYEROWNERTYPE && isTraderOffer) {
// don't include trader offers
return false;
}
if (
searchRequest.oneHourExpiration &&
offer.endTime - timeUtil.getTimestamp() > TimeUtil.ONE_HOUR_AS_SECONDS
) {
// offer expires within an hour
return false;
}
if (searchRequest.quantityFrom > 0 && searchRequest.quantityFrom >= offerRootItem.upd.StackObjectsCount) {
// too little items to offer
return false;
}
if (searchRequest.quantityTo > 0 && searchRequest.quantityTo <= offerRootItem.upd.StackObjectsCount) {
// too many items to offer
return false;
}
if (searchRequest.onlyFunctional && !isItemFunctional(offerRootItem, offer)) {
// don't include non-functional items
return false;
}
if (offer.items.length === 1) {
// Single item
if (
isConditionItem(offerRootItem) &&
!itemQualityInRange(offerRootItem, searchRequest.conditionFrom, searchRequest.conditionTo)
) {
return false;
}
} else {
var itemQualityPercent = itemHelper.getItemQualityModifierForItems(offer.items) * 100;
if (itemQualityPercent < searchRequest.conditionFrom) {
return false;
}
if (itemQualityPercent > searchRequest.conditionTo) {
return false;
}
}
if (searchRequest.currency > 0 && paymentHelper.isMoneyTpl(moneyTypeTpl)) {
var currencies = ["all", "RUB", "USD", "EUR"];
if (ragfairHelper.getCurrencyTag(moneyTypeTpl) !== currencies[searchRequest.currency]) {
// don't include item paid in wrong currency
return false;
}
}
if (searchRequest.priceFrom > 0 && searchRequest.priceFrom >= offer.requirementsCost) {
// price is too low
return false;
}
if (searchRequest.priceTo > 0 && searchRequest.priceTo <= offer.requirementsCost) {
// price is too high
return false;
}
// Passes above checks, search criteria filters have not filtered offer out
return true;
}
/**
* Check that the passed in offer item is functional
* @param offerRootItem The root item of the offer
* @param offer Flea offer to check
* @returns True if the given item is functional
*/
public isItemFunctional(offerRootItem: IItem, offer: IRagfairOffer): boolean {
// Non-preset weapons/armor are always functional
if (!presetHelper.hasPreset(offerRootItem._tpl)) {
return true;
}
// For armor items that can hold mods, make sure the item count is atleast the amount of required plates
if (itemHelper.armorItemCanHoldMods(offerRootItem._tpl)) {
var offerRootTemplate = itemHelper.getItem(offerRootItem._tpl)[1];
var requiredPlateCount = offerRootTemplate._props.Slots?.filter((item) => item._required)?.length;
return offer.items.length > requiredPlateCount;
}
// For other presets, make sure the offer has more than 1 item
return offer.items.length > 1;
}
/**
* Should a ragfair offer be visible to the player
* @param searchRequest Search request
* @param itemsToAdd ?
* @param traderAssorts Trader assort items - used for filtering out locked trader items
* @param offer The flea offer
* @param pmcProfile Player profile
* @returns True = should be shown to player
*/
public isDisplayableOffer(
searchRequest: ISearchRequestData,
itemsToAdd: string[],
traderAssorts: Record<string, ITraderAssort>,
offer: IRagfairOffer,
pmcProfile: IPmcData,
playerIsFleaBanned?: boolean,
): boolean {
var offerRootItem = offer.items[0];
/** Currency offer is sold for */
var moneyTypeTpl = offer.requirements[0]._tpl;
var isTraderOffer = offer.user.id in databaseService.getTraders();
if (!isTraderOffer && playerIsFleaBanned) {
return false;
}
// Offer root items tpl not in searched for array
if (!itemsToAdd?.includes(offerRootItem._tpl)) {
// skip items we shouldn't include
return false;
}
// Performing a required search and offer doesn't have requirement for item
if (
searchRequest.neededSearchId &&
!offer.requirements.some((requirement) => requirement._tpl === searchRequest.neededSearchId)
) {
return false;
}
// Weapon/equipment search + offer is preset
if (
Object.keys(searchRequest.buildItems).length === 0 && // Prevent equipment loadout searches filtering out presets
searchRequest.buildCount &&
presetHelper.hasPreset(offerRootItem._tpl)
) {
return false;
}
// commented out as required search "which is for checking offers that are barters"
// has info.removeBartering as true, this if statement removed barter items.
if (searchRequest.removeBartering && !paymentHelper.isMoneyTpl(moneyTypeTpl)) {
// Don't include barter offers
return false;
}
if (JSType.Number.isNaN(offer.requirementsCost)) {
// Don't include offers with undefined or NaN in it
return false;
}
// Handle trader items to remove items that are not available to the user right now
// e.g. required search for "lamp" shows 4 items, 3 of which are not available to a new player
// filter those out
if (isTraderOffer) {
if (!(offer.user.id in traderAssorts)) {
// trader not visible on flea market
return false;
}
if (
!traderAssorts[offer.user.id].items.some((item) => {
return item._id === offer.root;
})
) {
// skip (quest) locked items
return false;
}
}
return true;
}
public isDisplayableOfferThatNeedsItem(searchRequest: ISearchRequestData, offer: IRagfairOffer): boolean {
if (offer.requirements.some((requirement) => requirement._tpl === searchRequest.neededSearchId)) {
return true;
}
return false;
}
/**
* Does the passed in item have a condition property
* @param item Item to check
* @returns True if has condition
*/
protected isConditionItem(item: IItem): boolean {
// thanks typescript, undefined assertion is not returnable since it
// tries to return a multitype object
return !!(
item.upd.MedKit ||
item.upd.Repairable ||
item.upd.Resource ||
item.upd.FoodDrink ||
item.upd.Key ||
item.upd.RepairKit
);
}
/**
* Is items quality value within desired range
* @param item Item to check quality of
* @param min Desired minimum quality
* @param max Desired maximum quality
* @returns True if in range
*/
protected itemQualityInRange(item: IItem, min: number, max: number): boolean {
var itemQualityPercentage = 100 * itemHelper.getItemQualityModifier(item);
if (min > 0 && min > itemQualityPercentage) {
// Item condition too low
return false;
}
if (max < 100 && max <= itemQualityPercentage) {
// Item condition too high
return false;
}
return true;
}
/**
* Does this offer come from a trader
* @param offer Offer to check
* @returns True = from trader
*/
public offerIsFromTrader(offer: IRagfairOffer) {
return offer.user.memberType === MemberCategory.TRADER;
}
}