Finalise fence changes

This commit is contained in:
Dev 2024-01-20 17:56:19 +00:00
parent 42b915990e
commit 80fdaf65e6
3 changed files with 82 additions and 156 deletions

View File

@ -39,7 +39,7 @@
"discountOptions": { "discountOptions": {
"assortSize": 50, "assortSize": 50,
"itemPriceMult": 0.8, "itemPriceMult": 0.8,
"presetPriceMult": 1.5 "presetPriceMult": 1.3
}, },
"partialRefreshTimeSeconds": 240, "partialRefreshTimeSeconds": 240,
"partialRefreshChangePercent": 15, "partialRefreshChangePercent": 15,
@ -168,7 +168,11 @@
"62f10b79e7ee985f386b2f47", "62f10b79e7ee985f386b2f47",
"633ffb5d419dbf4bea7004c6", "633ffb5d419dbf4bea7004c6",
"543be5dd4bdc2deb348b4569", "543be5dd4bdc2deb348b4569",
"65649eb40bf0ed77b8044453" "65649eb40bf0ed77b8044453",
"5448e54d4bdc2dcc718b4568",
"5a341c4086f77401f2541505",
"5422acb9af1c889c16000029",
"5448e5284bdc2dcb718b4567"
], ],
"coopExtractGift": { "coopExtractGift": {
"sendGift": true, "sendGift": true,

View File

@ -40,7 +40,7 @@ export class FenceBaseAssortGenerator
} }
/** /**
* Create base fence assorts dynamically and store in db * Create base fence assorts dynamically and store in memory
*/ */
public generateFenceBaseAssorts(): void public generateFenceBaseAssorts(): void
{ {
@ -84,17 +84,9 @@ export class FenceBaseAssortGenerator
_tpl: rootItemDb._id, _tpl: rootItemDb._id,
parentId: "hideout", parentId: "hideout",
slotId: "hideout", slotId: "hideout",
upd: { StackObjectsCount: 9999999, UnlimitedCount: true }, upd: { StackObjectsCount: 9999999 },
}]; }];
// Need to add mods to armors so they dont show as red in the trade screen
let price = this.handbookHelper.getTemplatePrice(rootItemDb._id);
if (this.itemHelper.itemRequiresSoftInserts(rootItemDb._id))
{
this.addChildrenToArmorModSlots(itemWithChildrenToAdd, rootItemDb);
price = this.getHandbookItemPriceWithChildren(itemWithChildrenToAdd);
}
// Ensure IDs are unique // Ensure IDs are unique
this.itemHelper.remapRootItemId(itemWithChildrenToAdd); this.itemHelper.remapRootItemId(itemWithChildrenToAdd);
if (itemWithChildrenToAdd.length > 1) if (itemWithChildrenToAdd.length > 1)
@ -136,6 +128,7 @@ export class FenceBaseAssortGenerator
this.jsonUtil.clone(defaultPreset._items), this.jsonUtil.clone(defaultPreset._items),
); );
// Find root item and add some properties to it
for (let i = 0; i < presetAndMods.length; i++) for (let i = 0; i < presetAndMods.length; i++)
{ {
const mod = presetAndMods[i]; const mod = presetAndMods[i];
@ -143,13 +136,10 @@ export class FenceBaseAssortGenerator
// Build root Item info // Build root Item info
if (!("parentId" in mod)) if (!("parentId" in mod))
{ {
mod._id = presetAndMods[0]._id;
mod.parentId = "hideout"; mod.parentId = "hideout";
mod.slotId = "hideout"; mod.slotId = "hideout";
mod.upd = { mod.upd = {
UnlimitedCount: false,
StackObjectsCount: 1, StackObjectsCount: 1,
BuyRestrictionCurrent: 0,
sptPresetId: defaultPreset._id, // Store preset id here so we can check it later to prevent preset dupes sptPresetId: defaultPreset._id, // Store preset id here so we can check it later to prevent preset dupes
}; };
@ -158,21 +148,15 @@ export class FenceBaseAssortGenerator
} }
} }
const presetDbItem = this.itemHelper.getItem(presetAndMods[0]._tpl)[1];
// Add constructed preset to assorts // Add constructed preset to assorts
baseFenceAssort.items.push(...presetAndMods); baseFenceAssort.items.push(...presetAndMods);
// Calculate preset price // Calculate preset price
let rub = 0; const price = this.getHandbookItemPriceWithChildren(presetAndMods);
for (const it of presetAndMods)
{
rub += this.handbookHelper.getTemplatePrice(it._tpl);
}
// Multiply weapon+mods rouble price by multipler in config // Multiply weapon+mods rouble price by multipler in config
baseFenceAssort.barter_scheme[presetAndMods[0]._id] = [[]]; baseFenceAssort.barter_scheme[presetAndMods[0]._id] = [[]];
baseFenceAssort.barter_scheme[presetAndMods[0]._id][0][0] = { _tpl: Money.ROUBLES, count: Math.round(rub) }; baseFenceAssort.barter_scheme[presetAndMods[0]._id][0][0] = { _tpl: Money.ROUBLES, count: Math.round(price) * this.traderConfig.fence.presetPriceMult };
baseFenceAssort.loyal_level_items[presetAndMods[0]._id] = 1; baseFenceAssort.loyal_level_items[presetAndMods[0]._id] = 1;
} }

View File

@ -4,14 +4,13 @@ import { HandbookHelper } from "@spt-aki/helpers/HandbookHelper";
import { ItemHelper } from "@spt-aki/helpers/ItemHelper"; import { ItemHelper } from "@spt-aki/helpers/ItemHelper";
import { PresetHelper } from "@spt-aki/helpers/PresetHelper"; import { PresetHelper } from "@spt-aki/helpers/PresetHelper";
import { MinMax } from "@spt-aki/models/common/MinMax"; import { MinMax } from "@spt-aki/models/common/MinMax";
import { IFenceLevel, IPreset } from "@spt-aki/models/eft/common/IGlobals"; import { IFenceLevel } from "@spt-aki/models/eft/common/IGlobals";
import { IPmcData } from "@spt-aki/models/eft/common/IPmcData"; import { IPmcData } from "@spt-aki/models/eft/common/IPmcData";
import { Item, Repairable } from "@spt-aki/models/eft/common/tables/IItem"; import { Item, Repairable } from "@spt-aki/models/eft/common/tables/IItem";
import { ITemplateItem } from "@spt-aki/models/eft/common/tables/ITemplateItem"; import { ITemplateItem } from "@spt-aki/models/eft/common/tables/ITemplateItem";
import { ITraderAssort } from "@spt-aki/models/eft/common/tables/ITrader"; import { ITraderAssort } from "@spt-aki/models/eft/common/tables/ITrader";
import { BaseClasses } from "@spt-aki/models/enums/BaseClasses"; import { BaseClasses } from "@spt-aki/models/enums/BaseClasses";
import { ConfigTypes } from "@spt-aki/models/enums/ConfigTypes"; import { ConfigTypes } from "@spt-aki/models/enums/ConfigTypes";
import { Money } from "@spt-aki/models/enums/Money";
import { Traders } from "@spt-aki/models/enums/Traders"; import { Traders } from "@spt-aki/models/enums/Traders";
import { ITraderConfig } from "@spt-aki/models/spt/config/ITraderConfig"; import { ITraderConfig } from "@spt-aki/models/spt/config/ITraderConfig";
import { ILogger } from "@spt-aki/models/spt/utils/ILogger"; import { ILogger } from "@spt-aki/models/spt/utils/ILogger";
@ -33,7 +32,7 @@ export class FenceService
{ {
/** Main assorts you see at all rep levels */ /** Main assorts you see at all rep levels */
protected fenceAssort: ITraderAssort = undefined; protected fenceAssort: ITraderAssort = undefined;
/** Assorts shown on a separte tab when you max out fence rep */ /** Assorts shown on a separate tab when you max out fence rep */
protected fenceDiscountAssort: ITraderAssort = undefined; protected fenceDiscountAssort: ITraderAssort = undefined;
protected traderConfig: ITraderConfig; protected traderConfig: ITraderConfig;
protected nextMiniRefreshTimestamp: number; protected nextMiniRefreshTimestamp: number;
@ -67,11 +66,11 @@ export class FenceService
/** /**
* Replace high rep level fence assort with new assort * Replace high rep level fence assort with new assort
* @param assort New assorts to replace old with * @param discountAssort New assorts to replace old with
*/ */
public setFenceDiscountAssort(assort: ITraderAssort): void public setFenceDiscountAssort(discountAssort: ITraderAssort): void
{ {
this.fenceDiscountAssort = assort; this.fenceDiscountAssort = discountAssort;
} }
/** /**
@ -238,8 +237,8 @@ export class FenceService
itemCountToReplace = this.getCountOfItemsToGenerate(itemCountToReplace); itemCountToReplace = this.getCountOfItemsToGenerate(itemCountToReplace);
const newItems = this.createBaseTraderAssortItem(); const newItems = this.createFenceAssortSkeleton();
const newDiscountItems = this.createBaseTraderAssortItem(); const newDiscountItems = this.createFenceAssortSkeleton();
this.createAssorts(itemCountToReplace, newItems, 1); this.createAssorts(itemCountToReplace, newItems, 1);
this.createAssorts(discountItemCountToReplace, newDiscountItems, 2); this.createAssorts(discountItemCountToReplace, newDiscountItems, 2);
@ -366,21 +365,22 @@ export class FenceService
/** /**
* Create trader assorts for fence and store in fenceService cache * Create trader assorts for fence and store in fenceService cache
* Uses fence base cache generatedon server start as a base
*/ */
public generateFenceAssorts(): void public generateFenceAssorts(): void
{ {
// Reset refresh time now assorts are being generated // Reset refresh time now assorts are being generated
this.incrementPartialRefreshTime(); this.incrementPartialRefreshTime();
const assorts = this.createBaseTraderAssortItem(); const assorts = this.createFenceAssortSkeleton();
const discountAssorts = this.createBaseTraderAssortItem(); const discountAssorts = this.createFenceAssortSkeleton();
// Create basic fence assort // Create basic fence assort
this.createAssorts(this.traderConfig.fence.assortSize, assorts, 1); this.createAssorts(this.traderConfig.fence.assortSize, assorts, 1);
// Create level 2 assorts accessible at rep level 6 // Create level 2 assorts accessible at rep level 6
this.createAssorts(this.traderConfig.fence.discountOptions.assortSize, discountAssorts, 2); this.createAssorts(this.traderConfig.fence.discountOptions.assortSize, discountAssorts, 2);
// store in fenceAssort class property // store in fenceAssort class properties
this.setFenceAssort(assorts); this.setFenceAssort(assorts);
this.setFenceDiscountAssort(discountAssorts); this.setFenceDiscountAssort(discountAssorts);
} }
@ -389,7 +389,7 @@ export class FenceService
* Create skeleton to hold assort items * Create skeleton to hold assort items
* @returns ITraderAssort object * @returns ITraderAssort object
*/ */
protected createBaseTraderAssortItem(): ITraderAssort protected createFenceAssortSkeleton(): ITraderAssort
{ {
return { return {
items: [], items: [],
@ -414,38 +414,7 @@ export class FenceService
// Add presets // Add presets
const maxPresetCount = Math.round(assortCount * (this.traderConfig.fence.maxPresetsPercent / 100)); const maxPresetCount = Math.round(assortCount * (this.traderConfig.fence.maxPresetsPercent / 100));
const randomisedPresetCount = this.randomUtil.getInt(0, maxPresetCount); const randomisedPresetCount = this.randomUtil.getInt(0, maxPresetCount);
this.addPresetsToAssort(randomisedPresetCount, assorts, baseFenceAssort); this.addPresetsToAssort(randomisedPresetCount, assorts, baseFenceAssort, loyaltyLevel);
}
protected addPresetsToAssort(desiredPresetCount: number, assorts: ITraderAssort, baseFenceAssort: ITraderAssort): void
{
let presetsAddedCount = 0;
if (desiredPresetCount <= 0)
{
return;
}
const presetRootItems = baseFenceAssort.items.filter(item => item.upd?.sptPresetId);
while (presetsAddedCount < desiredPresetCount)
{
const randomPresetRoot = this.randomUtil.getArrayValue(presetRootItems);
const rootItemDb = this.itemHelper.getItem(randomPresetRoot._tpl)[1];
const presetWithChildrenClone = this.jsonUtil.clone(this.itemHelper.findAndReturnChildrenAsItems(baseFenceAssort.items, randomPresetRoot._id));
this.removeRandomModsOfItem(presetWithChildrenClone);
// 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);
}
assorts.items.push(...presetWithChildrenClone);
assorts.barter_scheme[randomPresetRoot._id] = baseFenceAssort.barter_scheme[randomPresetRoot._id];
assorts.loyal_level_items[randomPresetRoot._id] = baseFenceAssort.loyal_level_items[randomPresetRoot._id];
presetsAddedCount++;
}
} }
protected addItemAssorts( protected addItemAssorts(
@ -457,7 +426,7 @@ export class FenceService
): void ): void
{ {
const priceLimits = this.traderConfig.fence.itemCategoryRoublePriceLimit; const priceLimits = this.traderConfig.fence.itemCategoryRoublePriceLimit;
const assortRootItems = fenceAssort.items.filter(x => x.parentId === "hideout"); const assortRootItems = fenceAssort.items.filter(x => x.parentId === "hideout" && !x.upd?.sptPresetId);
for (let i = 0; i < assortCount; i++) for (let i = 0; i < assortCount; i++)
{ {
const chosenAssortRoot = this.randomUtil.getArrayValue(assortRootItems); const chosenAssortRoot = this.randomUtil.getArrayValue(assortRootItems);
@ -500,12 +469,16 @@ export class FenceService
itemLimitCount.current++; itemLimitCount.current++;
} }
// MUST randomise Ids as its possible to add the same base fence assort twice = duplicate IDs = dead client
this.itemHelper.remapRootItemId(desiredAssortItemAndChildrenClone);
this.itemHelper.replaceIDs(null, desiredAssortItemAndChildrenClone);
const rootItemBeingAdded = desiredAssortItemAndChildrenClone[0]; const rootItemBeingAdded = desiredAssortItemAndChildrenClone[0];
this.randomiseItemUpdProperties(itemDbDetails, rootItemBeingAdded); this.randomiseItemUpdProperties(itemDbDetails, rootItemBeingAdded);
rootItemBeingAdded.upd.StackObjectsCount = this.getSingleItemStackCount(itemDbDetails); rootItemBeingAdded.upd.StackObjectsCount = this.getSingleItemStackCount(itemDbDetails);
rootItemBeingAdded.upd.BuyRestrictionCurrent = 0; //rootItemBeingAdded.upd.BuyRestrictionCurrent = 0;
rootItemBeingAdded.upd.UnlimitedCount = false; //rootItemBeingAdded.upd.UnlimitedCount = false;
// Need to add mods to armors so they dont show as red in the trade screen // Need to add mods to armors so they dont show as red in the trade screen
if (this.itemHelper.itemRequiresSoftInserts(rootItemBeingAdded._tpl)) if (this.itemHelper.itemRequiresSoftInserts(rootItemBeingAdded._tpl))
@ -519,6 +492,55 @@ export class FenceService
} }
} }
/**
* Find presets in base fence assort and add desired number to 'assorts' parameter
* @param desiredPresetCount
* @param assorts
* @param baseFenceAssort
* @param loyaltyLevel Which loyalty level is required to see/buy item
*/
protected addPresetsToAssort(desiredPresetCount: number, assorts: ITraderAssort, baseFenceAssort: ITraderAssort, loyaltyLevel: number): void
{
let presetsAddedCount = 0;
if (desiredPresetCount <= 0)
{
return;
}
const presetRootItems = baseFenceAssort.items.filter(item => item.upd?.sptPresetId);
while (presetsAddedCount < desiredPresetCount)
{
const randomPresetRoot = this.randomUtil.getArrayValue(presetRootItems);
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]);
}
this.removeRandomModsOfItem(presetWithChildrenClone);
// MUST randomise Ids as its possible to add the same base fence assort twice = duplicate IDs = dead client
this.itemHelper.remapRootItemId(presetWithChildrenClone);
this.itemHelper.replaceIDs(null, presetWithChildrenClone);
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;
presetsAddedCount++;
}
}
/** /**
* Adjust plate / soft insert durability values * Adjust plate / soft insert durability values
* @param armor Armor item array to add mods into * @param armor Armor item array to add mods into
@ -552,7 +574,7 @@ export class FenceService
} }
// Find items mod to apply dura changes to // Find items mod to apply dura changes to
const modItemToAdjust = armor.find(mod => mod.slotId === requiredSlot._name); const modItemToAdjust = armor.find(mod => mod.slotId.toLowerCase() === requiredSlot._name.toLowerCase());
if (!modItemToAdjust.upd) if (!modItemToAdjust.upd)
{ {
modItemToAdjust.upd = {} modItemToAdjust.upd = {}
@ -604,7 +626,7 @@ export class FenceService
this.traderConfig.fence.armorMaxDurabilityPercentMinMax); this.traderConfig.fence.armorMaxDurabilityPercentMinMax);
// Find items mod to apply dura changes to // Find items mod to apply dura changes to
const modItemToAdjust = armor.find(mod => mod.slotId === plateSlot._name); const modItemToAdjust = armor.find(mod => mod.slotId.toLowerCase() === plateSlot._name.toLowerCase());
if (!modItemToAdjust.upd) if (!modItemToAdjust.upd)
{ {
modItemToAdjust.upd = {} modItemToAdjust.upd = {}
@ -649,90 +671,6 @@ export class FenceService
return 1; return 1;
} }
/**
* Add weapon/armor presets to fence
* @param assortCount how many assorts to add to assorts
* @param defaultPresets a dictionary of default weapon presets
* @param assorts Trader assort object to add preset to
* @param loyaltyLevel loyalty level to requre item at
*/
protected addPresets(
desiredPresetCount: number,
defaultPresets: Record<string, IPreset>,
assorts: ITraderAssort,
loyaltyLevel: number,
): void
{
let presetCount = 0;
const presetKeys = Object.keys(defaultPresets);
for (let index = 0; index < desiredPresetCount; index++)
{
const presetId = presetKeys[this.randomUtil.getInt(0, presetKeys.length - 1)];
const preset = defaultPresets[presetId];
// Check we're under preset limit
if (presetCount > desiredPresetCount)
{
return;
}
// Skip presets we've already added
if (assorts.items.some((i) => i.upd && i.upd.sptPresetId === preset._id))
{
continue;
}
// Construct preset + mods
const presetAndMods: Item[] = this.itemHelper.replaceIDs(
null,
this.jsonUtil.clone(preset._items),
);
this.removeRandomModsOfItem(presetAndMods);
for (let i = 0; i < presetAndMods.length; i++)
{
const mod = presetAndMods[i];
// Build root Item info
if (!("parentId" in mod))
{
mod._id = presetAndMods[0]._id;
mod.parentId = "hideout";
mod.slotId = "hideout";
mod.upd = {
UnlimitedCount: false,
StackObjectsCount: 1,
BuyRestrictionCurrent: 0,
sptPresetId: preset._id, // Store preset id here so we can check it later to prevent preset dupes
};
// Updated root item, exit loop
break;
}
}
const presetDbItem = this.itemHelper.getItem(presetAndMods[0]._tpl)[1];
this.randomiseItemUpdProperties(presetDbItem, presetAndMods[0]);
// Add constructed preset to assorts
assorts.items.push(...presetAndMods);
// Calculate preset price
let rub = 0;
for (const it of presetAndMods)
{
rub += this.handbookHelper.getTemplatePrice(it._tpl);
}
// Multiply weapon+mods rouble price by multipler in config
assorts.barter_scheme[presetAndMods[0]._id] = [[]];
assorts.barter_scheme[presetAndMods[0]._id][0][0] = { _tpl: Money.ROUBLES, count: Math.round(rub) };
assorts.loyal_level_items[presetAndMods[0]._id] = loyaltyLevel;
presetCount++;
}
}
/** /**
* Remove parts of a weapon prior to being listed on flea * Remove parts of a weapon prior to being listed on flea
* @param itemAndMods Weapon to remove parts from * @param itemAndMods Weapon to remove parts from