Add plate filtering system based on bot level
This commit is contained in:
parent
0fcc411123
commit
db13df89d9
@ -1376,62 +1376,164 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"armorPlateWeighting" : [
|
||||
{
|
||||
"levelRange": {
|
||||
"armorPlateWeighting": [{
|
||||
"levelRange": {
|
||||
"min": 1,
|
||||
"max": 10
|
||||
},
|
||||
"frontPlateWeights": {
|
||||
"level2": 25,
|
||||
"level3": 20,
|
||||
"level4": 5,
|
||||
"level5": 1,
|
||||
"level6": 1
|
||||
},
|
||||
"backPlateWeights": {
|
||||
"level2": 25,
|
||||
"level3": 20,
|
||||
"level4": 5,
|
||||
"level5": 1,
|
||||
"level6": 1
|
||||
},
|
||||
"sidePlateWeights": {
|
||||
"level2": 25,
|
||||
"level3": 20,
|
||||
"level4": 5,
|
||||
"level5": 1,
|
||||
"level6": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"levelRange": {
|
||||
"front_plate": {
|
||||
"2": 25,
|
||||
"3": 20,
|
||||
"4": 5,
|
||||
"5": 1,
|
||||
"6": 1
|
||||
},
|
||||
"back_plate": {
|
||||
"2": 25,
|
||||
"3": 20,
|
||||
"4": 5,
|
||||
"5": 1,
|
||||
"6": 1
|
||||
},
|
||||
"side_plate": {
|
||||
"2": 25,
|
||||
"3": 20,
|
||||
"4": 5,
|
||||
"5": 1,
|
||||
"6": 1
|
||||
}
|
||||
}, {
|
||||
"levelRange": {
|
||||
"min": 11,
|
||||
"max": 14
|
||||
},
|
||||
"frontPlateWeights": {
|
||||
"level2": 30,
|
||||
"level3": 15,
|
||||
"level4": 5,
|
||||
"level5": 1,
|
||||
"level6": 1
|
||||
},
|
||||
"backPlateWeights": {
|
||||
"level2": 30,
|
||||
"level3": 15,
|
||||
"level4": 5,
|
||||
"level5": 1,
|
||||
"level6": 1
|
||||
},
|
||||
"sidePlateWeights": {
|
||||
"level2": 30,
|
||||
"level3": 15,
|
||||
"level4": 5,
|
||||
"level5": 1,
|
||||
"level6": 1
|
||||
}
|
||||
}
|
||||
],
|
||||
"front_plate": {
|
||||
"2": 30,
|
||||
"3": 15,
|
||||
"4": 5,
|
||||
"5": 1,
|
||||
"6": 1
|
||||
},
|
||||
"back_plate": {
|
||||
"2": 30,
|
||||
"3": 15,
|
||||
"4": 5,
|
||||
"5": 1,
|
||||
"6": 1
|
||||
},
|
||||
"side_plate": {
|
||||
"2": 30,
|
||||
"3": 15,
|
||||
"4": 5,
|
||||
"5": 1,
|
||||
"6": 1
|
||||
}
|
||||
}, {
|
||||
"levelRange": {
|
||||
"min": 15,
|
||||
"max": 24
|
||||
},
|
||||
"front_plate": {
|
||||
"2": 10,
|
||||
"3": 15,
|
||||
"4": 20,
|
||||
"5": 8,
|
||||
"6": 4
|
||||
},
|
||||
"back_plate": {
|
||||
"2": 10,
|
||||
"3": 15,
|
||||
"4": 20,
|
||||
"5": 8,
|
||||
"6": 4
|
||||
},
|
||||
"side_plate": {
|
||||
"2": 10,
|
||||
"3": 15,
|
||||
"4": 20,
|
||||
"5": 8,
|
||||
"6": 4
|
||||
}
|
||||
}, {
|
||||
"levelRange": {
|
||||
"min": 25,
|
||||
"max": 35
|
||||
},
|
||||
"front_plate": {
|
||||
"2": 3,
|
||||
"3": 15,
|
||||
"4": 30,
|
||||
"5": 20,
|
||||
"6": 10
|
||||
},
|
||||
"back_plate": {
|
||||
"2": 3,
|
||||
"3": 15,
|
||||
"4": 30,
|
||||
"5": 20,
|
||||
"6": 10
|
||||
},
|
||||
"side_plate": {
|
||||
"2": 3,
|
||||
"3": 15,
|
||||
"4": 30,
|
||||
"5": 20,
|
||||
"6": 10
|
||||
}
|
||||
}, {
|
||||
"levelRange": {
|
||||
"min": 36,
|
||||
"max": 55
|
||||
},
|
||||
"front_plate": {
|
||||
"2": 1,
|
||||
"3": 10,
|
||||
"4": 30,
|
||||
"5": 40,
|
||||
"6": 20
|
||||
},
|
||||
"back_plate": {
|
||||
"2": 1,
|
||||
"3": 10,
|
||||
"4": 30,
|
||||
"5": 40,
|
||||
"6": 20
|
||||
},
|
||||
"side_plate": {
|
||||
"2": 1,
|
||||
"3": 10,
|
||||
"4": 30,
|
||||
"5": 40,
|
||||
"6": 20
|
||||
}
|
||||
}, {
|
||||
"levelRange": {
|
||||
"min": 56,
|
||||
"max": 90
|
||||
},
|
||||
"front_plate": {
|
||||
"2": 0,
|
||||
"3": 0,
|
||||
"4": 10,
|
||||
"5": 40,
|
||||
"6": 50
|
||||
},
|
||||
"back_plate": {
|
||||
"2": 0,
|
||||
"3": 0,
|
||||
"4": 10,
|
||||
"5": 40,
|
||||
"6": 50
|
||||
},
|
||||
"side_plate": {
|
||||
"2": 0,
|
||||
"3": 0,
|
||||
"4": 10,
|
||||
"5": 40,
|
||||
"6": 50
|
||||
}
|
||||
}
|
||||
],
|
||||
"whitelist": [{
|
||||
"levelRange": {
|
||||
"min": 101,
|
||||
|
@ -6,6 +6,7 @@ import { BotWeaponGeneratorHelper } from "@spt-aki/helpers/BotWeaponGeneratorHel
|
||||
import { ItemHelper } from "@spt-aki/helpers/ItemHelper";
|
||||
import { ProbabilityHelper } from "@spt-aki/helpers/ProbabilityHelper";
|
||||
import { ProfileHelper } from "@spt-aki/helpers/ProfileHelper";
|
||||
import { WeightedRandomHelper } from "@spt-aki/helpers/WeightedRandomHelper";
|
||||
import { Mods, ModsChances } from "@spt-aki/models/eft/common/tables/IBotType";
|
||||
import { Item } from "@spt-aki/models/eft/common/tables/IItem";
|
||||
import { ITemplateItem, Slot } from "@spt-aki/models/eft/common/tables/ITemplateItem";
|
||||
@ -45,6 +46,7 @@ export class BotEquipmentModGenerator
|
||||
@inject("BotHelper") protected botHelper: BotHelper,
|
||||
@inject("BotGeneratorHelper") protected botGeneratorHelper: BotGeneratorHelper,
|
||||
@inject("BotWeaponGeneratorHelper") protected botWeaponGeneratorHelper: BotWeaponGeneratorHelper,
|
||||
@inject("WeightedRandomHelper") protected weightedRandomHelper: WeightedRandomHelper,
|
||||
@inject("LocalisationService") protected localisationService: LocalisationService,
|
||||
@inject("BotEquipmentModPoolService") protected botEquipmentModPoolService: BotEquipmentModPoolService,
|
||||
@inject("ConfigServer") protected configServer: ConfigServer,
|
||||
@ -59,8 +61,6 @@ export class BotEquipmentModGenerator
|
||||
* @param modPool Mod list to choose frm
|
||||
* @param parentId parentid of item to add mod to
|
||||
* @param parentTemplate template objet of item to add mods to
|
||||
* @param modSpawnChances dictionary of mod items and their chance to spawn for this bot type
|
||||
* @param botRole the bot role being generated for
|
||||
* @param forceSpawn should this mod be forced to spawn
|
||||
* @returns Item + compatible mods as an array
|
||||
*/
|
||||
@ -106,7 +106,7 @@ export class BotEquipmentModGenerator
|
||||
forceSpawn = true;
|
||||
}
|
||||
|
||||
const modPoolToChooseFrom = this.filterPlateModsForSlot(settings.botRole, modSlot, compatibleModsPool[modSlot]);
|
||||
const modPoolToChooseFrom = this.filterPlateModsForSlot(settings, modSlot.toLowerCase(), compatibleModsPool[modSlot], parentTemplate);
|
||||
|
||||
let modTpl: string;
|
||||
let found = false;
|
||||
@ -169,16 +169,58 @@ export class BotEquipmentModGenerator
|
||||
return equipment;
|
||||
}
|
||||
|
||||
protected filterPlateModsForSlot(botRole: string, modSlot: string, modPool: string[]): string[]
|
||||
/**
|
||||
* Filter a bots plate pool based on its current level
|
||||
* @param settings Bot equipment generation settings
|
||||
* @param modSlot Slot being filtered
|
||||
* @param modPool Plates tpls to choose from
|
||||
* @param armorItem
|
||||
* @returns Array of plate tpls to choose from
|
||||
*/
|
||||
protected filterPlateModsForSlot(settings: IGenerateEquipmentProperties, modSlot: string, modPool: string[], armorItem: ITemplateItem): string[]
|
||||
{
|
||||
// Not pmc or not a plate slot, return original mod pool array
|
||||
if (!this.botHelper.isBotPmc(botRole) || !this.slotIsPlate(modSlot))
|
||||
if (!this.slotIsPlate(modSlot))
|
||||
{
|
||||
return modPool;
|
||||
}
|
||||
|
||||
const filteredModPool = [];
|
||||
// Get the front/back/side weights based on bots level
|
||||
const plateSlotWeights = settings
|
||||
.botEquipmentConfig
|
||||
?.armorPlateWeighting
|
||||
?.find(x => settings.botLevel >= x.levelRange.min && settings.botLevel <= x.levelRange.max);
|
||||
if (!plateSlotWeights)
|
||||
{
|
||||
return modPool;
|
||||
}
|
||||
|
||||
// Get the specific plate slot weights (front/back/side)
|
||||
const plateWeights: Record<string, number> = plateSlotWeights[modSlot];
|
||||
if (!plateWeights)
|
||||
{
|
||||
this.logger.warning(`Plate slot ${modSlot} lacks weight data, skipping`);
|
||||
|
||||
return modPool;
|
||||
}
|
||||
|
||||
// Choose a plate level based on weighting
|
||||
const chosenArmorPlateLevel = this.weightedRandomHelper.getWeightedValue<string>(plateWeights);
|
||||
|
||||
// Convert the array of ids into database items
|
||||
const platesFromDb = modPool.map(x => this.itemHelper.getItem(x)[1]);
|
||||
|
||||
// Filter plates to the chosen level based on its armorClass property
|
||||
const filteredPlates = platesFromDb.filter(x => x._props.armorClass === chosenArmorPlateLevel);
|
||||
if (!filteredPlates)
|
||||
{
|
||||
this.logger.warning(`Plate filter was too restrictive, unable to find plates of level: ${chosenArmorPlateLevel}. Returning all ${modPool.length} plates instead`);
|
||||
|
||||
return modPool;
|
||||
}
|
||||
|
||||
// Only return the items ids
|
||||
return filteredPlates.map(x => x._id);
|
||||
|
||||
}
|
||||
|
||||
|
@ -168,8 +168,8 @@ export class BotInventoryGenerator
|
||||
}
|
||||
|
||||
this.generateEquipment({
|
||||
equipmentSlot: equipmentSlot,
|
||||
equipmentPool: templateInventory.equipment[equipmentSlot],
|
||||
rootEquipmentSlot: equipmentSlot,
|
||||
rootEquipmentPool: templateInventory.equipment[equipmentSlot],
|
||||
modPool: templateInventory.mods,
|
||||
spawnChances: equipmentChances,
|
||||
botRole: botRole,
|
||||
@ -182,8 +182,8 @@ export class BotInventoryGenerator
|
||||
|
||||
// Generate below in specific order
|
||||
this.generateEquipment({
|
||||
equipmentSlot: EquipmentSlots.FACE_COVER,
|
||||
equipmentPool: templateInventory.equipment.FaceCover,
|
||||
rootEquipmentSlot: EquipmentSlots.FACE_COVER,
|
||||
rootEquipmentPool: templateInventory.equipment.FaceCover,
|
||||
modPool: templateInventory.mods,
|
||||
spawnChances: equipmentChances,
|
||||
botRole: botRole,
|
||||
@ -193,8 +193,8 @@ export class BotInventoryGenerator
|
||||
randomisationDetails: randomistionDetails
|
||||
});
|
||||
this.generateEquipment({
|
||||
equipmentSlot: EquipmentSlots.HEADWEAR,
|
||||
equipmentPool: templateInventory.equipment.Headwear,
|
||||
rootEquipmentSlot: EquipmentSlots.HEADWEAR,
|
||||
rootEquipmentPool: templateInventory.equipment.Headwear,
|
||||
modPool: templateInventory.mods,
|
||||
spawnChances: equipmentChances,
|
||||
botRole: botRole,
|
||||
@ -204,8 +204,8 @@ export class BotInventoryGenerator
|
||||
randomisationDetails: randomistionDetails
|
||||
});
|
||||
this.generateEquipment({
|
||||
equipmentSlot: EquipmentSlots.EARPIECE,
|
||||
equipmentPool: templateInventory.equipment.Earpiece,
|
||||
rootEquipmentSlot: EquipmentSlots.EARPIECE,
|
||||
rootEquipmentPool: templateInventory.equipment.Earpiece,
|
||||
modPool: templateInventory.mods,
|
||||
spawnChances: equipmentChances,
|
||||
botRole: botRole,
|
||||
@ -215,8 +215,8 @@ export class BotInventoryGenerator
|
||||
randomisationDetails: randomistionDetails
|
||||
});
|
||||
this.generateEquipment({
|
||||
equipmentSlot: EquipmentSlots.TACTICAL_VEST,
|
||||
equipmentPool: templateInventory.equipment.TacticalVest,
|
||||
rootEquipmentSlot: EquipmentSlots.TACTICAL_VEST,
|
||||
rootEquipmentPool: templateInventory.equipment.TacticalVest,
|
||||
modPool: templateInventory.mods,
|
||||
spawnChances: equipmentChances,
|
||||
botRole: botRole,
|
||||
@ -226,8 +226,8 @@ export class BotInventoryGenerator
|
||||
randomisationDetails: randomistionDetails
|
||||
});
|
||||
this.generateEquipment({
|
||||
equipmentSlot: EquipmentSlots.ARMOR_VEST,
|
||||
equipmentPool: templateInventory.equipment.ArmorVest,
|
||||
rootEquipmentSlot: EquipmentSlots.ARMOR_VEST,
|
||||
rootEquipmentPool: templateInventory.equipment.ArmorVest,
|
||||
modPool: templateInventory.mods,
|
||||
spawnChances: equipmentChances,
|
||||
botRole: botRole,
|
||||
@ -246,34 +246,34 @@ export class BotInventoryGenerator
|
||||
* @param spawnChances Chances items will be chosen to be added
|
||||
* @param botRole Role of bot e.g. assault
|
||||
* @param inventory Inventory to add item into
|
||||
* @param randomisationDetails settings from bot.json to adjust how item is generated
|
||||
* @param randomisationDetails
|
||||
*/
|
||||
protected generateEquipment(settings: IGenerateEquipmentProperties): void
|
||||
{
|
||||
const spawnChance =
|
||||
([EquipmentSlots.POCKETS, EquipmentSlots.SECURED_CONTAINER] as string[]).includes(settings.equipmentSlot)
|
||||
([EquipmentSlots.POCKETS, EquipmentSlots.SECURED_CONTAINER] as string[]).includes(settings.rootEquipmentSlot)
|
||||
? 100
|
||||
: settings.spawnChances.equipment[settings.equipmentSlot];
|
||||
: settings.spawnChances.equipment[settings.rootEquipmentSlot];
|
||||
if (typeof spawnChance === "undefined")
|
||||
{
|
||||
this.logger.warning(
|
||||
this.localisationService.getText("bot-no_spawn_chance_defined_for_equipment_slot", settings.equipmentSlot),
|
||||
this.localisationService.getText("bot-no_spawn_chance_defined_for_equipment_slot", settings.rootEquipmentSlot),
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const shouldSpawn = this.randomUtil.getChance100(spawnChance);
|
||||
if (Object.keys(settings.equipmentPool).length && shouldSpawn)
|
||||
if (Object.keys(settings.rootEquipmentPool).length && shouldSpawn)
|
||||
{
|
||||
const id = this.hashUtil.generate();
|
||||
const equipmentItemTpl = this.weightedRandomHelper.getWeightedValue<string>(settings.equipmentPool);
|
||||
const equipmentItemTpl = this.weightedRandomHelper.getWeightedValue<string>(settings.rootEquipmentPool);
|
||||
const itemTemplate = this.itemHelper.getItem(equipmentItemTpl);
|
||||
|
||||
if (!itemTemplate[0])
|
||||
{
|
||||
this.logger.error(this.localisationService.getText("bot-missing_item_template", equipmentItemTpl));
|
||||
this.logger.info(`EquipmentSlot -> ${settings.equipmentSlot}`);
|
||||
this.logger.info(`EquipmentSlot -> ${settings.rootEquipmentSlot}`);
|
||||
|
||||
return;
|
||||
}
|
||||
@ -282,7 +282,7 @@ export class BotInventoryGenerator
|
||||
this.botGeneratorHelper.isItemIncompatibleWithCurrentItems(
|
||||
settings.inventory.items,
|
||||
equipmentItemTpl,
|
||||
settings.equipmentSlot,
|
||||
settings.rootEquipmentSlot,
|
||||
).incompatible
|
||||
)
|
||||
{
|
||||
@ -294,7 +294,7 @@ export class BotInventoryGenerator
|
||||
_id: id,
|
||||
_tpl: equipmentItemTpl,
|
||||
parentId: settings.inventory.equipment,
|
||||
slotId: settings.equipmentSlot,
|
||||
slotId: settings.rootEquipmentSlot,
|
||||
...this.botGeneratorHelper.generateExtraPropertiesForItem(itemTemplate[1], settings.botRole),
|
||||
};
|
||||
|
||||
@ -302,7 +302,7 @@ export class BotInventoryGenerator
|
||||
const botEquipmentRole = this.botGeneratorHelper.getBotEquipmentRole(settings.botRole);
|
||||
if (
|
||||
this.botConfig.equipment[botEquipmentRole]
|
||||
&& settings.randomisationDetails?.randomisedArmorSlots?.includes(settings.equipmentSlot)
|
||||
&& settings.randomisationDetails?.randomisedArmorSlots?.includes(settings.rootEquipmentSlot)
|
||||
)
|
||||
{
|
||||
settings.modPool[equipmentItemTpl] = this.getFilteredDynamicModsForItem(
|
||||
@ -316,7 +316,6 @@ export class BotInventoryGenerator
|
||||
{
|
||||
const items = this.botEquipmentModGenerator.generateModsForEquipment(
|
||||
[item],
|
||||
settings.modPool,
|
||||
id,
|
||||
itemTemplate[1],
|
||||
settings
|
||||
@ -467,13 +466,19 @@ export class BotInventoryGenerator
|
||||
|
||||
export interface IGenerateEquipmentProperties
|
||||
{
|
||||
equipmentSlot: string,
|
||||
equipmentPool: Record<string, number>,
|
||||
/** Root Slot being generated */
|
||||
rootEquipmentSlot: string,
|
||||
/** Equipment pool for root slot being generated */
|
||||
rootEquipmentPool: Record<string, number>,
|
||||
modPool: Mods,
|
||||
/** Dictionary of mod items and their chance to spawn for this bot type */
|
||||
spawnChances: Chances,
|
||||
/** Role being generated for */
|
||||
botRole: string,
|
||||
/** level of bot being generated */
|
||||
botLevel: number,
|
||||
inventory: PmcInventory,
|
||||
botEquipmentConfig: EquipmentFilters,
|
||||
/** Settings from bot.json to adjust how item is generated */
|
||||
randomisationDetails: RandomisationDetails
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user