Improve addItemWithChildrenToEquipmentSlot(), Make it return different result when:

There are no containers to add item to
Unknown reason

Improved handling of when container has no space for item

Improve `ExternalInventoryMagGen.process()`, Handle edge case when a weapon with a default internal magazine but weapon uses external magazine

Improve most locations that call `addItemWithChildrenToEquipmentSlot()` to log failure reason
This commit is contained in:
Dev 2023-11-24 16:05:58 +00:00
parent 8c7b5da9ff
commit 3c0e6a34f2
5 changed files with 127 additions and 23 deletions

View File

@ -352,13 +352,21 @@ export class BotLootGenerator
itemsToAdd,
inventoryToAddItemsTo,
);
if (itemAddedResult === ItemAddedResult.NO_SPACE)
if (itemAddedResult !== ItemAddedResult.SUCCESS)
{
if (itemAddedResult === ItemAddedResult.NO_CONTAINERS)
{
// Bot has no container to put item in, exit
this.logger.debug(`Unable to add ${totalItemCount} items to bot as it lacks a container to include them`);
break;
}
fitItemIntoContainerAttempts++;
if (fitItemIntoContainerAttempts >= 4)
{
this.logger.debug(
`Failed to place item ${i} of ${totalItemCount} item into ${botRole} container: ${equipmentSlots}, ${fitItemIntoContainerAttempts} times. No space, skipping`,
`Failed to place item ${i} of ${totalItemCount} item into ${botRole} container: ${equipmentSlots}, ${fitItemIntoContainerAttempts} times. ${ItemAddedResult[itemAddedResult]}, skipping`,
);
break;
@ -436,7 +444,7 @@ export class BotLootGenerator
if (result !== ItemAddedResult.SUCCESS)
{
this.logger.debug(`Failed to add additional weapon ${generatedWeapon.weapon[0]._id} to bot backpack, reason: ${result}`);
this.logger.debug(`Failed to add additional weapon ${generatedWeapon.weapon[0]._id} to bot backpack, reason: ${ItemAddedResult[result]}`);
}
}
}

View File

@ -12,6 +12,7 @@ import { IBotType } from "@spt-aki/models/eft/common/tables/IBotType";
import { Item } from "@spt-aki/models/eft/common/tables/IItem";
import { AccountTypes } from "@spt-aki/models/enums/AccountTypes";
import { ConfigTypes } from "@spt-aki/models/enums/ConfigTypes";
import { ItemAddedResult } from "@spt-aki/models/enums/ItemAddedResult";
import { MemberCategory } from "@spt-aki/models/enums/MemberCategory";
import { Traders } from "@spt-aki/models/enums/Traders";
import { IPlayerScavConfig, KarmaLevel } from "@spt-aki/models/spt/config/IPlayerScavConfig";
@ -125,13 +126,18 @@ export class PlayerScavGenerator
_tpl: labsCard._id,
...this.botGeneratorHelper.generateExtraPropertiesForItem(labsCard),
}];
this.botWeaponGeneratorHelper.addItemWithChildrenToEquipmentSlot(
const result = this.botWeaponGeneratorHelper.addItemWithChildrenToEquipmentSlot(
["TacticalVest", "Pockets", "Backpack"],
itemsToAdd[0]._id,
labsCard._id,
itemsToAdd,
scavData.Inventory,
);
if (result !== ItemAddedResult.SUCCESS)
{
this.logger.debug(`Unable to add keycard to bot. Reason: ${ItemAddedResult[result]}`);
}
}
// Remove secure container

View File

@ -4,10 +4,12 @@ import { IInventoryMagGen } from "@spt-aki/generators/weapongen/IInventoryMagGen
import { InventoryMagGen } from "@spt-aki/generators/weapongen/InventoryMagGen";
import { BotWeaponGeneratorHelper } from "@spt-aki/helpers/BotWeaponGeneratorHelper";
import { ItemHelper } from "@spt-aki/helpers/ItemHelper";
import { ITemplateItem } from "@spt-aki/models/eft/common/tables/ITemplateItem";
import { EquipmentSlots } from "@spt-aki/models/enums/EquipmentSlots";
import { ItemAddedResult } from "@spt-aki/models/enums/ItemAddedResult";
import { ILogger } from "@spt-aki/models/spt/utils/ILogger";
import { LocalisationService } from "@spt-aki/services/LocalisationService";
import { RandomUtil } from "@spt-aki/utils/RandomUtil";
@injectable()
export class ExternalInventoryMagGen implements IInventoryMagGen
@ -17,6 +19,7 @@ export class ExternalInventoryMagGen implements IInventoryMagGen
@inject("ItemHelper") protected itemHelper: ItemHelper,
@inject("LocalisationService") protected localisationService: LocalisationService,
@inject("BotWeaponGeneratorHelper") protected botWeaponGeneratorHelper: BotWeaponGeneratorHelper,
@inject("RandomUtil") protected randomUtil: RandomUtil
)
{}
@ -33,8 +36,15 @@ export class ExternalInventoryMagGen implements IInventoryMagGen
process(inventoryMagGen: InventoryMagGen): void
{
// Cout of attempts to fit a magazine into bot inventory
let fitAttempts = 0;
// Magazine Db template
let magTemplate = inventoryMagGen.getMagazineTemplate();
let magazineTpl = magTemplate._id;
const weapon = inventoryMagGen.getWeaponTemplate();
const attemptedMagBlacklist: string[] = [];
const defaultMagazineTpl = this.botWeaponGeneratorHelper.getWeaponsDefaultMagazineTpl(weapon);
const randomizedMagazineCount = Number(
this.botWeaponGeneratorHelper.getRandomizedMagazineCount(inventoryMagGen.getMagCount()),
);
@ -46,7 +56,7 @@ export class ExternalInventoryMagGen implements IInventoryMagGen
magTemplate,
);
const ableToFitMagazinesIntoBotInventory = this.botWeaponGeneratorHelper.addItemWithChildrenToEquipmentSlot(
const fitsIntoInventory = this.botWeaponGeneratorHelper.addItemWithChildrenToEquipmentSlot(
[EquipmentSlots.TACTICAL_VEST, EquipmentSlots.POCKETS],
magazineWithAmmo[0]._id,
magazineTpl,
@ -54,43 +64,104 @@ export class ExternalInventoryMagGen implements IInventoryMagGen
inventoryMagGen.getPmcInventory(),
);
if (ableToFitMagazinesIntoBotInventory === ItemAddedResult.NO_SPACE && i < randomizedMagazineCount)
if (fitsIntoInventory === ItemAddedResult.NO_CONTAINERS)
{
// No containers to fit magazines, stop trying
break;
}
// No space for magazine and we haven't reached desired magazine count
else if (fitsIntoInventory === ItemAddedResult.NO_SPACE && i < randomizedMagazineCount)
{
// Prevent infinite loop by only allowing 5 attempts at fitting a magazine into inventory
if (fitAttempts > 5)
{
this.logger.debug(`Failed ${fitAttempts} times to add magazine ${magazineTpl} to bot inventory, stopping`);
break;
}
/* We were unable to fit at least the minimum amount of magazines,
* so we fallback to default magazine and try again.
* Temporary workaround to Killa spawning with no extras if he spawns with a drum mag */
* Temporary workaround to Killa spawning with no extra mags if he spawns with a drum mag */
if (
magazineTpl
=== this.botWeaponGeneratorHelper.getWeaponsDefaultMagazineTpl(
inventoryMagGen.getWeaponTemplate(),
)
)
if (magazineTpl === defaultMagazineTpl)
{
// We were already on default - stop here to prevent infinite looping
break;
}
// Get default magazine tpl, reset loop counter by 1 and try again
magazineTpl = this.botWeaponGeneratorHelper.getWeaponsDefaultMagazineTpl(
inventoryMagGen.getWeaponTemplate(),
);
// Add failed magazine tpl to blacklist
attemptedMagBlacklist.push(magazineTpl);
// Set chosen magazine tpl to the weapons default magazine tpl and try to fit into inventory next loop
magazineTpl = defaultMagazineTpl;
magTemplate = this.itemHelper.getItem(magazineTpl)[1];
if (!magTemplate)
{
this.logger.error(
this.localisationService.getText("bot-unable_to_find_default_magazine_item", magazineTpl),
);
break;
}
// Edge case - some weapons (SKS) have an internal magazine as default, choose random non-internal magazine to add to bot instead
if (magTemplate._props.ReloadMagType === "InternalMagazine")
{
break;
const result = this.getRandomExternalMagazineForInternalMagazineGun(inventoryMagGen.getWeaponTemplate()._id, attemptedMagBlacklist);
if (!result?._id)
{
this.logger.debug(`Unable to add additional magazine into bot inventory for weapon: ${weapon._name}, attempted: ${fitAttempts} times`);
break;
}
magazineTpl = result._id;
magTemplate = result;
fitAttempts++;
}
// Reduce loop counter by 1 to ensure we get full cout of desired magazines
i--;
}
if (fitsIntoInventory === ItemAddedResult.SUCCESS)
{
// Reset fit counter now it succeeded
fitAttempts = 0;
}
}
}
/**
* Get a random compatible external magazine for a weapon, excluses internal magazines from possible pool
* @param weaponTpl Weapon to get mag for
* @returns tpl of magazine
*/
protected getRandomExternalMagazineForInternalMagazineGun(weaponTpl: string, magazineBlacklist: string[]): ITemplateItem
{
// The mag Slot data for the weapon
const magSlot = this.itemHelper.getItem(weaponTpl)[1]._props.Slots.find(x => x._name === "mod_magazine");
if (!magSlot)
{
return null;
}
// All possible mags that fit into the weapon excluding blacklisted
const magazinePool = magSlot._props.filters[0].Filter.filter(x => !magazineBlacklist.includes(x)).map((x) => this.itemHelper.getItem(x)[1]);
if (!magazinePool)
{
return null;
}
// Non-internal magazines that fit into the weapon
const externalMagazineOnlyPool = magazinePool.filter(x => x._props.ReloadMagType !== "InternalMagazine");
if (!externalMagazineOnlyPool || externalMagazineOnlyPool?.length === 0)
{
return null;
}
// Randomly chosen external magazine
return this.randomUtil.getArrayValue(externalMagazineOnlyPool);
}
}

View File

@ -134,7 +134,7 @@ export class BotWeaponGeneratorHelper
if (result !== ItemAddedResult.SUCCESS)
{
this.logger.debug(`Unable to add ammo: ${ammoItem._tpl} to bot equipment, ${ItemAddedResult[result]}`);
this.logger.debug(`Unable to add ammo: ${ammoItem._tpl} to bot inventory, ${ItemAddedResult[result]}`);
if (result === ItemAddedResult.NO_SPACE)
{
@ -173,6 +173,7 @@ export class BotWeaponGeneratorHelper
inventory: Inventory,
): ItemAddedResult
{
let missingContainerCount = 0;
for (const slot of equipmentSlots)
{
// Get container to put item into
@ -183,8 +184,15 @@ export class BotWeaponGeneratorHelper
this.logger.debug(
`Unable to add item: ${
itemWithChildren[0]._tpl
} to: ${slot}, slot missing/bot generated without equipment`,
} to: ${slot}, slot missing/bot generated without item in slot`,
);
missingContainerCount++;
if (missingContainerCount === equipmentSlots.length)
{
return ItemAddedResult.NO_CONTAINERS
}
continue;
}
@ -205,6 +213,9 @@ export class BotWeaponGeneratorHelper
// Get x/y grid size of item
const itemSize = this.inventoryHelper.getItemSize(parentTpl, parentId, itemWithChildren);
// Iterate over each grid in the container and look for a big enough space for the item to be placed in
let currentGridCount = 1;
const slotGridCount = containerTemplate[1]._props.Grids.length;
for (const slotGrid of containerTemplate[1]._props.Grids)
{
// Grid is empty, skip
@ -267,11 +278,18 @@ export class BotWeaponGeneratorHelper
return ItemAddedResult.SUCCESS;
}
// If we've checked all grids in container and reached this point, there's no space for item
if (slotGridCount >= currentGridCount)
{
return ItemAddedResult.NO_SPACE;
}
currentGridCount++;
// Start loop again in next grid of container
}
}
return ItemAddedResult.NO_SPACE;
return ItemAddedResult.UNKNOWN;
}
/**

View File

@ -1,5 +1,6 @@
export enum ItemAddedResult
{
export enum ItemAddedResult {
UNKNOWN = -1,
SUCCESS = 1,
NO_SPACE = 2,
NO_CONTAINERS = 3
}