Rework fence service to handle armor/weapons separately,

made config work with a weapon/equipment min/max value
Improve price filtering system to work with weapons/equipment
This commit is contained in:
Dev 2024-02-05 19:52:46 +00:00
parent 3653ab9f83
commit d36593ca57
4 changed files with 145 additions and 27 deletions

View File

@ -49,12 +49,19 @@
"discountOptions": {
"assortSize": 50,
"itemPriceMult": 0.8,
"presetPriceMult": 1.3
"presetPriceMult": 1.2
},
"partialRefreshTimeSeconds": 240,
"partialRefreshChangePercent": 15,
"assortSize": 140,
"maxPresetsPercent": 10,
"assortSize": 150,
"weaponPresetMinMax": {
"min": 8,
"max": 15
},
"equipmentPresetMinMax": {
"min": 8,
"max": 15
},
"itemPriceMult": 1.2,
"presetPriceMult": 2.0,
"regenerateAssortsOnRefresh": false,
@ -66,7 +73,7 @@
"555ef6e44bdc2de9068b457e": 5,
"5a341c4086f77401f2541505": 5,
"55818ad54bdc2ddc698b4569": 5,
"5448e53e4bdc2d60728b4567": 5,
"5448e53e4bdc2d60728b4567": 6,
"55818ac54bdc2d5b648b456e": 5,
"55818af64bdc2d5b648b4570": 5,
"55818a304bdc2db5418b457d": 4,
@ -97,7 +104,7 @@
"min": 35,
"max": 60
},
"chancePlateExistsInArmorPercent": 40,
"chancePlateExistsInArmorPercent": 50,
"armorMaxDurabilityPercentMinMax": {
"min": 35,
"max": 60
@ -119,17 +126,29 @@
"57864a66245977548f04a81f": 71000,
"5448e54d4bdc2dcc718b4568": 200000,
"5a2c3a9486f774688b05e574": 70000,
"5a341c4086f77401f2541505": 125000,
"5448f3a64bdc2d60728b456a": 70000,
"5447b6194bdc2d67278b4567": 500000,
"550aa4cd4bdc2dd8348b456c": 70000,
"57864ee62459775490116fc1": 95000,
"5448bc234bdc2d3c308b4569": 29000,
"5447b6094bdc2dc3278b4567": 35009,
"5447bedf4bdc2d87278b4568": 27008,
"5447bed64bdc2d97278b4568": 27007,
"5447b5e04bdc2d62278b4567": 33006,
"5447b5fc4bdc2d87278b4567": 42005,
"5447b5f14bdc2d61278b4567": 44004,
"5447b5cf4bdc2d65278b4567": 30003,
"5447b6254bdc2dc3278b4568": 28002,
"5447e1d04bdc2dff2f8b4567": 19001,
"55818ae44bdc2dde698b456c": 45000,
"55818add4bdc2d5b648b456f": 35000,
"590c745b86f7743cc433c5f2": 64000,
"57864bb7245977548b3b66c2": 90000,
"5c99f98d86f7745c314214b3": 15000
"57864bb7245977548b3b66c2": 85000,
"5448e5284bdc2dcb718b4567": 40001,
"5a341c4086f77401f2541505": 105000
},
"presetSlotsToRemoveChancePercent": {
"mod_scope": 70,
@ -264,4 +283,4 @@
},
"btrDeliveryExpireHours": 240
}
}
}

View File

@ -1,6 +1,7 @@
import { inject, injectable } from "tsyringe";
import { Category } from "@spt-aki/models/eft/common/tables/IHandbookBase";
import { Item } from "@spt-aki/models/eft/common/tables/IItem";
import { Money } from "@spt-aki/models/enums/Money";
import { DatabaseServer } from "@spt-aki/servers/DatabaseServer";
import { JsonUtil } from "@spt-aki/utils/JsonUtil";
@ -102,6 +103,17 @@ export class HandbookHelper
return handbookItem.Price;
}
public getTemplatePriceForItems(items: Item[]): number
{
let total = 0;
for (const item of items)
{
total += this.getTemplatePrice(item._tpl);
}
return total;
}
/**
* Get all items in template with the given parent category
* @param parentId

View File

@ -26,7 +26,8 @@ export interface FenceConfig
partialRefreshTimeSeconds: number;
partialRefreshChangePercent: number;
assortSize: number;
maxPresetsPercent: number;
weaponPresetMinMax: MinMax;
equipmentPresetMinMax: MinMax;
itemPriceMult: number;
presetPriceMult: number;
armorMaxDurabilityPercentMinMax: MinMax;

View File

@ -422,9 +422,15 @@ export class FenceService
this.addItemAssorts(assortCount, assorts, baseFenceAssort, itemTypeCounts, loyaltyLevel);
// Add presets
const maxPresetCount = Math.round(assortCount * (this.traderConfig.fence.maxPresetsPercent / 100));
const randomisedPresetCount = this.randomUtil.getInt(0, maxPresetCount);
this.addPresetsToAssort(randomisedPresetCount, assorts, baseFenceAssort, loyaltyLevel);
const weaponPresetCount = this.randomUtil.getInt(
this.traderConfig.fence.weaponPresetMinMax.min,
this.traderConfig.fence.weaponPresetMinMax.max,
);
const equipmentPresetCount = this.randomUtil.getInt(
this.traderConfig.fence.equipmentPresetMinMax.min,
this.traderConfig.fence.equipmentPresetMinMax.max,
);
this.addPresetsToAssort(weaponPresetCount, equipmentPresetCount, assorts, baseFenceAssort, loyaltyLevel);
}
protected addItemAssorts(
@ -509,39 +515,37 @@ export class FenceService
/**
* Find presets in base fence assort and add desired number to 'assorts' parameter
* @param desiredPresetCount
* @param desiredWeaponPresetsCount
* @param assorts
* @param baseFenceAssort
* @param loyaltyLevel Which loyalty level is required to see/buy item
*/
protected addPresetsToAssort(
desiredPresetCount: number,
desiredWeaponPresetsCount: number,
desiredEquipmentPresetsCount: number,
assorts: ITraderAssort,
baseFenceAssort: ITraderAssort,
loyaltyLevel: number,
): void
{
let presetsAddedCount = 0;
if (desiredPresetCount <= 0)
let weaponPresetsAddedCount = 0;
if (desiredWeaponPresetsCount <= 0)
{
return;
}
const presetRootItems = baseFenceAssort.items.filter((item) => item.upd?.sptPresetId);
while (presetsAddedCount < desiredPresetCount)
const weaponPresetRootItems = baseFenceAssort.items.filter((item) =>
item.upd?.sptPresetId && this.itemHelper.isOfBaseclass(item._tpl, BaseClasses.WEAPON)
);
while (weaponPresetsAddedCount < desiredWeaponPresetsCount)
{
const randomPresetRoot = this.randomUtil.getArrayValue(presetRootItems);
const randomPresetRoot = this.randomUtil.getArrayValue(weaponPresetRootItems);
const rootItemDb = this.itemHelper.getItem(randomPresetRoot._tpl)[1];
const presetWithChildrenClone = this.jsonUtil.clone(
this.itemHelper.findAndReturnChildrenAsItems(baseFenceAssort.items, randomPresetRoot._id),
);
// Need to add mods to armors so they dont show as red in the trade screen
if (this.itemHelper.itemRequiresSoftInserts(randomPresetRoot._tpl))
{
this.randomiseArmorModDurability(presetWithChildrenClone, rootItemDb);
}
if (this.itemHelper.isOfBaseclass(rootItemDb._id, BaseClasses.WEAPON))
{
this.randomiseItemUpdProperties(rootItemDb, presetWithChildrenClone[0]);
@ -549,6 +553,26 @@ export class FenceService
this.removeRandomModsOfItem(presetWithChildrenClone);
// Check chosen item is below price cap
const priceLimitRouble = this.traderConfig.fence.itemCategoryRoublePriceLimit[rootItemDb._parent];
if (priceLimitRouble)
{
if (this.handbookHelper.getTemplatePriceForItems(presetWithChildrenClone) > priceLimitRouble)
{
// Too expensive, try again
this.logger.warning(
`Blocked ${rootItemDb._name}, price: ${
this.handbookHelper.getTemplatePriceForItems(presetWithChildrenClone)
} limit: ${priceLimitRouble}`,
);
continue;
}
}
else
{
this.logger.warning(`No limit ${rootItemDb._name} ${rootItemDb._parent}`);
}
// MUST randomise Ids as its possible to add the same base fence assort twice = duplicate IDs = dead client
this.itemHelper.reparentItemAndChildren(presetWithChildrenClone[0], presetWithChildrenClone);
this.itemHelper.remapRootItemId(presetWithChildrenClone);
@ -562,7 +586,69 @@ export class FenceService
assorts.barter_scheme[presetWithChildrenClone[0]._id] = baseFenceAssort.barter_scheme[randomPresetRoot._id];
assorts.loyal_level_items[presetWithChildrenClone[0]._id] = loyaltyLevel;
presetsAddedCount++;
weaponPresetsAddedCount++;
}
let equipmentPresetsAddedCount = 0;
if (desiredEquipmentPresetsCount <= 0)
{
return;
}
const equipmentPresetRootItems = baseFenceAssort.items.filter((item) =>
item.upd?.sptPresetId && this.itemHelper.armorItemCanHoldMods(item._tpl)
);
while (equipmentPresetsAddedCount < desiredEquipmentPresetsCount)
{
const randomPresetRoot = this.randomUtil.getArrayValue(equipmentPresetRootItems);
const rootItemDb = this.itemHelper.getItem(randomPresetRoot._tpl)[1];
const presetWithChildrenClone = this.jsonUtil.clone(
this.itemHelper.findAndReturnChildrenAsItems(baseFenceAssort.items, randomPresetRoot._id),
);
// Need to add mods to armors so they dont show as red in the trade screen
if (this.itemHelper.itemRequiresSoftInserts(randomPresetRoot._tpl))
{
this.randomiseArmorModDurability(presetWithChildrenClone, rootItemDb);
}
this.removeRandomModsOfItem(presetWithChildrenClone);
// Check chosen item is below price cap
const priceLimitRouble = this.traderConfig.fence.itemCategoryRoublePriceLimit[rootItemDb._parent];
if (priceLimitRouble)
{
if (this.handbookHelper.getTemplatePriceForItems(presetWithChildrenClone) > priceLimitRouble)
{
// Too expensive, try again
this.logger.warning(
`Blocked ${rootItemDb._name}, price: ${
this.handbookHelper.getTemplatePriceForItems(presetWithChildrenClone)
} limit: ${priceLimitRouble}`,
);
continue;
}
}
else
{
this.logger.warning(`No limit ${rootItemDb._name} ${rootItemDb._parent}`);
}
// MUST randomise Ids as its possible to add the same base fence assort twice = duplicate IDs = dead client
this.itemHelper.reparentItemAndChildren(presetWithChildrenClone[0], presetWithChildrenClone);
this.itemHelper.remapRootItemId(presetWithChildrenClone);
// Remapping IDs causes parentid to be altered
presetWithChildrenClone[0].parentId = "hideout";
assorts.items.push(...presetWithChildrenClone);
// Must be careful to use correct id as the item has had its IDs regenerated
assorts.barter_scheme[presetWithChildrenClone[0]._id] = baseFenceAssort.barter_scheme[randomPresetRoot._id];
assorts.loyal_level_items[presetWithChildrenClone[0]._id] = loyaltyLevel;
equipmentPresetsAddedCount++;
}
}