2023-03-03 16:23:46 +01:00
import { inject , injectable } from "tsyringe" ;
2023-10-19 19:21:17 +02:00
import { ItemHelper } from "@spt-aki/helpers/ItemHelper" ;
import { PaymentHelper } from "@spt-aki/helpers/PaymentHelper" ;
import { PresetHelper } from "@spt-aki/helpers/PresetHelper" ;
import { ProfileHelper } from "@spt-aki/helpers/ProfileHelper" ;
import { RagfairHelper } from "@spt-aki/helpers/RagfairHelper" ;
import { RagfairServerHelper } from "@spt-aki/helpers/RagfairServerHelper" ;
import { RagfairSortHelper } from "@spt-aki/helpers/RagfairSortHelper" ;
import { TraderHelper } from "@spt-aki/helpers/TraderHelper" ;
import { IPmcData } from "@spt-aki/models/eft/common/IPmcData" ;
import { Item } from "@spt-aki/models/eft/common/tables/IItem" ;
import { ITraderAssort } from "@spt-aki/models/eft/common/tables/ITrader" ;
import { IItemEventRouterResponse } from "@spt-aki/models/eft/itemEvent/IItemEventRouterResponse" ;
import { IAkiProfile , ISystemData } from "@spt-aki/models/eft/profile/IAkiProfile" ;
import { IRagfairOffer } from "@spt-aki/models/eft/ragfair/IRagfairOffer" ;
import { ISearchRequestData , OfferOwnerType } from "@spt-aki/models/eft/ragfair/ISearchRequestData" ;
import { ConfigTypes } from "@spt-aki/models/enums/ConfigTypes" ;
import { MemberCategory } from "@spt-aki/models/enums/MemberCategory" ;
import { MessageType } from "@spt-aki/models/enums/MessageType" ;
import { RagfairSort } from "@spt-aki/models/enums/RagfairSort" ;
import { Traders } from "@spt-aki/models/enums/Traders" ;
import { IQuestConfig } from "@spt-aki/models/spt/config/IQuestConfig" ;
import { IRagfairConfig } from "@spt-aki/models/spt/config/IRagfairConfig" ;
import { ILogger } from "@spt-aki/models/spt/utils/ILogger" ;
import { EventOutputHolder } from "@spt-aki/routers/EventOutputHolder" ;
import { ConfigServer } from "@spt-aki/servers/ConfigServer" ;
import { DatabaseServer } from "@spt-aki/servers/DatabaseServer" ;
import { SaveServer } from "@spt-aki/servers/SaveServer" ;
import { LocaleService } from "@spt-aki/services/LocaleService" ;
import { LocalisationService } from "@spt-aki/services/LocalisationService" ;
import { MailSendService } from "@spt-aki/services/MailSendService" ;
import { RagfairOfferService } from "@spt-aki/services/RagfairOfferService" ;
import { HashUtil } from "@spt-aki/utils/HashUtil" ;
import { TimeUtil } from "@spt-aki/utils/TimeUtil" ;
2023-03-03 16:23:46 +01:00
@injectable ( )
export class RagfairOfferHelper
{
2023-05-29 17:05:19 +02:00
protected static goodSoldTemplate = "5bdabfb886f7743e152e867e 0" ; // Your {soldItem} {itemCount} items were bought by {buyerNickname}.
2023-03-03 16:23:46 +01:00
protected ragfairConfig : IRagfairConfig ;
protected questConfig : IQuestConfig ;
constructor (
@inject ( "WinstonLogger" ) protected logger : ILogger ,
@inject ( "TimeUtil" ) protected timeUtil : TimeUtil ,
@inject ( "HashUtil" ) protected hashUtil : HashUtil ,
@inject ( "EventOutputHolder" ) protected eventOutputHolder : EventOutputHolder ,
@inject ( "DatabaseServer" ) protected databaseServer : DatabaseServer ,
@inject ( "TraderHelper" ) protected traderHelper : TraderHelper ,
@inject ( "SaveServer" ) protected saveServer : SaveServer ,
@inject ( "ItemHelper" ) protected itemHelper : ItemHelper ,
@inject ( "PaymentHelper" ) protected paymentHelper : PaymentHelper ,
@inject ( "PresetHelper" ) protected presetHelper : PresetHelper ,
@inject ( "ProfileHelper" ) protected profileHelper : ProfileHelper ,
@inject ( "RagfairServerHelper" ) protected ragfairServerHelper : RagfairServerHelper ,
@inject ( "RagfairSortHelper" ) protected ragfairSortHelper : RagfairSortHelper ,
@inject ( "RagfairHelper" ) protected ragfairHelper : RagfairHelper ,
@inject ( "RagfairOfferService" ) protected ragfairOfferService : RagfairOfferService ,
@inject ( "LocaleService" ) protected localeService : LocaleService ,
2023-07-19 12:00:34 +02:00
@inject ( "LocalisationService" ) protected localisationService : LocalisationService ,
2023-10-10 13:03:20 +02:00
@inject ( "MailSendService" ) protected mailSendService : MailSendService ,
2023-11-16 22:42:06 +01:00
@inject ( "ConfigServer" ) protected configServer : ConfigServer ,
2023-03-03 16:23:46 +01:00
)
{
this . ragfairConfig = this . configServer . getConfig ( ConfigTypes . RAGFAIR ) ;
this . questConfig = this . configServer . getConfig ( ConfigTypes . QUEST ) ;
}
2023-05-29 17:32:06 +02:00
/ * *
* Passthrough to ragfairOfferService . getOffers ( ) , get flea offers a player should see
2023-10-10 13:03:20 +02:00
* @param searchRequest Data from client
* @param itemsToAdd ragfairHelper . filterCategories ( )
2023-05-29 17:32:06 +02:00
* @param traderAssorts Trader assorts
* @param pmcProfile Player profile
* @returns Offers the player should see
* /
2023-11-16 22:42:06 +01:00
public getValidOffers (
searchRequest : ISearchRequestData ,
itemsToAdd : string [ ] ,
traderAssorts : Record < string , ITraderAssort > ,
pmcProfile : IPmcData ,
) : IRagfairOffer [ ]
2023-03-03 16:23:46 +01:00
{
2023-11-16 22:42:06 +01:00
return this . ragfairOfferService . getOffers ( ) . filter ( ( x ) = >
this . isDisplayableOffer ( searchRequest , itemsToAdd , traderAssorts , x , pmcProfile )
) ;
2023-03-03 16:23:46 +01:00
}
/ * *
* 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 pmcProfile Player profile
2023-05-29 17:32:06 +02:00
* @returns IRagfairOffer array
2023-03-03 16:23:46 +01:00
* /
2023-11-16 22:42:06 +01:00
public getOffersForBuild (
searchRequest : ISearchRequestData ,
itemsToAdd : string [ ] ,
traderAssorts : Record < string , ITraderAssort > ,
pmcProfile : IPmcData ,
) : IRagfairOffer [ ]
2023-03-03 16:23:46 +01:00
{
const offersMap = new Map < string , IRagfairOffer [ ] > ( ) ;
const offers : IRagfairOffer [ ] = [ ] ;
for ( const offer of this . ragfairOfferService . getOffers ( ) )
{
if ( this . isDisplayableOffer ( searchRequest , itemsToAdd , traderAssorts , offer , pmcProfile ) )
{
const isTraderOffer = offer . user . memberType === MemberCategory . TRADER ;
if ( isTraderOffer && this . traderBuyRestrictionReached ( offer ) )
{
continue ;
}
if ( isTraderOffer && this . traderOutOfStock ( offer ) )
{
continue ;
}
if ( isTraderOffer && this . traderOfferItemQuestLocked ( offer , traderAssorts ) )
{
continue ;
}
2023-10-10 13:03:20 +02:00
if ( isTraderOffer && this . traderOfferLockedBehindLoyaltyLevel ( offer , pmcProfile ) )
{
continue ;
}
2023-03-03 16:23:46 +01:00
const key = offer . items [ 0 ] . _tpl ;
if ( ! offersMap . has ( key ) )
{
offersMap . set ( key , [ ] ) ;
}
offersMap . get ( key ) . push ( offer ) ;
}
}
// get best offer for each item to show on screen
for ( let possibleOffers of 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 . length > 1 )
{
const lockedOffers = this . getLoyaltyLockedOffers ( possibleOffers , pmcProfile ) ;
2023-11-16 22:42:06 +01:00
2023-03-03 16:23:46 +01:00
// Exclude locked offers + above loyalty locked offers if at least 1 was found
2023-11-16 22:42:06 +01:00
const availableOffers = possibleOffers . filter ( ( x ) = > ! ( x . locked || lockedOffers . includes ( x . _id ) ) ) ;
2023-03-03 16:23:46 +01:00
if ( availableOffers . length > 0 )
{
possibleOffers = availableOffers ;
2023-11-16 22:42:06 +01:00
}
2023-03-03 16:23:46 +01:00
}
const offer = this . ragfairSortHelper . sortOffers ( possibleOffers , RagfairSort . PRICE , 0 ) [ 0 ] ;
offers . push ( offer ) ;
}
return offers ;
}
2023-10-10 13:03:20 +02:00
/ * *
* 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 traderOfferLockedBehindLoyaltyLevel ( offer : IRagfairOffer , pmcProfile : IPmcData ) : boolean
{
const userTraderSettings = pmcProfile . TradersInfo [ offer . user . id ] ;
return userTraderSettings . loyaltyLevel < offer . loyaltyLevel ;
}
2023-03-03 16:23:46 +01:00
/ * *
* 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 traderOfferItemQuestLocked ( offer : IRagfairOffer , traderAssorts : Record < string , ITraderAssort > ) : boolean
{
2023-11-16 22:42:06 +01:00
return offer . items ? . some ( ( i ) = >
traderAssorts [ offer . user . id ] . barter_scheme [ i . _id ] ? . some ( ( bs1 ) = > bs1 ? . some ( ( bs2 ) = > bs2 . sptQuestLocked ) )
) ;
2023-03-03 16:23:46 +01:00
}
/ * *
* Has a traders offer ran out of stock to sell to player
* @param offer Offer to check stock of
* @returns true if out of stock
* /
2023-11-16 22:42:06 +01:00
protected traderOutOfStock ( offer : IRagfairOffer ) : boolean
2023-03-03 16:23:46 +01:00
{
if ( offer ? . items ? . length === 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 traderBuyRestrictionReached ( offer : IRagfairOffer ) : boolean
{
2023-10-10 13:03:20 +02:00
const traderAssorts = this . traderHelper . getTraderAssortsByTraderId ( offer . user . id ) . items ;
2023-11-16 22:42:06 +01:00
const assortData = traderAssorts . find ( ( x ) = > x . _id === offer . items [ 0 ] . _id ) ;
2023-03-03 16:23:46 +01:00
// No trader assort data
if ( ! assortData )
{
2023-11-16 22:42:06 +01:00
this . logger . warning (
` Unable to find trader: ${ offer . user . nickname } assort for item: ${
this . itemHelper . getItemName ( offer . items [ 0 ] . _tpl )
} $ { offer . items [ 0 ] . _tpl } , cannot check if buy restriction reached ` ,
) ;
2023-03-03 16:23:46 +01:00
return false ;
}
// No restriction values
2023-11-16 22:42:06 +01:00
// Can't use !assortData.upd.BuyRestrictionX as value could be 0
2023-03-03 16:23:46 +01:00
if ( assortData . upd . BuyRestrictionMax === undefined || assortData . upd . BuyRestrictionCurrent === undefined )
{
return false ;
}
// Current equals max, limit reached
if ( assortData ? . upd . BuyRestrictionCurrent === assortData . upd . 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
2023-05-29 17:32:06 +02:00
* @returns array of offer ids player cannot see
2023-03-03 16:23:46 +01:00
* /
protected getLoyaltyLockedOffers ( offers : IRagfairOffer [ ] , pmcProfile : IPmcData ) : string [ ]
{
const loyaltyLockedOffers : string [ ] = [ ] ;
for ( const offer of offers )
{
if ( offer . user . memberType === MemberCategory . TRADER )
{
const traderDetails = pmcProfile . TradersInfo [ offer . user . id ] ;
if ( traderDetails . loyaltyLevel < offer . loyaltyLevel )
{
loyaltyLockedOffers . push ( offer . _id ) ;
}
}
}
return loyaltyLockedOffers ;
}
2023-05-29 17:32:06 +02:00
/ * *
* Process all player - listed flea offers for a desired profile
* @param sessionID Session id to process offers for
* @returns true = complete
* /
2023-03-03 16:23:46 +01:00
public processOffersOnProfile ( sessionID : string ) : boolean
{
const timestamp = this . timeUtil . getTimestamp ( ) ;
const profileOffers = this . getProfileOffers ( sessionID ) ;
2023-05-29 17:05:19 +02:00
// No offers, don't do anything
2023-03-03 16:23:46 +01:00
if ( ! profileOffers ? . length )
{
return true ;
}
for ( const offer of profileOffers . values ( ) )
{
if ( offer . sellResult && offer . sellResult . length > 0 && timestamp >= offer . sellResult [ 0 ] . sellTime )
{
// Item sold
let totalItemsCount = 1 ;
let boughtAmount = 1 ;
if ( ! offer . sellInOnePiece )
{
totalItemsCount = offer . items . reduce ( ( sum : number , item ) = > sum += item . upd . StackObjectsCount , 0 ) ;
boughtAmount = offer . sellResult [ 0 ] . amount ;
}
2023-11-16 22:42:06 +01:00
this . increaseProfileRagfairRating (
this . saveServer . getProfile ( sessionID ) ,
offer . summaryCost / totalItemsCount * boughtAmount ,
) ;
2023-03-03 16:23:46 +01:00
this . completeOffer ( sessionID , offer , boughtAmount ) ;
offer . sellResult . splice ( 0 , 1 ) ;
}
}
return true ;
}
2023-05-29 17:12:27 +02:00
/ * *
* 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 increaseProfileRagfairRating ( profile : IAkiProfile , amountToIncrementBy : number ) : void
{
profile . characters . pmc . RagfairInfo . isRatingGrowing = true ;
2023-11-16 22:42:06 +01:00
if ( Number . isNaN ( amountToIncrementBy ) )
2023-06-13 20:33:42 +02:00
{
this . logger . warning ( ` Unable to increment ragfair rating, value was not a number: ${ amountToIncrementBy } ` ) ;
return ;
}
profile . characters . pmc . RagfairInfo . rating += this . ragfairConfig . sell . reputation . gain * amountToIncrementBy ;
2023-05-29 17:12:27 +02:00
}
2023-05-29 17:32:06 +02:00
/ * *
* Return all offers a player has listed on a desired profile
* @param sessionID Session id
* @returns Array of ragfair offers
* /
2023-03-03 16:23:46 +01:00
protected getProfileOffers ( sessionID : string ) : IRagfairOffer [ ]
{
const profile = this . profileHelper . getPmcProfile ( sessionID ) ;
if ( profile . RagfairInfo === undefined || profile . RagfairInfo . offers === undefined )
{
return [ ] ;
}
return profile . RagfairInfo . offers ;
}
2023-05-29 17:32:06 +02:00
/ * *
2023-10-10 13:03:20 +02:00
* Delete an offer from a desired profile and from ragfair offers
2023-05-29 17:32:06 +02:00
* @param sessionID Session id of profile to delete offer from
2023-10-10 13:03:20 +02:00
* @param offerId Id of offer to delete
2023-05-29 17:32:06 +02:00
* /
2023-10-10 13:03:20 +02:00
protected deleteOfferById ( sessionID : string , offerId : string ) : void
2023-03-03 16:23:46 +01:00
{
const profileRagfairInfo = this . saveServer . getProfile ( sessionID ) . characters . pmc . RagfairInfo ;
2023-11-16 22:42:06 +01:00
const index = profileRagfairInfo . offers . findIndex ( ( o ) = > o . _id === offerId ) ;
2023-03-03 16:23:46 +01:00
profileRagfairInfo . offers . splice ( index , 1 ) ;
2023-12-14 16:47:01 +01:00
2023-10-10 13:03:20 +02:00
// Also delete from ragfair
2023-03-03 16:23:46 +01:00
this . ragfairOfferService . removeOfferById ( offerId ) ;
}
2023-05-29 17:20:41 +02:00
/ * *
* Complete the selling of players ' offer
* @param sessionID Session id
* @param offer Sold offer details
* @param boughtAmount Amount item was purchased for
2023-10-10 13:03:20 +02:00
* @returns IItemEventRouterResponse
2023-05-29 17:20:41 +02:00
* /
2023-03-03 16:23:46 +01:00
protected completeOffer ( sessionID : string , offer : IRagfairOffer , boughtAmount : number ) : IItemEventRouterResponse
{
const itemTpl = offer . items [ 0 ] . _tpl ;
let itemsToSend = [ ] ;
2023-10-10 13:03:20 +02:00
const offerStackCount = offer . items [ 0 ] . upd . StackObjectsCount ;
2023-03-03 16:23:46 +01:00
2023-10-10 13:03:20 +02:00
if ( offer . sellInOnePiece || boughtAmount === offerStackCount )
2023-03-03 16:23:46 +01:00
{
2023-10-10 13:03:20 +02:00
this . deleteOfferById ( sessionID , offer . _id ) ;
2023-03-03 16:23:46 +01:00
}
else
{
offer . items [ 0 ] . upd . StackObjectsCount -= boughtAmount ;
2023-11-16 22:42:06 +01:00
const rootItems = offer . items . filter ( ( i ) = > i . parentId === "hideout" ) ;
2023-03-03 16:23:46 +01:00
rootItems . splice ( 0 , 1 ) ;
let removeCount = boughtAmount ;
let idsToRemove : string [ ] = [ ] ;
while ( removeCount > 0 && rootItems . length > 0 )
{
const lastItem = rootItems [ rootItems . length - 1 ] ;
if ( lastItem . upd . StackObjectsCount > removeCount )
{
lastItem . upd . StackObjectsCount -= removeCount ;
removeCount = 0 ;
}
else
{
removeCount -= lastItem . upd . StackObjectsCount ;
idsToRemove . push ( lastItem . _id ) ;
rootItems . splice ( rootItems . length - 1 , 1 ) ;
}
}
let foundNewItems = true ;
while ( foundNewItems )
{
foundNewItems = false ;
2023-11-16 22:42:06 +01:00
2023-03-03 16:23:46 +01:00
// eslint-disable-next-line @typescript-eslint/no-unused-vars
for ( const id of idsToRemove )
{
2023-11-16 22:42:06 +01:00
const newIds = offer . items . filter ( ( i ) = >
! idsToRemove . includes ( i . _id ) && idsToRemove . includes ( i . parentId )
) . map ( ( i ) = > i . _id ) ;
2023-03-03 16:23:46 +01:00
if ( newIds . length > 0 )
{
foundNewItems = true ;
idsToRemove = [ . . . idsToRemove , . . . newIds ] ;
}
}
}
if ( idsToRemove . length > 0 )
{
2023-11-16 22:42:06 +01:00
offer . items = offer . items . filter ( ( i ) = > ! idsToRemove . includes ( i . _id ) ) ;
2023-03-03 16:23:46 +01:00
}
}
2023-05-29 17:20:41 +02:00
// Assemble the payment item(s)
2023-03-03 16:23:46 +01:00
for ( const requirement of offer . requirements )
{
// Create an item template item
const requestedItem : Item = {
_id : this.hashUtil.generate ( ) ,
_tpl : requirement._tpl ,
2023-11-16 22:42:06 +01:00
upd : { StackObjectsCount : requirement.count * boughtAmount } ,
2023-03-03 16:23:46 +01:00
} ;
const stacks = this . itemHelper . splitStack ( requestedItem ) ;
for ( const item of stacks )
{
const outItems = [ item ] ;
2023-12-14 16:47:01 +01:00
// TODO - is this code used?, may have been when adding barters to flea was still possible for player
2023-03-03 16:23:46 +01:00
if ( requirement . onlyFunctional )
{
const presetItems = this . ragfairServerHelper . getPresetItemsByTpl ( item ) ;
if ( presetItems . length )
{
outItems . push ( presetItems [ 0 ] ) ;
}
}
itemsToSend = [ . . . itemsToSend , . . . outItems ] ;
}
}
2023-10-10 13:03:20 +02:00
const ragfairDetails = {
offerId : offer._id ,
count : offer.sellInOnePiece ? offerStackCount : boughtAmount , // pack-offers NEED to the full item count otherwise it only removes 1 from the pack, leaving phantom offer on client ui
2023-11-16 22:42:06 +01:00
handbookId : itemTpl ,
2023-10-10 13:03:20 +02:00
} ;
this . mailSendService . sendDirectNpcMessageToPlayer (
sessionID ,
this . traderHelper . getTraderById ( Traders . RAGMAN ) ,
MessageType . FLEAMARKET_MESSAGE ,
this . getLocalisedOfferSoldMessage ( itemTpl , boughtAmount ) ,
itemsToSend ,
this . timeUtil . getHoursAsSeconds ( this . questConfig . redeemTime ) ,
null ,
2023-11-16 22:42:06 +01:00
ragfairDetails ,
) ;
2023-10-10 13:03:20 +02:00
return this . eventOutputHolder . getOutput ( sessionID ) ;
}
/ * *
* 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
{
2023-03-03 16:23:46 +01:00
// Generate a message to inform that item was sold
const globalLocales = this . localeService . getLocaleDb ( ) ;
2023-05-29 17:20:41 +02:00
const soldMessageLocaleGuid = globalLocales [ RagfairOfferHelper . goodSoldTemplate ] ;
if ( ! soldMessageLocaleGuid )
{
2023-11-16 22:42:06 +01:00
this . logger . error (
this . localisationService . getText (
"ragfair-unable_to_find_locale_by_key" ,
RagfairOfferHelper . goodSoldTemplate ,
) ,
) ;
2023-05-29 17:20:41 +02:00
}
// Used to replace tokens in sold message sent to player
2023-03-03 16:23:46 +01:00
const tplVars : ISystemData = {
soldItem : globalLocales [ ` ${ itemTpl } Name ` ] || itemTpl ,
buyerNickname : this.ragfairServerHelper.getNickname ( this . hashUtil . generate ( ) ) ,
2023-11-16 22:42:06 +01:00
itemCount : boughtAmount ,
2023-03-03 16:23:46 +01:00
} ;
2023-10-10 13:03:20 +02:00
const offerSoldMessageText = soldMessageLocaleGuid . replace ( /{\w+}/g , ( matched ) = >
2023-03-03 16:23:46 +01:00
{
return tplVars [ matched . replace ( /{|}/g , "" ) ] ;
} ) ;
2023-10-10 13:03:20 +02:00
return offerSoldMessageText . replace ( /"/g , "" ) ;
2023-03-03 16:23:46 +01:00
}
2023-05-29 17:32:06 +02:00
/ * *
* Should a ragfair offer be visible to the player
2023-10-10 13:03:20 +02:00
* @param searchRequest Search request
2023-05-29 17:32:06 +02:00
* @param itemsToAdd ?
* @param traderAssorts Trader assort items
* @param offer The flea offer
* @param pmcProfile Player profile
* @returns True = should be shown to player
* /
2023-11-16 22:42:06 +01:00
public isDisplayableOffer (
searchRequest : ISearchRequestData ,
itemsToAdd : string [ ] ,
traderAssorts : Record < string , ITraderAssort > ,
offer : IRagfairOffer ,
pmcProfile : IPmcData ,
) : boolean
2023-03-03 16:23:46 +01:00
{
const item = offer . items [ 0 ] ;
const money = offer . requirements [ 0 ] . _tpl ;
const isTraderOffer = offer . user . memberType === MemberCategory . TRADER ;
const isDefaultUserOffer = offer . user . memberType === MemberCategory . DEFAULT ;
2023-11-16 22:42:06 +01:00
if (
pmcProfile . Info . Level < this . databaseServer . getTables ( ) . globals . config . RagFair . minUserLevel
&& isDefaultUserOffer
)
2023-03-03 16:23:46 +01:00
{
// Skip item if player is < global unlock level (default is 15) and item is from a dynamically generated source
return false ;
}
if ( ! ! itemsToAdd && ! itemsToAdd . includes ( item . _tpl ) )
{
// skip items we shouldn't include
return false ;
}
2023-10-10 13:03:20 +02:00
// Performing a required search and offer doesn't have requirement for item
2023-11-16 22:42:06 +01:00
if ( searchRequest . neededSearchId && ! offer . requirements . some ( ( x ) = > x . _tpl === searchRequest . neededSearchId ) )
2023-10-10 13:03:20 +02:00
{
return false ;
}
if ( searchRequest . offerOwnerType === OfferOwnerType . TRADEROWNERTYPE && ! isTraderOffer )
2023-03-03 16:23:46 +01:00
{
// don't include player offers
return false ;
}
2023-10-10 13:03:20 +02:00
if ( searchRequest . offerOwnerType === OfferOwnerType . PLAYEROWNERTYPE && isTraderOffer )
2023-03-03 16:23:46 +01:00
{
// don't include trader offers
return false ;
}
2023-10-10 13:03:20 +02:00
if ( searchRequest . oneHourExpiration && offer . endTime - this . timeUtil . getTimestamp ( ) > TimeUtil . oneHourAsSeconds )
2023-03-03 16:23:46 +01:00
{
// offer doesnt expire within an hour
return false ;
}
2023-10-10 13:03:20 +02:00
if ( searchRequest . quantityFrom > 0 && searchRequest . quantityFrom >= item . upd . StackObjectsCount )
2023-03-03 16:23:46 +01:00
{
// too little items to offer
return false ;
}
2023-10-10 13:03:20 +02:00
if ( searchRequest . quantityTo > 0 && searchRequest . quantityTo <= item . upd . StackObjectsCount )
2023-03-03 16:23:46 +01:00
{
// too many items to offer
return false ;
}
2023-10-10 13:03:20 +02:00
if ( searchRequest . onlyFunctional && this . presetHelper . hasPreset ( item . _tpl ) && offer . items . length === 1 )
2023-03-03 16:23:46 +01:00
{
// don't include non-functional items
return false ;
}
2023-10-10 13:03:20 +02:00
if ( searchRequest . buildCount && this . presetHelper . hasPreset ( item . _tpl ) && offer . items . length > 1 )
2023-03-03 16:23:46 +01:00
{
// don't include preset items
return false ;
}
2023-12-18 12:18:13 +01:00
if ( this . isConditionItem ( item )
2023-11-16 22:42:06 +01:00
&& ! this . itemQualityInRange ( item , searchRequest . conditionFrom , searchRequest . conditionTo )
)
2023-03-03 16:23:46 +01:00
{
2023-10-10 13:03:20 +02:00
return false ;
2023-03-03 16:23:46 +01:00
}
// commented out as required search "which is for checking offers that are barters"
// has info.removeBartering as true, this if statement removed barter items.
2023-10-10 13:03:20 +02:00
if ( searchRequest . removeBartering && ! this . paymentHelper . isMoneyTpl ( money ) )
2023-03-03 16:23:46 +01:00
{
// don't include barter offers
return false ;
}
2023-10-10 13:03:20 +02:00
if ( searchRequest . currency > 0 && this . paymentHelper . isMoneyTpl ( money ) )
2023-03-03 16:23:46 +01:00
{
const currencies = [ "all" , "RUB" , "USD" , "EUR" ] ;
2023-10-10 13:03:20 +02:00
if ( this . ragfairHelper . getCurrencyTag ( money ) !== currencies [ searchRequest . currency ] )
2023-03-03 16:23:46 +01:00
{
// don't include item paid in wrong currency
return false ;
}
}
2023-10-10 13:03:20 +02:00
if ( searchRequest . priceFrom > 0 && searchRequest . priceFrom >= offer . requirementsCost )
2023-03-03 16:23:46 +01:00
{
// price is too low
return false ;
}
2023-10-10 13:03:20 +02:00
if ( searchRequest . priceTo > 0 && searchRequest . priceTo <= offer . requirementsCost )
2023-03-03 16:23:46 +01:00
{
// price is too high
return false ;
}
2023-11-16 22:42:06 +01:00
if ( Number . isNaN ( offer . requirementsCost ) )
2023-03-03 16:23:46 +01:00
{
// don't include offers with null or NaN in it
return false ;
}
// handle trader items to remove items that are not available to the user right now
// required search for "lamp" shows 4 items, 3 of which are not available to a new player
// filter those out
if ( offer . user . id in this . databaseServer . getTables ( ) . traders )
{
if ( ! ( offer . user . id in traderAssorts ) )
{
// trader not visible on flea market
return false ;
}
2023-11-16 22:42:06 +01:00
if (
! traderAssorts [ offer . user . id ] . items . find ( ( item ) = >
{
return item . _id === offer . root ;
} )
)
2023-03-03 16:23:46 +01:00
{
// skip (quest) locked items
return false ;
}
}
return true ;
}
2023-10-10 13:03:20 +02:00
2023-12-18 12:18:13 +01:00
/ * *
* Does the passed in item have a condition property
* @param item Item to check
* @returns True if has condition
* /
protected isConditionItem ( item : Item ) : 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 )
? true
: false ;
}
2023-10-10 13:03:20 +02:00
/ * *
* 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 : Item , min : number , max : number ) : boolean
{
const itemQualityPercentage = 100 * this . 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 ;
}
2023-11-16 22:42:06 +01:00
}