Moe majority of assort generation from FenceService
into FenceBaseAssortGenerator
Generate an items children and store in fence assort base Better handle presets Fix `removeRandomItemFromAssorts()` not removing all of an items mods from memory Correctly calculate an items price including its children
This commit is contained in:
parent
8b2fa7c8dd
commit
42b915990e
@ -2,6 +2,7 @@ import { inject, injectable } from "tsyringe";
|
|||||||
|
|
||||||
import { HandbookHelper } from "@spt-aki/helpers/HandbookHelper";
|
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 { Item } from "@spt-aki/models/eft/common/tables/IItem";
|
import { Item } 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 { IBarterScheme } from "@spt-aki/models/eft/common/tables/ITrader";
|
import { IBarterScheme } from "@spt-aki/models/eft/common/tables/ITrader";
|
||||||
@ -14,6 +15,8 @@ import { ConfigServer } from "@spt-aki/servers/ConfigServer";
|
|||||||
import { DatabaseServer } from "@spt-aki/servers/DatabaseServer";
|
import { DatabaseServer } from "@spt-aki/servers/DatabaseServer";
|
||||||
import { ItemFilterService } from "@spt-aki/services/ItemFilterService";
|
import { ItemFilterService } from "@spt-aki/services/ItemFilterService";
|
||||||
import { SeasonalEventService } from "@spt-aki/services/SeasonalEventService";
|
import { SeasonalEventService } from "@spt-aki/services/SeasonalEventService";
|
||||||
|
import { HashUtil } from "@spt-aki/utils/HashUtil";
|
||||||
|
import { JsonUtil } from "@spt-aki/utils/JsonUtil";
|
||||||
|
|
||||||
@injectable()
|
@injectable()
|
||||||
export class FenceBaseAssortGenerator
|
export class FenceBaseAssortGenerator
|
||||||
@ -22,9 +25,12 @@ export class FenceBaseAssortGenerator
|
|||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@inject("WinstonLogger") protected logger: ILogger,
|
@inject("WinstonLogger") protected logger: ILogger,
|
||||||
|
@inject("HashUtil") protected hashUtil: HashUtil,
|
||||||
|
@inject("JsonUtil") protected jsonUtil: JsonUtil,
|
||||||
@inject("DatabaseServer") protected databaseServer: DatabaseServer,
|
@inject("DatabaseServer") protected databaseServer: DatabaseServer,
|
||||||
@inject("HandbookHelper") protected handbookHelper: HandbookHelper,
|
@inject("HandbookHelper") protected handbookHelper: HandbookHelper,
|
||||||
@inject("ItemHelper") protected itemHelper: ItemHelper,
|
@inject("ItemHelper") protected itemHelper: ItemHelper,
|
||||||
|
@inject("PresetHelper") protected presetHelper: PresetHelper,
|
||||||
@inject("ItemFilterService") protected itemFilterService: ItemFilterService,
|
@inject("ItemFilterService") protected itemFilterService: ItemFilterService,
|
||||||
@inject("SeasonalEventService") protected seasonalEventService: SeasonalEventService,
|
@inject("SeasonalEventService") protected seasonalEventService: SeasonalEventService,
|
||||||
@inject("ConfigServer") protected configServer: ConfigServer,
|
@inject("ConfigServer") protected configServer: ConfigServer,
|
||||||
@ -39,29 +45,27 @@ export class FenceBaseAssortGenerator
|
|||||||
public generateFenceBaseAssorts(): void
|
public generateFenceBaseAssorts(): void
|
||||||
{
|
{
|
||||||
const blockedSeasonalItems = this.seasonalEventService.getInactiveSeasonalEventItems();
|
const blockedSeasonalItems = this.seasonalEventService.getInactiveSeasonalEventItems();
|
||||||
|
|
||||||
const baseFenceAssort = this.databaseServer.getTables().traders[Traders.FENCE].assort;
|
const baseFenceAssort = this.databaseServer.getTables().traders[Traders.FENCE].assort;
|
||||||
|
|
||||||
const dbItems = Object.values(this.databaseServer.getTables().templates.items);
|
for (const rootItemDb of this.itemHelper.getItems().filter((item) => this.isValidFenceItem(item)))
|
||||||
for (const item of dbItems.filter((x) => this.isValidFenceItem(x)))
|
|
||||||
{
|
{
|
||||||
// Skip blacklisted items
|
// Skip blacklisted items
|
||||||
if (this.itemFilterService.isItemBlacklisted(item._id))
|
if (this.itemFilterService.isItemBlacklisted(rootItemDb._id))
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.itemHelper.isValidItem(item._id))
|
// Invalid
|
||||||
|
if (!this.itemHelper.isValidItem(rootItemDb._id))
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Skip items on fence ignore list
|
// Item base type blacklisted
|
||||||
if (this.traderConfig.fence.blacklist.length > 0)
|
if (this.traderConfig.fence.blacklist.length > 0)
|
||||||
{
|
{
|
||||||
if (
|
if (this.traderConfig.fence.blacklist.includes(rootItemDb._id)
|
||||||
this.traderConfig.fence.blacklist.includes(item._id)
|
|| this.itemHelper.isOfBaseclasses(rootItemDb._id, this.traderConfig.fence.blacklist)
|
||||||
|| this.itemHelper.isOfBaseclasses(item._id, this.traderConfig.fence.blacklist)
|
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
@ -69,37 +73,200 @@ export class FenceBaseAssortGenerator
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Skip seasonal event items when not in seasonal event
|
// Skip seasonal event items when not in seasonal event
|
||||||
if (this.traderConfig.fence.blacklistSeasonalItems && blockedSeasonalItems.includes(item._id))
|
if (this.traderConfig.fence.blacklistSeasonalItems && blockedSeasonalItems.includes(rootItemDb._id))
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create barter scheme object
|
// Create item object in array
|
||||||
|
const itemWithChildrenToAdd: Item[] = [{
|
||||||
|
_id: this.hashUtil.generate(),
|
||||||
|
_tpl: rootItemDb._id,
|
||||||
|
parentId: "hideout",
|
||||||
|
slotId: "hideout",
|
||||||
|
upd: { StackObjectsCount: 9999999, UnlimitedCount: true },
|
||||||
|
}];
|
||||||
|
|
||||||
|
// 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
|
||||||
|
this.itemHelper.remapRootItemId(itemWithChildrenToAdd);
|
||||||
|
if (itemWithChildrenToAdd.length > 1)
|
||||||
|
{
|
||||||
|
this.itemHelper.reparentItemAndChildren(itemWithChildrenToAdd[0], itemWithChildrenToAdd);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create barter scheme (price)
|
||||||
const barterSchemeToAdd: IBarterScheme = {
|
const barterSchemeToAdd: IBarterScheme = {
|
||||||
count: Math.round(
|
count: Math.round(
|
||||||
this.handbookHelper.getTemplatePrice(item._id) * this.traderConfig.fence.itemPriceMult,
|
this.handbookHelper.getTemplatePrice(rootItemDb._id) * this.traderConfig.fence.itemPriceMult,
|
||||||
),
|
),
|
||||||
_tpl: Money.ROUBLES,
|
_tpl: Money.ROUBLES,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Add barter data to base
|
// Add barter data to base
|
||||||
baseFenceAssort.barter_scheme[item._id] = [[barterSchemeToAdd]];
|
baseFenceAssort.barter_scheme[itemWithChildrenToAdd[0]._id] = [[barterSchemeToAdd]];
|
||||||
|
|
||||||
// Create item object
|
|
||||||
const itemToAdd: Item = {
|
|
||||||
_id: item._id,
|
|
||||||
_tpl: item._id,
|
|
||||||
parentId: "hideout",
|
|
||||||
slotId: "hideout",
|
|
||||||
upd: { StackObjectsCount: 9999999, UnlimitedCount: true },
|
|
||||||
};
|
|
||||||
|
|
||||||
// Add item to base
|
// Add item to base
|
||||||
baseFenceAssort.items.push(itemToAdd);
|
baseFenceAssort.items.push(...itemWithChildrenToAdd);
|
||||||
|
|
||||||
// Add loyalty data to base
|
// Add loyalty data to base
|
||||||
baseFenceAssort.loyal_level_items[item._id] = 1;
|
baseFenceAssort.loyal_level_items[itemWithChildrenToAdd[0]._id] = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add all default presets to base fence assort
|
||||||
|
const defaultPresets = Object.values(this.presetHelper.getDefaultPresets());
|
||||||
|
for (const defaultPreset of defaultPresets)
|
||||||
|
{
|
||||||
|
// Skip presets we've already added
|
||||||
|
if (baseFenceAssort.items.some((item) => item.upd && item.upd.sptPresetId === defaultPreset._id))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Construct preset + mods
|
||||||
|
const presetAndMods: Item[] = this.itemHelper.replaceIDs(
|
||||||
|
null,
|
||||||
|
this.jsonUtil.clone(defaultPreset._items),
|
||||||
|
);
|
||||||
|
|
||||||
|
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: defaultPreset._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];
|
||||||
|
|
||||||
|
// Add constructed preset to assorts
|
||||||
|
baseFenceAssort.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
|
||||||
|
baseFenceAssort.barter_scheme[presetAndMods[0]._id] = [[]];
|
||||||
|
baseFenceAssort.barter_scheme[presetAndMods[0]._id][0][0] = { _tpl: Money.ROUBLES, count: Math.round(rub) };
|
||||||
|
|
||||||
|
baseFenceAssort.loyal_level_items[presetAndMods[0]._id] = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add soft inserts + armor plates to an armor
|
||||||
|
* @param armor Armor item array to add mods into
|
||||||
|
* @param itemDbDetails Armor items db template
|
||||||
|
*/
|
||||||
|
protected addChildrenToArmorModSlots(armor: Item[], itemDbDetails: ITemplateItem): void
|
||||||
|
{
|
||||||
|
// Armor has no mods, make no additions
|
||||||
|
const hasMods = itemDbDetails._props.Slots.length > 0;
|
||||||
|
if (!hasMods)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for and add required soft inserts to armors
|
||||||
|
const requiredSlots = itemDbDetails._props.Slots.filter(slot => slot._required);
|
||||||
|
const hasRequiredSlots = requiredSlots.length > 0;
|
||||||
|
if (hasRequiredSlots)
|
||||||
|
{
|
||||||
|
for (const requiredSlot of requiredSlots)
|
||||||
|
{
|
||||||
|
const modItemDbDetails = this.itemHelper.getItem(requiredSlot._props.filters[0].Plate)[1];
|
||||||
|
const plateTpl = requiredSlot._props.filters[0].Plate; // `Plate` property appears to be the 'default' item for slot
|
||||||
|
if (plateTpl === "")
|
||||||
|
{
|
||||||
|
// Some bsg plate properties are empty, skip mod
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const mod: Item = {
|
||||||
|
_id: this.hashUtil.generate(),
|
||||||
|
_tpl: plateTpl,
|
||||||
|
parentId: armor[0]._id,
|
||||||
|
slotId: requiredSlot._name,
|
||||||
|
upd: {
|
||||||
|
Repairable: {
|
||||||
|
Durability: modItemDbDetails._props.MaxDurability,
|
||||||
|
MaxDurability: modItemDbDetails._props.MaxDurability
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
armor.push(mod);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for and add plate items
|
||||||
|
const plateSlots = itemDbDetails._props.Slots.filter(slot => this.itemHelper.isRemovablePlateSlot(slot._name));
|
||||||
|
if (plateSlots.length > 0)
|
||||||
|
{
|
||||||
|
for (const plateSlot of plateSlots)
|
||||||
|
{
|
||||||
|
const plateTpl = plateSlot._props.filters[0].Plate
|
||||||
|
if (!plateTpl)
|
||||||
|
{
|
||||||
|
// Bsg data lacks a default plate, skip adding mod
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const modItemDbDetails = this.itemHelper.getItem(plateTpl)[1];
|
||||||
|
armor.push({
|
||||||
|
_id: this.hashUtil.generate(),
|
||||||
|
_tpl: plateSlot._props.filters[0].Plate, // `Plate` property appears to be the 'default' item for slot
|
||||||
|
parentId: armor[0]._id,
|
||||||
|
slotId: plateSlot._name,
|
||||||
|
upd: {
|
||||||
|
Repairable: {
|
||||||
|
Durability: modItemDbDetails._props.MaxDurability,
|
||||||
|
MaxDurability: modItemDbDetails._props.MaxDurability
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate and return the price of an item and its child mods
|
||||||
|
* @param itemWithChildren Item + mods to calcualte price of
|
||||||
|
* @returns price
|
||||||
|
*/
|
||||||
|
protected getHandbookItemPriceWithChildren(itemWithChildren: Item[]): number
|
||||||
|
{
|
||||||
|
let price = 0;
|
||||||
|
for (const item of itemWithChildren)
|
||||||
|
{
|
||||||
|
price += this.handbookHelper.getTemplatePrice(item._tpl);
|
||||||
|
}
|
||||||
|
|
||||||
|
return price;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -507,7 +507,8 @@ export class ItemHelper
|
|||||||
|
|
||||||
for (const itemFromAssort of assort)
|
for (const itemFromAssort of assort)
|
||||||
{
|
{
|
||||||
if (itemFromAssort.parentId === itemIdToFind && !list.find((item) => itemFromAssort._id === item._id))
|
if (itemFromAssort.parentId === itemIdToFind
|
||||||
|
&& !list.find((item) => itemFromAssort._id === item._id))
|
||||||
{
|
{
|
||||||
list.push(itemFromAssort);
|
list.push(itemFromAssort);
|
||||||
list = list.concat(this.findAndReturnChildrenByAssort(itemFromAssort._id, assort));
|
list = list.concat(this.findAndReturnChildrenByAssort(itemFromAssort._id, assort));
|
||||||
|
@ -114,7 +114,7 @@ export class FenceService
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Adjust all items contained inside an assort by a multiplier
|
* Adjust all items contained inside an assort by a multiplier
|
||||||
* @param assort Assort that contains items with prices to adjust
|
* @param assort (clone)Assort that contains items with prices to adjust
|
||||||
* @param itemMultipler multipler to use on items
|
* @param itemMultipler multipler to use on items
|
||||||
* @param presetMultiplier preset multipler to use on presets
|
* @param presetMultiplier preset multipler to use on presets
|
||||||
*/
|
*/
|
||||||
@ -325,12 +325,16 @@ export class FenceService
|
|||||||
itemToRemove = this.randomUtil.getArrayValue(assort.items);
|
itemToRemove = this.randomUtil.getArrayValue(assort.items);
|
||||||
}
|
}
|
||||||
|
|
||||||
const indexOfItemToRemove = assort.items.findIndex((x) => x._id === itemToRemove._id);
|
const indexOfItemToRemove = assort.items.findIndex((item) => item._id === itemToRemove._id);
|
||||||
assort.items.splice(indexOfItemToRemove, 1);
|
assort.items.splice(indexOfItemToRemove, 1);
|
||||||
|
|
||||||
// Clean up any mods if item removed was a weapon
|
// Clean up any mods if item removed was a weapon
|
||||||
// TODO: also check for mods attached down the item chain
|
const itemWithChildren = this.itemHelper.findAndReturnChildrenAsItems(assort.items, itemToRemove._id);
|
||||||
assort.items = assort.items.filter((x) => x.parentId !== itemToRemove._id);
|
for (const itemToDelete of itemWithChildren)
|
||||||
|
{
|
||||||
|
// Delete item from assort items array
|
||||||
|
assort.items.splice(assort.items.indexOf(itemToDelete), 1);
|
||||||
|
}
|
||||||
|
|
||||||
delete assort.barter_scheme[itemToRemove._id];
|
delete assort.barter_scheme[itemToRemove._id];
|
||||||
delete assort.loyal_level_items[itemToRemove._id];
|
delete assort.loyal_level_items[itemToRemove._id];
|
||||||
@ -402,22 +406,50 @@ export class FenceService
|
|||||||
*/
|
*/
|
||||||
protected createAssorts(assortCount: number, assorts: ITraderAssort, loyaltyLevel: number): void
|
protected createAssorts(assortCount: number, assorts: ITraderAssort, loyaltyLevel: number): void
|
||||||
{
|
{
|
||||||
const fenceAssort = this.databaseServer.getTables().traders[Traders.FENCE].assort;
|
const baseFenceAssort = this.databaseServer.getTables().traders[Traders.FENCE].assort;
|
||||||
const fenceAssortIds = Object.keys(fenceAssort.loyal_level_items);
|
|
||||||
const itemTypeCounts = this.initItemLimitCounter(this.traderConfig.fence.itemTypeLimits);
|
const itemTypeCounts = this.initItemLimitCounter(this.traderConfig.fence.itemTypeLimits);
|
||||||
|
|
||||||
this.addItemAssorts(assortCount, fenceAssortIds, assorts, fenceAssort, itemTypeCounts, loyaltyLevel);
|
this.addItemAssorts(assortCount, assorts, baseFenceAssort, itemTypeCounts, loyaltyLevel);
|
||||||
|
|
||||||
// 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);
|
||||||
const defaultPresets = this.presetHelper.getDefaultPresets();
|
this.addPresetsToAssort(randomisedPresetCount, assorts, baseFenceAssort);
|
||||||
this.addPresets(randomisedPresetCount, defaultPresets, assorts, 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(
|
||||||
assortCount: number,
|
assortCount: number,
|
||||||
fenceAssortIds: string[],
|
|
||||||
assorts: ITraderAssort,
|
assorts: ITraderAssort,
|
||||||
fenceAssort: ITraderAssort,
|
fenceAssort: ITraderAssort,
|
||||||
itemTypeCounts: Record<string, { current: number; max: number; }>,
|
itemTypeCounts: Record<string, { current: number; max: number; }>,
|
||||||
@ -425,89 +457,83 @@ 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");
|
||||||
for (let i = 0; i < assortCount; i++)
|
for (let i = 0; i < assortCount; i++)
|
||||||
{
|
{
|
||||||
const itemTpl = fenceAssortIds[this.randomUtil.getInt(0, fenceAssortIds.length - 1)];
|
const chosenAssortRoot = this.randomUtil.getArrayValue(assortRootItems);
|
||||||
|
if (!chosenAssortRoot)
|
||||||
|
{
|
||||||
|
this.logger.error(this.localisationService.getText("fence-unable_to_find_assort_by_id", chosenAssortRoot._id));
|
||||||
|
|
||||||
const price = this.handbookHelper.getTemplatePrice(itemTpl);
|
continue;
|
||||||
const itemIsPreset = this.presetHelper.isPreset(itemTpl);
|
}
|
||||||
|
const desiredAssortItemAndChildrenClone = this.jsonUtil.clone(this.itemHelper.findAndReturnChildrenAsItems(fenceAssort.items, chosenAssortRoot._id));
|
||||||
|
|
||||||
|
const itemDbDetails = this.itemHelper.getItem(chosenAssortRoot._tpl)[1];
|
||||||
|
const itemLimitCount = itemTypeCounts[itemDbDetails._parent];
|
||||||
|
if (itemLimitCount && itemLimitCount.current > itemLimitCount.max)
|
||||||
|
{
|
||||||
|
// Skip adding item as assort as limit reached, decrement i counter so we still get another item
|
||||||
|
i--;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const itemIsPreset = this.presetHelper.isPreset(chosenAssortRoot._id);
|
||||||
|
|
||||||
|
const price = fenceAssort.barter_scheme[chosenAssortRoot._id][0][0].count;
|
||||||
if (price === 0 || (price === 1 && !itemIsPreset) || price === 100)
|
if (price === 0 || (price === 1 && !itemIsPreset) || price === 100)
|
||||||
{
|
{
|
||||||
// Don't allow "special" items
|
// Don't allow "special" items
|
||||||
i--;
|
i--;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// It's a normal non-preset item
|
if (price > priceLimits[itemDbDetails._parent])
|
||||||
if (!itemIsPreset)
|
|
||||||
{
|
{
|
||||||
const desiredAssort = fenceAssort.items[fenceAssort.items.findIndex((i) => i._id === itemTpl)];
|
i--;
|
||||||
if (!desiredAssort)
|
continue;
|
||||||
{
|
|
||||||
this.logger.error(this.localisationService.getText("fence-unable_to_find_assort_by_id", itemTpl));
|
|
||||||
}
|
|
||||||
|
|
||||||
const itemDbDetails = this.itemHelper.getItem(desiredAssort._tpl)[1];
|
|
||||||
const itemLimitCount = itemTypeCounts[itemDbDetails._parent];
|
|
||||||
|
|
||||||
if (itemLimitCount && itemLimitCount.current > itemLimitCount.max)
|
|
||||||
{
|
|
||||||
// Skip adding item as assort as limit reached, decrement i counter so we still get another item
|
|
||||||
i--;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (price > priceLimits[itemDbDetails._parent])
|
|
||||||
{
|
|
||||||
i--;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Increment count as item is being added
|
|
||||||
if (itemLimitCount)
|
|
||||||
{
|
|
||||||
itemLimitCount.current++;
|
|
||||||
}
|
|
||||||
|
|
||||||
const itemsToPush: Item[] = [];
|
|
||||||
const rootItemToPush = this.jsonUtil.clone(desiredAssort);
|
|
||||||
this.randomiseItemUpdProperties(itemDbDetails, rootItemToPush);
|
|
||||||
itemsToPush.push(rootItemToPush);
|
|
||||||
|
|
||||||
rootItemToPush._id = this.hashUtil.generate();
|
|
||||||
rootItemToPush.upd.StackObjectsCount = this.getSingleItemStackCount(itemDbDetails);
|
|
||||||
rootItemToPush.upd.BuyRestrictionCurrent = 0;
|
|
||||||
rootItemToPush.upd.UnlimitedCount = false;
|
|
||||||
|
|
||||||
// Need to add mods to armors so they dont show as red in the trade screen
|
|
||||||
if (this.itemHelper.itemRequiresSoftInserts(rootItemToPush._tpl))
|
|
||||||
{
|
|
||||||
this.addModsToArmorModSlots(itemsToPush, itemDbDetails);
|
|
||||||
}
|
|
||||||
|
|
||||||
assorts.items.push(...itemsToPush);
|
|
||||||
assorts.barter_scheme[rootItemToPush._id] = fenceAssort.barter_scheme[itemTpl];
|
|
||||||
assorts.loyal_level_items[rootItemToPush._id] = loyaltyLevel;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Increment count as item is being added
|
||||||
|
if (itemLimitCount)
|
||||||
|
{
|
||||||
|
itemLimitCount.current++;
|
||||||
|
}
|
||||||
|
|
||||||
|
const rootItemBeingAdded = desiredAssortItemAndChildrenClone[0];
|
||||||
|
this.randomiseItemUpdProperties(itemDbDetails, rootItemBeingAdded);
|
||||||
|
|
||||||
|
rootItemBeingAdded.upd.StackObjectsCount = this.getSingleItemStackCount(itemDbDetails);
|
||||||
|
rootItemBeingAdded.upd.BuyRestrictionCurrent = 0;
|
||||||
|
rootItemBeingAdded.upd.UnlimitedCount = false;
|
||||||
|
|
||||||
|
// Need to add mods to armors so they dont show as red in the trade screen
|
||||||
|
if (this.itemHelper.itemRequiresSoftInserts(rootItemBeingAdded._tpl))
|
||||||
|
{
|
||||||
|
this.randomiseArmorModDurability(desiredAssortItemAndChildrenClone, itemDbDetails);
|
||||||
|
}
|
||||||
|
|
||||||
|
assorts.items.push(...desiredAssortItemAndChildrenClone);
|
||||||
|
assorts.barter_scheme[rootItemBeingAdded._id] = fenceAssort.barter_scheme[chosenAssortRoot._id];
|
||||||
|
assorts.loyal_level_items[rootItemBeingAdded._id] = loyaltyLevel;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add soft inserts + armor plates to an armor
|
* Adjust plate / soft insert durability values
|
||||||
* @param armor Armor item array to add mods into
|
* @param armor Armor item array to add mods into
|
||||||
* @param itemDbDetails Armor items db template
|
* @param itemDbDetails Armor items db template
|
||||||
*/
|
*/
|
||||||
protected addModsToArmorModSlots(armor: Item[], itemDbDetails: ITemplateItem): void
|
protected randomiseArmorModDurability(armor: Item[], itemDbDetails: ITemplateItem): void
|
||||||
{
|
{
|
||||||
// Armor has no mods, make no additions
|
// Armor has no mods, make no changes
|
||||||
const hasMods = itemDbDetails._props.Slots.length > 0;
|
const hasMods = itemDbDetails._props.Slots.length > 0;
|
||||||
if (!hasMods)
|
if (!hasMods)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for and add required soft inserts to armors
|
// Check for and adjust soft insert durability values
|
||||||
const requiredSlots = itemDbDetails._props.Slots.filter(slot => slot._required);
|
const requiredSlots = itemDbDetails._props.Slots.filter(slot => slot._required);
|
||||||
const hasRequiredSlots = requiredSlots.length > 0;
|
const hasRequiredSlots = requiredSlots.length > 0;
|
||||||
if (hasRequiredSlots)
|
if (hasRequiredSlots)
|
||||||
@ -515,7 +541,9 @@ export class FenceService
|
|||||||
for (const requiredSlot of requiredSlots)
|
for (const requiredSlot of requiredSlots)
|
||||||
{
|
{
|
||||||
const modItemDbDetails = this.itemHelper.getItem(requiredSlot._props.filters[0].Plate)[1];
|
const modItemDbDetails = this.itemHelper.getItem(requiredSlot._props.filters[0].Plate)[1];
|
||||||
const durabilityValues = this.getRandomisedArmorDurabilityValues(modItemDbDetails, this.traderConfig.fence.armorMaxDurabilityPercentMinMax);
|
const durabilityValues = this.getRandomisedArmorDurabilityValues(
|
||||||
|
modItemDbDetails,
|
||||||
|
this.traderConfig.fence.armorMaxDurabilityPercentMinMax);
|
||||||
const plateTpl = requiredSlot._props.filters[0].Plate; // `Plate` property appears to be the 'default' item for slot
|
const plateTpl = requiredSlot._props.filters[0].Plate; // `Plate` property appears to be the 'default' item for slot
|
||||||
if (plateTpl === "")
|
if (plateTpl === "")
|
||||||
{
|
{
|
||||||
@ -523,34 +551,36 @@ export class FenceService
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const mod: Item = {
|
// Find items mod to apply dura changes to
|
||||||
_id: this.hashUtil.generate(),
|
const modItemToAdjust = armor.find(mod => mod.slotId === requiredSlot._name);
|
||||||
_tpl: plateTpl,
|
if (!modItemToAdjust.upd)
|
||||||
parentId: armor[0]._id,
|
{
|
||||||
slotId: requiredSlot._name,
|
modItemToAdjust.upd = {}
|
||||||
upd: {
|
}
|
||||||
Repairable: {
|
|
||||||
Durability: durabilityValues.Durability,
|
if (!modItemToAdjust.upd.Repairable)
|
||||||
MaxDurability: durabilityValues.MaxDurability
|
{
|
||||||
}
|
modItemToAdjust.upd.Repairable = {
|
||||||
}
|
Durability: modItemDbDetails._props.MaxDurability,
|
||||||
};
|
MaxDurability: modItemDbDetails._props.MaxDurability
|
||||||
|
};
|
||||||
|
}
|
||||||
|
modItemToAdjust.upd.Repairable.Durability = durabilityValues.Durability;
|
||||||
|
modItemToAdjust.upd.Repairable.MaxDurability = durabilityValues.MaxDurability;
|
||||||
|
|
||||||
// 25% chance to add shots to visor when its below max durability
|
// 25% chance to add shots to visor when its below max durability
|
||||||
if (this.randomUtil.getChance100(25)
|
if (this.randomUtil.getChance100(25)
|
||||||
&& mod.parentId === BaseClasses.ARMORED_EQUIPMENT && mod.slotId === "mod_equipment_000"
|
&& modItemToAdjust.parentId === BaseClasses.ARMORED_EQUIPMENT && modItemToAdjust.slotId === "mod_equipment_000"
|
||||||
&& mod.upd.Repairable.Durability < modItemDbDetails._props.MaxDurability)
|
&& modItemToAdjust.upd.Repairable.Durability < modItemDbDetails._props.MaxDurability) // Is damaged
|
||||||
{
|
{
|
||||||
mod.upd.FaceShield = {
|
modItemToAdjust.upd.FaceShield = {
|
||||||
Hits: this.randomUtil.getInt(1,3)
|
Hits: this.randomUtil.getInt(1,3)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
armor.push(mod);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for and add plate items
|
// Check for and adjust plate durability values
|
||||||
const plateSlots = itemDbDetails._props.Slots.filter(slot => this.itemHelper.isRemovablePlateSlot(slot._name));
|
const plateSlots = itemDbDetails._props.Slots.filter(slot => this.itemHelper.isRemovablePlateSlot(slot._name));
|
||||||
if (plateSlots.length > 0)
|
if (plateSlots.length > 0)
|
||||||
{
|
{
|
||||||
@ -569,19 +599,27 @@ export class FenceService
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const modItemDbDetails = this.itemHelper.getItem(plateTpl)[1];
|
const modItemDbDetails = this.itemHelper.getItem(plateTpl)[1];
|
||||||
const durabilityValues = this.getRandomisedArmorDurabilityValues(modItemDbDetails, this.traderConfig.fence.armorMaxDurabilityPercentMinMax);
|
const durabilityValues = this.getRandomisedArmorDurabilityValues(
|
||||||
armor.push({
|
modItemDbDetails,
|
||||||
_id: this.hashUtil.generate(),
|
this.traderConfig.fence.armorMaxDurabilityPercentMinMax);
|
||||||
_tpl: plateSlot._props.filters[0].Plate, // `Plate` property appears to be the 'default' item for slot
|
|
||||||
parentId: armor[0]._id,
|
// Find items mod to apply dura changes to
|
||||||
slotId: plateSlot._name,
|
const modItemToAdjust = armor.find(mod => mod.slotId === plateSlot._name);
|
||||||
upd: {
|
if (!modItemToAdjust.upd)
|
||||||
Repairable: {
|
{
|
||||||
Durability: durabilityValues.Durability,
|
modItemToAdjust.upd = {}
|
||||||
MaxDurability: durabilityValues.MaxDurability
|
}
|
||||||
}
|
|
||||||
}
|
if (!modItemToAdjust.upd.Repairable)
|
||||||
});
|
{
|
||||||
|
modItemToAdjust.upd.Repairable = {
|
||||||
|
Durability: modItemDbDetails._props.MaxDurability,
|
||||||
|
MaxDurability: modItemDbDetails._props.MaxDurability
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
modItemToAdjust.upd.Repairable.Durability = durabilityValues.Durability;
|
||||||
|
modItemToAdjust.upd.Repairable.MaxDurability = durabilityValues.MaxDurability;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -776,10 +814,7 @@ export class FenceService
|
|||||||
|
|
||||||
// Randomise armor durability
|
// Randomise armor durability
|
||||||
if (
|
if (
|
||||||
(itemDetails._parent === BaseClasses.ARMOR
|
(itemDetails._parent === BaseClasses.ARMORED_EQUIPMENT
|
||||||
|| itemDetails._parent === BaseClasses.HEADWEAR
|
|
||||||
|| itemDetails._parent === BaseClasses.VEST
|
|
||||||
|| itemDetails._parent === BaseClasses.ARMORED_EQUIPMENT
|
|
||||||
|| itemDetails._parent === BaseClasses.FACECOVER
|
|| itemDetails._parent === BaseClasses.FACECOVER
|
||||||
|| itemDetails._parent === BaseClasses.ARMOR_PLATE
|
|| itemDetails._parent === BaseClasses.ARMOR_PLATE
|
||||||
) && itemDetails._props.MaxDurability > 0
|
) && itemDetails._props.MaxDurability > 0
|
||||||
|
Loading…
x
Reference in New Issue
Block a user