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() @injectable()
export class RagfairAssortGenerator export class RagfairAssortGenerator
{ {
protected generatedAssortItems: Item[] = []; protected generatedAssortItems: Item[][] = [];
protected ragfairConfig: IRagfairConfig; protected ragfairConfig: IRagfairConfig;
protected ragfairItemInvalidBaseTypes: string[] = [ protected ragfairItemInvalidBaseTypes: string[] = [
@ -43,10 +43,11 @@ export class RagfairAssortGenerator
} }
/** /**
* Get an array of unique items that can be sold on the flea * Get an array of arrays that can be sold on the flea
* @returns array of unique items * Each sub array contains item + children (if any)
* @returns array of arrays
*/ */
public getAssortItems(): Item[] public getAssortItems(): Item[][]
{ {
if (!this.assortsAreGenerated()) if (!this.assortsAreGenerated())
{ {
@ -66,16 +67,17 @@ export class RagfairAssortGenerator
} }
/** /**
* Generate an array of items the flea can sell * Generate an array of arrays (item + children) the flea can sell
* @returns array of unique items * @returns array of arrays (item + children)
*/ */
protected generateRagfairAssortItems(): Item[] protected generateRagfairAssortItems(): Item[][]
{ {
const results: Item[] = []; const results: Item[][] = [];
/** Get cloned items from db */ /** Get cloned items from db */
const dbItemsClone = this.itemHelper.getItems().filter(item => item._type !== "Node"); 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 processedArmorItems: string[] = [];
const seasonalEventActive = this.seasonalEventService.seasonalEventEnabled(); const seasonalEventActive = this.seasonalEventService.seasonalEventEnabled();
const seasonalItemTplBlacklist = this.seasonalEventService.getInactiveSeasonalEventItems(); const seasonalItemTplBlacklist = this.seasonalEventService.getInactiveSeasonalEventItems();
@ -83,13 +85,21 @@ export class RagfairAssortGenerator
const presets = this.getPresetsToAdd(); const presets = this.getPresetsToAdd();
for (const preset of presets) 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 // 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 presetAndMods[0].parentId = "hideout";
results.push(this.createRagfairAssortItem(presetItemTpl, preset._id)); presetAndMods[0].slotId = "hideout";
presetAndMods[0].upd = { StackObjectsCount: 99999999, UnlimitedCount: true, sptPresetId: preset._id};
results.push(presetAndMods);
} }
for (const item of dbItemsClone) for (const item of dbItemsClone)
@ -114,7 +124,9 @@ export class RagfairAssortGenerator
continue; 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; 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 * 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 tplId tplid to add to item
* @param id id 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 { return {
_id: id, _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 * Create multiple offers for items by using a unique list of items we've generated previously
* @param expiredOffers optional, expired offers to regenerate * @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; const config = this.ragfairConfig.dynamic;
// get assort items from param if they exist, otherwise grab freshly generated assorts // get assort items from param if they exist, otherwise grab freshly generated assorts
const assortItemsToProcess: Item[] = expiredOffers const assortItemsToProcess: Item[][] = replacingExpiredOffers
? expiredOffers ? expiredOffers
: this.ragfairAssortGenerator.getAssortItems(); : this.ragfairAssortGenerator.getAssortItems();
// Store all functions to create an offer for every item and pass into Promise.all to run async // Store all functions to create an offer for every item and pass into Promise.all to run async
const assorOffersForItemsProcesses = []; const assorOffersForItemsProcesses = [];
for (const assortItemIndex in assortItemsToProcess) for (const assortItemWithChildren of assortItemsToProcess)
{ {
assorOffersForItemsProcesses.push( 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 assortItemWithChildren Item with its children to process into offers
* @param assortItemsToProcess Item array containing index * @param isExpiredOffer is an expired offer
* @param expiredOffers Currently expired offers on flea
* @param config Ragfair dynamic config * @param config Ragfair dynamic config
*/ */
protected async createOffersForItems( protected async createOffersFromAssort(
assortItemIndex: string, assortItemWithChildren: Item[],
assortItemsToProcess: Item[], isExpiredOffer: boolean,
expiredOffers: Item[],
config: Dynamic, config: Dynamic,
): Promise<void> ): Promise<void>
{ {
const assortItem = assortItemsToProcess[assortItemIndex]; const itemDetails = this.itemHelper.getItem(assortItemWithChildren[0]._tpl);
const itemDetails = this.itemHelper.getItem(assortItem._tpl); const isPreset = this.presetHelper.isPreset(assortItemWithChildren[0].upd.sptPresetId);
const isPreset = this.presetHelper.isPreset(assortItem._id);
// Only perform checks on newly generated items, skip expired items being refreshed // 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; 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 // Armor presets can hold plates above the allowed flea level, remove if necessary
if (isPreset && this.ragfairConfig.dynamic.blacklist.enableBsgList) 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 // Get number of offers to create
// Limit to 1 offer when processing expired // Limit to 1 offer when processing expired - like-for-like replacement
const offerCount = expiredOffers const offerCount = isExpiredOffer
? 1 ? 1
: Math.round(this.randomUtil.getInt(config.offerItemCount.min, config.offerItemCount.max)); : Math.round(this.randomUtil.getInt(config.offerItemCount.min, config.offerItemCount.max));
@ -385,13 +373,13 @@ export class RagfairOfferGenerator
if (!isPreset) if (!isPreset)
{ {
// Presets get unique id generated during getPresetItems() earlier + would require regenerating all children to match // 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 assortItemWithChildren[0].parentId;
delete itemWithChildren[0].slotId; delete assortItemWithChildren[0].slotId;
assortSingleOfferProcesses.push(this.createSingleOfferForItem(itemWithChildren, isPreset, itemDetails)); assortSingleOfferProcesses.push(this.createSingleOfferForItem(assortItemWithChildren, isPreset, itemDetails));
} }
await Promise.all(assortSingleOfferProcesses); await Promise.all(assortSingleOfferProcesses);

View File

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

View File

@ -47,27 +47,27 @@ export class RagfairServer
// Generate trader offers // Generate trader offers
const traders = this.getUpdateableTraders(); const traders = this.getUpdateableTraders();
for (const traderID of traders) for (const traderId of traders)
{ {
// Skip generating fence offers // Skip generating fence offers
if (traderID === Traders.FENCE) if (traderId === Traders.FENCE)
{ {
continue; 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) if (this.ragfairOfferService.getExpiredOfferCount() >= this.ragfairConfig.dynamic.expiredOfferThreshold)
{ {
const expiredOfferItems = this.ragfairOfferService.getExpiredOfferItems(); const expiredAssortsWithChildren = this.ragfairOfferService.getExpiredOfferAssorts();
await this.ragfairOfferGenerator.generateDynamicOffers(expiredOfferItems); 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(); this.ragfairOfferService.resetExpiredOffers();
} }

View File

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