Refactor of RagfairAssortGenerator and associated code that touches it

This commit is contained in:
Dev 2024-01-23 14:17:01 +00:00
parent 86d34e3efe
commit 4ce26ea870
5 changed files with 70 additions and 61 deletions

View File

@ -16,7 +16,7 @@ import { JsonUtil } from "@spt-aki/utils/JsonUtil";
@injectable()
export class RagfairAssortGenerator
{
protected generatedAssortItems: Item[] = [];
protected generatedAssortItems: Item[][] = [];
protected ragfairConfig: IRagfairConfig;
protected ragfairItemInvalidBaseTypes: string[] = [
@ -43,10 +43,11 @@ export class RagfairAssortGenerator
}
/**
* Get an array of unique items that can be sold on the flea
* @returns array of unique items
* Get an array of arrays that can be sold on the flea
* Each sub array contains item + children (if any)
* @returns array of arrays
*/
public getAssortItems(): Item[]
public getAssortItems(): Item[][]
{
if (!this.assortsAreGenerated())
{
@ -66,16 +67,17 @@ export class RagfairAssortGenerator
}
/**
* Generate an array of items the flea can sell
* @returns array of unique items
* Generate an array of arrays (item + children) the flea can sell
* @returns array of arrays (item + children)
*/
protected generateRagfairAssortItems(): Item[]
protected generateRagfairAssortItems(): Item[][]
{
const results: Item[] = [];
const results: Item[][] = [];
/** Get cloned items from db */
const dbItemsClone = this.itemHelper.getItems().filter(item => item._type !== "Node");
/** Store processed preset tpls so we dont add them when procesing non-preset items */
const processedArmorItems: string[] = [];
const seasonalEventActive = this.seasonalEventService.seasonalEventEnabled();
const seasonalItemTplBlacklist = this.seasonalEventService.getInactiveSeasonalEventItems();
@ -83,13 +85,21 @@ export class RagfairAssortGenerator
const presets = this.getPresetsToAdd();
for (const preset of presets)
{
const presetItemTpl = preset._items[0]._tpl;
// Update Ids and clone
const presetAndMods: Item[] = this.itemHelper.replaceIDs(
null,
this.jsonUtil.clone(preset._items),
);
this.itemHelper.remapRootItemId(presetAndMods);
// Add presets base item tpl to the processed list so its skipped later on when processing items
processedArmorItems.push(presetItemTpl)
processedArmorItems.push(preset._items[0]._tpl);
// Preset id must be passed through to ensure flea shows preset
results.push(this.createRagfairAssortItem(presetItemTpl, preset._id));
presetAndMods[0].parentId = "hideout";
presetAndMods[0].slotId = "hideout";
presetAndMods[0].upd = { StackObjectsCount: 99999999, UnlimitedCount: true, sptPresetId: preset._id};
results.push(presetAndMods);
}
for (const item of dbItemsClone)
@ -114,7 +124,9 @@ export class RagfairAssortGenerator
continue;
}
results.push(this.createRagfairAssortItem(item._id, item._id)); // tplid and id must be the same so hideout recipe rewards work
const ragfairAssort = this.createRagfairAssortRootItem(item._id, item._id); // tplid and id must be the same so hideout recipe rewards work
results.push([ragfairAssort]);
}
return results;
@ -136,9 +148,9 @@ export class RagfairAssortGenerator
* Create a base assort item and return it with populated values + 999999 stack count + unlimited count = true
* @param tplId tplid to add to item
* @param id id to add to item
* @returns hydrated Item object
* @returns Hydrated Item object
*/
protected createRagfairAssortItem(tplId: string, id = this.hashUtil.generate()): Item
protected createRagfairAssortRootItem(tplId: string, id = this.hashUtil.generate()): Item
{
return {
_id: id,

View File

@ -312,21 +312,22 @@ export class RagfairOfferGenerator
* 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>
public async generateDynamicOffers(expiredOffers: Item[][] = null): Promise<void>
{
const replacingExpiredOffers = expiredOffers?.length > 0;
const config = this.ragfairConfig.dynamic;
// get assort items from param if they exist, otherwise grab freshly generated assorts
const assortItemsToProcess: Item[] = expiredOffers
const assortItemsToProcess: Item[][] = replacingExpiredOffers
? 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)
for (const assortItemWithChildren of assortItemsToProcess)
{
assorOffersForItemsProcesses.push(
this.createOffersForItems(assortItemIndex, assortItemsToProcess, expiredOffers, config),
this.createOffersFromAssort(assortItemWithChildren, replacingExpiredOffers, config),
);
}
@ -334,47 +335,34 @@ export class RagfairOfferGenerator
}
/**
* @param assortItemIndex Index of assort item
* @param assortItemsToProcess Item array containing index
* @param expiredOffers Currently expired offers on flea
* @param assortItemWithChildren Item with its children to process into offers
* @param isExpiredOffer is an expired offer
* @param config Ragfair dynamic config
*/
protected async createOffersForItems(
assortItemIndex: string,
assortItemsToProcess: Item[],
expiredOffers: Item[],
protected async createOffersFromAssort(
assortItemWithChildren: Item[],
isExpiredOffer: boolean,
config: Dynamic,
): Promise<void>
{
const assortItem = assortItemsToProcess[assortItemIndex];
const itemDetails = this.itemHelper.getItem(assortItem._tpl);
const isPreset = this.presetHelper.isPreset(assortItem._id);
const itemDetails = this.itemHelper.getItem(assortItemWithChildren[0]._tpl);
const isPreset = this.presetHelper.isPreset(assortItemWithChildren[0].upd.sptPresetId);
// Only perform checks on newly generated items, skip expired items being refreshed
if (!(expiredOffers || this.ragfairServerHelper.isItemValidRagfairItem(itemDetails)))
if (!(isExpiredOffer || this.ragfairServerHelper.isItemValidRagfairItem(itemDetails)))
{
return;
}
// Get item + sub-items (weapons + armors), otherwise just get item
const itemWithChildren: Item[] = isPreset
? this.ragfairServerHelper.getPresetItems(assortItem)
: [
...[assortItem],
...this.itemHelper.findAndReturnChildrenByAssort(assortItem._id, this.ragfairAssortGenerator.getAssortItems(),
),
];
// Armor presets can hold plates above the allowed flea level, remove if necessary
if (isPreset && this.ragfairConfig.dynamic.blacklist.enableBsgList)
{
this.removeBannedPlatesFromPreset(itemWithChildren, this.ragfairConfig.dynamic.blacklist.armorPlateMaxProtectionLevel);
this.removeBannedPlatesFromPreset(assortItemWithChildren, this.ragfairConfig.dynamic.blacklist.armorPlateMaxProtectionLevel);
}
// Get number of offers to create
// Limit to 1 offer when processing expired
const offerCount = expiredOffers
// Limit to 1 offer when processing expired - like-for-like replacement
const offerCount = isExpiredOffer
? 1
: Math.round(this.randomUtil.getInt(config.offerItemCount.min, config.offerItemCount.max));
@ -385,13 +373,13 @@ export class RagfairOfferGenerator
if (!isPreset)
{
// Presets get unique id generated during getPresetItems() earlier + would require regenerating all children to match
itemWithChildren[0]._id = this.hashUtil.generate();
assortItemWithChildren[0]._id = this.hashUtil.generate();
}
delete itemWithChildren[0].parentId;
delete itemWithChildren[0].slotId;
delete assortItemWithChildren[0].parentId;
delete assortItemWithChildren[0].slotId;
assortSingleOfferProcesses.push(this.createSingleOfferForItem(itemWithChildren, isPreset, itemDetails));
assortSingleOfferProcesses.push(this.createSingleOfferForItem(assortItemWithChildren, isPreset, itemDetails));
}
await Promise.all(assortSingleOfferProcesses);

View File

@ -261,7 +261,7 @@ export class TraderAssortHelper
protected getRagfairDataAsTraderAssort(): ITraderAssort
{
return {
items: this.ragfairAssortGenerator.getAssortItems(),
items: this.ragfairAssortGenerator.getAssortItems().flat(),
barter_scheme: {},
loyal_level_items: {},
nextResupply: null,

View File

@ -47,27 +47,27 @@ export class RagfairServer
// Generate trader offers
const traders = this.getUpdateableTraders();
for (const traderID of traders)
for (const traderId of traders)
{
// Skip generating fence offers
if (traderID === Traders.FENCE)
if (traderId === Traders.FENCE)
{
continue;
}
if (this.ragfairOfferService.traderOffersNeedRefreshing(traderID))
if (this.ragfairOfferService.traderOffersNeedRefreshing(traderId))
{
this.ragfairOfferGenerator.generateFleaOffersForTrader(traderID);
this.ragfairOfferGenerator.generateFleaOffersForTrader(traderId);
}
}
// Regen expired offers when over threshold count
// Regenerate expired offers when over threshold limit
if (this.ragfairOfferService.getExpiredOfferCount() >= this.ragfairConfig.dynamic.expiredOfferThreshold)
{
const expiredOfferItems = this.ragfairOfferService.getExpiredOfferItems();
await this.ragfairOfferGenerator.generateDynamicOffers(expiredOfferItems);
const expiredAssortsWithChildren = this.ragfairOfferService.getExpiredOfferAssorts();
await this.ragfairOfferGenerator.generateDynamicOffers(expiredAssortsWithChildren);
// reset expired offers now we've genned them
// Clear out expired offers now we've generated them
this.ragfairOfferService.resetExpiredOffers();
}

View File

@ -21,6 +21,7 @@ import { TimeUtil } from "@spt-aki/utils/TimeUtil";
export class RagfairOfferService
{
protected playerOffersLoaded = false;
/** Offer id + offer object */
protected expiredOffers: Record<string, IRagfairOffer> = {};
protected ragfairConfig: IRagfairConfig;
@ -71,27 +72,35 @@ export class RagfairOfferService
this.expiredOffers[staleOffer._id] = staleOffer;
}
/**
* Get total count of current expired offers
* @returns Number of expired offers
*/
public getExpiredOfferCount(): number
{
return Object.keys(this.expiredOffers).length;
}
/**
* Get an array of expired items not yet processed into new offers
* @returns items that need to be turned into offers
* Get an array of arrays of expired offer items + children
* @returns Expired offer assorts
*/
public getExpiredOfferItems(): Item[]
public getExpiredOfferAssorts(): Item[][]
{
const expiredItems: Item[] = [];
const expiredItems: Item[][] = [];
for (const expiredOfferId in this.expiredOffers)
{
expiredItems.push(this.expiredOffers[expiredOfferId].items[0]);
const expiredOffer = this.expiredOffers[expiredOfferId];
expiredItems.push(expiredOffer.items);
}
return expiredItems;
}
/**
* Clear out internal expiredOffers dictionary of all items
*/
public resetExpiredOffers(): void
{
this.expiredOffers = {};