Refactor equipment mod parameters

This commit is contained in:
Dev 2024-01-07 14:46:25 +00:00
parent 749fc75a25
commit c6135802be
4 changed files with 146 additions and 104 deletions

View File

@ -23,6 +23,7 @@ import { LocalisationService } from "@spt-aki/services/LocalisationService";
import { HashUtil } from "@spt-aki/utils/HashUtil";
import { JsonUtil } from "@spt-aki/utils/JsonUtil";
import { RandomUtil } from "@spt-aki/utils/RandomUtil";
import { IGenerateEquipmentProperties } from "./BotInventoryGenerator";
@injectable()
export class BotEquipmentModGenerator
@ -65,18 +66,16 @@ export class BotEquipmentModGenerator
*/
public generateModsForEquipment(
equipment: Item[],
modPool: Mods,
parentId: string,
parentTemplate: ITemplateItem,
modSpawnChances: ModsChances,
botRole: string,
settings: IGenerateEquipmentProperties,
forceSpawn = false,
): Item[]
{
const compatibleModsPool = modPool[parentTemplate._id];
const compatibleModsPool = settings.modPool[parentTemplate._id];
if (!compatibleModsPool)
{
this.logger.warning(`bot: ${botRole} lacks a mod slot pool for item: ${parentTemplate._id} ${parentTemplate._name}`);
this.logger.warning(`bot: ${settings.botRole} lacks a mod slot pool for item: ${parentTemplate._id} ${parentTemplate._name}`);
}
// Iterate over mod pool and choose mods to add to item
@ -90,13 +89,13 @@ export class BotEquipmentModGenerator
modSlot: modSlot,
parentId: parentTemplate._id,
parentName: parentTemplate._name,
botRole: botRole
botRole: settings.botRole
}),
);
continue;
}
if (!(this.shouldModBeSpawned(itemSlot, modSlot, modSpawnChances) || forceSpawn))
if (!(this.shouldModBeSpawned(itemSlot, modSlot, settings.spawnChances.mods) || forceSpawn))
{
continue;
}
@ -107,12 +106,14 @@ export class BotEquipmentModGenerator
forceSpawn = true;
}
const modPoolToChooseFrom = this.filterPlateModsForSlot(settings.botRole, modSlot, compatibleModsPool[modSlot]);
let modTpl: string;
let found = false;
// Find random mod and check its compatible
const exhaustableModPool = new ExhaustableArray(
compatibleModsPool[modSlot],
modPoolToChooseFrom,
this.randomUtil,
this.jsonUtil,
);
@ -144,24 +145,22 @@ export class BotEquipmentModGenerator
}
const modTemplate = this.itemHelper.getItem(modTpl);
if (!this.isModValidForSlot(modTemplate, itemSlot, modSlot, parentTemplate, botRole))
if (!this.isModValidForSlot(modTemplate, itemSlot, modSlot, parentTemplate, settings.botRole))
{
continue;
}
const modId = this.hashUtil.generate();
equipment.push(this.createModItem(modId, modTpl, parentId, modSlot, modTemplate[1], botRole));
equipment.push(this.createModItem(modId, modTpl, parentId, modSlot, modTemplate[1], settings.botRole));
if (Object.keys(modPool).includes(modTpl))
if (Object.keys(settings.modPool).includes(modTpl))
{
// Call self recursively
this.generateModsForEquipment(
equipment,
modPool,
modId,
modTemplate[1],
modSpawnChances,
botRole,
settings,
forceSpawn,
);
}
@ -170,6 +169,24 @@ export class BotEquipmentModGenerator
return equipment;
}
protected filterPlateModsForSlot(botRole: string, modSlot: string, modPool: string[]): string[]
{
// Not pmc or not a plate slot, return original mod pool array
if (!this.botHelper.isBotPmc(botRole) || !this.slotIsPlate(modSlot))
{
return modPool;
}
const filteredModPool = [];
}
protected slotIsPlate(modSlot: string): boolean
{
return ["front_plate", "back_plate", "side_plate"].includes(modSlot.toLowerCase());
}
/**
* Add mods to a weapon using the provided mod pool
* @param sessionId session id

View File

@ -11,7 +11,7 @@ import { Inventory as PmcInventory } from "@spt-aki/models/eft/common/tables/IBo
import { Chances, Generation, IBotType, Inventory, Mods } from "@spt-aki/models/eft/common/tables/IBotType";
import { ConfigTypes } from "@spt-aki/models/enums/ConfigTypes";
import { EquipmentSlots } from "@spt-aki/models/enums/EquipmentSlots";
import { EquipmentFilterDetails, IBotConfig, RandomisationDetails } from "@spt-aki/models/spt/config/IBotConfig";
import { EquipmentFilterDetails, EquipmentFilters, IBotConfig, RandomisationDetails } from "@spt-aki/models/spt/config/IBotConfig";
import { ILogger } from "@spt-aki/models/spt/utils/ILogger";
import { ConfigServer } from "@spt-aki/servers/ConfigServer";
import { DatabaseServer } from "@spt-aki/servers/DatabaseServer";
@ -159,7 +159,6 @@ export class BotInventoryGenerator
const botEquipConfig = this.botConfig.equipment[this.botGeneratorHelper.getBotEquipmentRole(botRole)];
const randomistionDetails = this.botHelper.getBotRandomizationDetails(botLevel, botEquipConfig);
for (const equipmentSlot in templateInventory.equipment)
{
// Weapons have special generation and will be generated separately; ArmorVest should be generated after TactivalVest
@ -168,63 +167,75 @@ export class BotInventoryGenerator
continue;
}
this.generateEquipment(
equipmentSlot,
templateInventory.equipment[equipmentSlot],
templateInventory.mods,
equipmentChances,
botRole,
botInventory,
randomistionDetails,
);
this.generateEquipment({
equipmentSlot: equipmentSlot,
equipmentPool: templateInventory.equipment[equipmentSlot],
modPool: templateInventory.mods,
spawnChances: equipmentChances,
botRole: botRole,
botLevel: botLevel,
inventory: botInventory,
botEquipmentConfig: botEquipConfig,
randomisationDetails: randomistionDetails
});
}
// Generate below in specific order
this.generateEquipment(
EquipmentSlots.FACE_COVER,
templateInventory.equipment.FaceCover,
templateInventory.mods,
equipmentChances,
botRole,
botInventory,
randomistionDetails,
);
this.generateEquipment(
EquipmentSlots.HEADWEAR,
templateInventory.equipment.Headwear,
templateInventory.mods,
equipmentChances,
botRole,
botInventory,
randomistionDetails,
);
this.generateEquipment(
EquipmentSlots.EARPIECE,
templateInventory.equipment.Earpiece,
templateInventory.mods,
equipmentChances,
botRole,
botInventory,
randomistionDetails,
);
this.generateEquipment(
EquipmentSlots.TACTICAL_VEST,
templateInventory.equipment.TacticalVest,
templateInventory.mods,
equipmentChances,
botRole,
botInventory,
randomistionDetails,
);
this.generateEquipment(
EquipmentSlots.ARMOR_VEST,
templateInventory.equipment.ArmorVest,
templateInventory.mods,
equipmentChances,
botRole,
botInventory,
randomistionDetails,
);
this.generateEquipment({
equipmentSlot: EquipmentSlots.FACE_COVER,
equipmentPool: templateInventory.equipment.FaceCover,
modPool: templateInventory.mods,
spawnChances: equipmentChances,
botRole: botRole,
botLevel: botLevel,
inventory: botInventory,
botEquipmentConfig: botEquipConfig,
randomisationDetails: randomistionDetails
});
this.generateEquipment({
equipmentSlot: EquipmentSlots.HEADWEAR,
equipmentPool: templateInventory.equipment.Headwear,
modPool: templateInventory.mods,
spawnChances: equipmentChances,
botRole: botRole,
botLevel: botLevel,
inventory: botInventory,
botEquipmentConfig: botEquipConfig,
randomisationDetails: randomistionDetails
});
this.generateEquipment({
equipmentSlot: EquipmentSlots.EARPIECE,
equipmentPool: templateInventory.equipment.Earpiece,
modPool: templateInventory.mods,
spawnChances: equipmentChances,
botRole: botRole,
botLevel: botLevel,
inventory: botInventory,
botEquipmentConfig: botEquipConfig,
randomisationDetails: randomistionDetails
});
this.generateEquipment({
equipmentSlot: EquipmentSlots.TACTICAL_VEST,
equipmentPool: templateInventory.equipment.TacticalVest,
modPool: templateInventory.mods,
spawnChances: equipmentChances,
botRole: botRole,
botLevel: botLevel,
inventory: botInventory,
botEquipmentConfig: botEquipConfig,
randomisationDetails: randomistionDetails
});
this.generateEquipment({
equipmentSlot: EquipmentSlots.ARMOR_VEST,
equipmentPool: templateInventory.equipment.ArmorVest,
modPool: templateInventory.mods,
spawnChances: equipmentChances,
botRole: botRole,
botLevel: botLevel,
inventory: botInventory,
botEquipmentConfig: botEquipConfig,
randomisationDetails: randomistionDetails
});
}
/**
@ -237,49 +248,41 @@ export class BotInventoryGenerator
* @param inventory Inventory to add item into
* @param randomisationDetails settings from bot.json to adjust how item is generated
*/
protected generateEquipment(
equipmentSlot: string,
equipmentPool: Record<string, number>,
modPool: Mods,
spawnChances: Chances,
botRole: string,
inventory: PmcInventory,
randomisationDetails: RandomisationDetails,
): void
protected generateEquipment(settings: IGenerateEquipmentProperties): void
{
const spawnChance =
([EquipmentSlots.POCKETS, EquipmentSlots.SECURED_CONTAINER] as string[]).includes(equipmentSlot)
([EquipmentSlots.POCKETS, EquipmentSlots.SECURED_CONTAINER] as string[]).includes(settings.equipmentSlot)
? 100
: spawnChances.equipment[equipmentSlot];
: settings.spawnChances.equipment[settings.equipmentSlot];
if (typeof spawnChance === "undefined")
{
this.logger.warning(
this.localisationService.getText("bot-no_spawn_chance_defined_for_equipment_slot", equipmentSlot),
this.localisationService.getText("bot-no_spawn_chance_defined_for_equipment_slot", settings.equipmentSlot),
);
return;
}
const shouldSpawn = this.randomUtil.getChance100(spawnChance);
if (Object.keys(equipmentPool).length && shouldSpawn)
if (Object.keys(settings.equipmentPool).length && shouldSpawn)
{
const id = this.hashUtil.generate();
const equipmentItemTpl = this.weightedRandomHelper.getWeightedValue<string>(equipmentPool);
const equipmentItemTpl = this.weightedRandomHelper.getWeightedValue<string>(settings.equipmentPool);
const itemTemplate = this.itemHelper.getItem(equipmentItemTpl);
if (!itemTemplate[0])
{
this.logger.error(this.localisationService.getText("bot-missing_item_template", equipmentItemTpl));
this.logger.info(`EquipmentSlot -> ${equipmentSlot}`);
this.logger.info(`EquipmentSlot -> ${settings.equipmentSlot}`);
return;
}
if (
this.botGeneratorHelper.isItemIncompatibleWithCurrentItems(
inventory.items,
settings.inventory.items,
equipmentItemTpl,
equipmentSlot,
settings.equipmentSlot,
).incompatible
)
{
@ -290,19 +293,19 @@ export class BotInventoryGenerator
const item = {
_id: id,
_tpl: equipmentItemTpl,
parentId: inventory.equipment,
slotId: equipmentSlot,
...this.botGeneratorHelper.generateExtraPropertiesForItem(itemTemplate[1], botRole),
parentId: settings.inventory.equipment,
slotId: settings.equipmentSlot,
...this.botGeneratorHelper.generateExtraPropertiesForItem(itemTemplate[1], settings.botRole),
};
// use dynamic mod pool if enabled in config
const botEquipmentRole = this.botGeneratorHelper.getBotEquipmentRole(botRole);
const botEquipmentRole = this.botGeneratorHelper.getBotEquipmentRole(settings.botRole);
if (
this.botConfig.equipment[botEquipmentRole]
&& randomisationDetails?.randomisedArmorSlots?.includes(equipmentSlot)
&& settings.randomisationDetails?.randomisedArmorSlots?.includes(settings.equipmentSlot)
)
{
modPool[equipmentItemTpl] = this.getFilteredDynamicModsForItem(
settings.modPool[equipmentItemTpl] = this.getFilteredDynamicModsForItem(
equipmentItemTpl,
this.botConfig.equipment[botEquipmentRole].blacklist,
);
@ -313,17 +316,16 @@ export class BotInventoryGenerator
{
const items = this.botEquipmentModGenerator.generateModsForEquipment(
[item],
modPool,
settings.modPool,
id,
itemTemplate[1],
spawnChances.mods,
botRole,
settings
);
inventory.items.push(...items);
settings.inventory.items.push(...items);
}
else
{
inventory.items.push(item);
settings.inventory.items.push(item);
}
}
}
@ -462,3 +464,16 @@ export class BotInventoryGenerator
);
}
}
export interface IGenerateEquipmentProperties
{
equipmentSlot: string,
equipmentPool: Record<string, number>,
modPool: Mods,
spawnChances: Chances,
botRole: string,
botLevel: number,
inventory: PmcInventory,
botEquipmentConfig: EquipmentFilters,
randomisationDetails: RandomisationDetails
}

View File

@ -116,6 +116,7 @@ export interface EquipmentFilters
weightingAdjustmentsByPlayerLevel?: WeightingAdjustmentDetails[];
/** Should the stock mod be forced to spawn on bot */
forceStock: boolean;
armorPlateWeighting?: IArmorPlateWeights[]
}
export interface ModLimits
@ -156,19 +157,28 @@ export interface WeightingAdjustmentDetails
/** Between what levels do these weight settings apply to */
levelRange: MinMax;
/** Key: ammo type e.g. Caliber556x45NATO, value: item tpl + weight */
ammo?: AdjustmentDetails;
ammo?: IAdjustmentDetails;
/** Key: equipment slot e.g. TacticalVest, value: item tpl + weight */
equipment?: AdjustmentDetails;
equipment?: IAdjustmentDetails;
/** Key: clothing slot e.g. feet, value: item tpl + weight */
clothing?: AdjustmentDetails;
clothing?: IAdjustmentDetails;
}
export interface AdjustmentDetails
export interface IAdjustmentDetails
{
add: Record<string, Record<string, number>>;
edit: Record<string, Record<string, number>>;
}
export interface IArmorPlateWeights
{
levelRange: MinMax;
frontPlateWeights: Record<string, number>;
backPlateWeights: Record<string, number>;
sidePlateWeights: Record<string, number>;
}
export interface IRandomisedResourceDetails
{
food: IRandomisedResourceValues;

View File

@ -12,9 +12,9 @@ import {
import { ConfigTypes } from "@spt-aki/models/enums/ConfigTypes";
import { BotGenerationDetails } from "@spt-aki/models/spt/bots/BotGenerationDetails";
import {
AdjustmentDetails,
EquipmentFilterDetails,
EquipmentFilters,
IAdjustmentDetails,
IBotConfig,
WeightingAdjustmentDetails,
} from "@spt-aki/models/spt/config/IBotConfig";
@ -393,7 +393,7 @@ export class BotEquipmentFilterService
* @param botItemPool Bot item dictionary to adjust
*/
protected adjustWeighting(
weightingAdjustments: AdjustmentDetails,
weightingAdjustments: IAdjustmentDetails,
botItemPool: Record<string, any>,
showEditWarnings = true,
): void