Improve selection of items to add mods for:

quest rewards
bot loot gen
map loot gen
fence assort gen

Updated `removeRandomModsOfItem()` to also skip slots with a _required prop
This commit is contained in:
Dev 2024-01-11 17:42:58 +00:00
parent 6a8e261af1
commit cb18d251ba
6 changed files with 73 additions and 30 deletions

View File

@ -344,7 +344,7 @@ export class BotLootGenerator
this.randomiseAmmoStackSize(isPmc, itemToAddTemplate, itemsToAdd[0]);
}
// Must add soft inserts/plates
else if (this.itemHelper.itemCanRequireArmorInserts(itemToAddTemplate._id))
else if (this.itemHelper.itemRequiresSoftInserts(itemToAddTemplate._id))
{
itemsToAdd = this.itemHelper.addChildSlotItems(itemsToAdd, itemToAddTemplate, null, true);
}

View File

@ -861,7 +861,7 @@ export class LocationGenerator
);
itemWithMods.push(...magazineItem);
}
else if (this.itemHelper.itemCanRequireArmorInserts(chosenTpl))
else if (this.itemHelper.armorItemCanHoldMods(chosenTpl))
{
itemWithMods.push({
_id: this.objectId.generate(),
@ -1071,8 +1071,9 @@ export class LocationGenerator
// Replace existing magazine with above array
items.splice(items.indexOf(items[0]), 1, ...magazineWithCartridges);
}
else if (this.itemHelper.itemCanRequireArmorInserts(chosenTpl))
else if (this.itemHelper.armorItemCanHoldMods(chosenTpl))
{
// We make base item above, at start of function, no need to do it here
if (itemTemplate._props.Slots?.length > 0)
{
items = this.itemHelper.addChildSlotItems(items, itemTemplate, this.locationConfig.equipmentLootSettings.modSpawnChancePercent);

View File

@ -105,11 +105,47 @@ export class ItemHelper
* @param itemTpl item to check
* @returns Does item have the possibility ot need soft inserts
*/
public itemCanRequireArmorInserts(itemTpl: string): boolean
public armorItemCanHoldMods(itemTpl: string): boolean
{
return this.isOfBaseclasses(itemTpl, [BaseClasses.HEADWEAR, BaseClasses.VEST, BaseClasses.ARMOR]);
}
/**
* Does the provided item tpl require soft inserts to become a valid armor item
* @param itemTpl Item tpl to check
* @returns True if it needs armor inserts
*/
public itemRequiresSoftInserts(itemTpl: string): boolean
{
// not a slot that takes soft-inserts
if (!this.armorItemCanHoldMods(itemTpl))
{
return false;
}
// Check is an item
const itemDbDetails = this.getItem(itemTpl);
if (!itemDbDetails[0])
{
return false;
}
// Has no slots
if (!(itemDbDetails[1]._props.Slots ?? []).length)
{
return false;
}
// Check if item has slots that match soft insert name ids
const softInsertSlotIds = ["groin", "soft_armor_back", "soft_armor_front", "soft_armor_left", "soft_armor_right", "shoulder_l", "shoulder_r", "collar"];
if (itemDbDetails[1]._props.Slots.find(slot => softInsertSlotIds.includes(slot._name.toLowerCase())))
{
return true;
}
return false;
}
/**
* Returns the item price based on the handbook or as a fallback from the prices.json if the item is not
* found in the handbook. If the price can't be found at all return 0

View File

@ -267,8 +267,8 @@ export class QuestHelper
const mods: Item[] = [];
const rootItem = questReward.items[0];
// Is armor item that needs inserts / plates
if (questReward.items.length === 1 && this.itemHelper.itemCanRequireArmorInserts(rootItem._tpl))
// Is armor item that may need inserts / plates
if (questReward.items.length === 1 && this.itemHelper.armorItemCanHoldMods(rootItem._tpl))
{
// Attempt to pull default preset from globals and add child items to reward
this.generateArmorRewardChildSlots(rootItem, questReward);

View File

@ -403,7 +403,7 @@ export class FenceService
protected createAssorts(assortCount: number, assorts: ITraderAssort, loyaltyLevel: number): void
{
const fenceAssort = this.databaseServer.getTables().traders[Traders.FENCE].assort;
const defaultWeaponPresets = this.presetHelper.getDefaultPresets();
const defaultPresets = this.presetHelper.getDefaultPresets();
const fenceAssortIds = Object.keys(fenceAssort.loyal_level_items);
const itemTypeCounts = this.initItemLimitCounter(this.traderConfig.fence.itemTypeLimits);
@ -412,7 +412,7 @@ export class FenceService
// Add presets
const maxPresetCount = Math.round(assortCount * (this.traderConfig.fence.maxPresetsPercent / 100));
const randomisedPresetCount = this.randomUtil.getInt(0, maxPresetCount);
this.addPresets(randomisedPresetCount, defaultWeaponPresets, assorts, loyaltyLevel);
this.addPresets(randomisedPresetCount, defaultPresets, assorts, loyaltyLevel);
}
protected addItemAssorts(
@ -481,7 +481,7 @@ export class FenceService
rootItemToPush.upd.UnlimitedCount = false;
// Need to add mods to armors so they dont show as red in the trade screen
if (this.itemHelper.itemCanRequireArmorInserts(rootItemToPush._tpl))
if (this.itemHelper.itemRequiresSoftInserts(rootItemToPush._tpl))
{
this.addModsToArmorModSlots(itemsToPush, itemDbDetails);
}
@ -594,25 +594,25 @@ export class FenceService
}
/**
* Add preset weapons to fence presets
* Add weapon/armor presets to fence
* @param assortCount how many assorts to add to assorts
* @param defaultWeaponPresets a dictionary of default weapon presets
* @param defaultPresets a dictionary of default weapon presets
* @param assorts object to add presets to
* @param loyaltyLevel loyalty level to requre item at
*/
protected addPresets(
desiredPresetCount: number,
defaultWeaponPresets: Record<string, IPreset>,
defaultPresets: Record<string, IPreset>,
assorts: ITraderAssort,
loyaltyLevel: number,
): void
{
let presetCount = 0;
const presetKeys = Object.keys(defaultWeaponPresets);
const presetKeys = Object.keys(defaultPresets);
for (let index = 0; index < desiredPresetCount; index++)
{
const presetId = presetKeys[this.randomUtil.getInt(0, presetKeys.length - 1)];
const preset = defaultWeaponPresets[presetId];
const preset = defaultPresets[presetId];
// Check we're under preset limit
if (presetCount > desiredPresetCount)
@ -626,12 +626,12 @@ export class FenceService
continue;
}
// Construct weapon + mods
// Construct preset + mods
const weaponAndMods: Item[] = this.itemHelper.replaceIDs(
null,
this.jsonUtil.clone(defaultWeaponPresets[preset._id]._items),
this.jsonUtil.clone(defaultPresets[preset._id]._items),
);
this.removeRandomPartsOfWeapon(weaponAndMods);
this.removeRandomModsOfItem(weaponAndMods);
for (let i = 0; i < weaponAndMods.length; i++)
{
const mod = weaponAndMods[i];
@ -676,42 +676,48 @@ export class FenceService
/**
* Remove parts of a weapon prior to being listed on flea
* @param weaponAndMods Weapon to remove parts from
* @param itemAndMods Weapon to remove parts from
*/
protected removeRandomPartsOfWeapon(weaponAndMods: Item[]): void
protected removeRandomModsOfItem(itemAndMods: Item[]): void
{
// Items to be removed from inventory
const toDelete: string[] = [];
// Loop over insurance items, find items to delete from player inventory
for (const weaponMod of weaponAndMods)
// Find mods to remove from item that could've been scavenged by other players in-raid
for (const itemMod of itemAndMods)
{
if (this.presetModItemWillBeRemoved(weaponMod, toDelete))
if (this.presetModItemWillBeRemoved(itemMod, toDelete))
{
// Skip if not an item
const itemDbDetails = this.itemHelper.getItem(weaponMod._tpl);
const itemDbDetails = this.itemHelper.getItem(itemMod._tpl);
if (!itemDbDetails[0])
{
continue;
}
// Is a mod and can't be edited in-raid
if (weaponMod.slotId !== "hideout" && !itemDbDetails[1]._props.RaidModdable)
if (itemMod.slotId !== "hideout" && !itemDbDetails[1]._props.RaidModdable)
{
continue;
}
const slotDetails = itemDbDetails[1]._props.Slots.find(slot => slot._name === itemMod.slotId);
if (slotDetails?._required)
{
continue;
}
// Remove item and its sub-items to prevent orphans
toDelete.push(...this.itemHelper.findAndReturnChildrenByItems(weaponAndMods, weaponMod._id));
toDelete.push(...this.itemHelper.findAndReturnChildrenByItems(itemAndMods, itemMod._id));
}
}
// Reverse loop and remove items
for (let index = weaponAndMods.length - 1; index >= 0; --index)
for (let index = itemAndMods.length - 1; index >= 0; --index)
{
if (toDelete.includes(weaponAndMods[index]._id))
if (toDelete.includes(itemAndMods[index]._id))
{
weaponAndMods.splice(index, 1);
itemAndMods.splice(index, 1);
}
}
}

View File

@ -302,7 +302,7 @@ export class InsuranceService
if (itemClientInsuranceData || itemIsSoftInsert)
{
// Get baseline item to return, clone pre raid item
// Get baseline item to return, clone pre-raid item
const itemToReturn: Item = this.jsonUtil.clone(preRaidItem);
// Add upd if it doesnt exist
@ -311,7 +311,7 @@ export class InsuranceService
itemToReturn.upd = {};
}
// Check for slotid values that need to be updated and adjust
// Check for slotId values that need to be updated and adjust
this.updateSlotIdValue(pmcData.Inventory.equipment, itemToReturn);
// Remove location property