0
0
mirror of https://github.com/sp-tarkov/server.git synced 2025-02-13 09:50:43 -05:00

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 { HashUtil } from "@spt-aki/utils/HashUtil";
import { JsonUtil } from "@spt-aki/utils/JsonUtil"; import { JsonUtil } from "@spt-aki/utils/JsonUtil";
import { RandomUtil } from "@spt-aki/utils/RandomUtil"; import { RandomUtil } from "@spt-aki/utils/RandomUtil";
import { IGenerateEquipmentProperties } from "./BotInventoryGenerator";
@injectable() @injectable()
export class BotEquipmentModGenerator export class BotEquipmentModGenerator
@ -65,18 +66,16 @@ export class BotEquipmentModGenerator
*/ */
public generateModsForEquipment( public generateModsForEquipment(
equipment: Item[], equipment: Item[],
modPool: Mods,
parentId: string, parentId: string,
parentTemplate: ITemplateItem, parentTemplate: ITemplateItem,
modSpawnChances: ModsChances, settings: IGenerateEquipmentProperties,
botRole: string,
forceSpawn = false, forceSpawn = false,
): Item[] ): Item[]
{ {
const compatibleModsPool = modPool[parentTemplate._id]; const compatibleModsPool = settings.modPool[parentTemplate._id];
if (!compatibleModsPool) 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 // Iterate over mod pool and choose mods to add to item
@ -90,13 +89,13 @@ export class BotEquipmentModGenerator
modSlot: modSlot, modSlot: modSlot,
parentId: parentTemplate._id, parentId: parentTemplate._id,
parentName: parentTemplate._name, parentName: parentTemplate._name,
botRole: botRole botRole: settings.botRole
}), }),
); );
continue; continue;
} }
if (!(this.shouldModBeSpawned(itemSlot, modSlot, modSpawnChances) || forceSpawn)) if (!(this.shouldModBeSpawned(itemSlot, modSlot, settings.spawnChances.mods) || forceSpawn))
{ {
continue; continue;
} }
@ -107,12 +106,14 @@ export class BotEquipmentModGenerator
forceSpawn = true; forceSpawn = true;
} }
const modPoolToChooseFrom = this.filterPlateModsForSlot(settings.botRole, modSlot, compatibleModsPool[modSlot]);
let modTpl: string; let modTpl: string;
let found = false; let found = false;
// Find random mod and check its compatible // Find random mod and check its compatible
const exhaustableModPool = new ExhaustableArray( const exhaustableModPool = new ExhaustableArray(
compatibleModsPool[modSlot], modPoolToChooseFrom,
this.randomUtil, this.randomUtil,
this.jsonUtil, this.jsonUtil,
); );
@ -144,24 +145,22 @@ export class BotEquipmentModGenerator
} }
const modTemplate = this.itemHelper.getItem(modTpl); const modTemplate = this.itemHelper.getItem(modTpl);
if (!this.isModValidForSlot(modTemplate, itemSlot, modSlot, parentTemplate, botRole)) if (!this.isModValidForSlot(modTemplate, itemSlot, modSlot, parentTemplate, settings.botRole))
{ {
continue; continue;
} }
const modId = this.hashUtil.generate(); 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 // Call self recursively
this.generateModsForEquipment( this.generateModsForEquipment(
equipment, equipment,
modPool,
modId, modId,
modTemplate[1], modTemplate[1],
modSpawnChances, settings,
botRole,
forceSpawn, forceSpawn,
); );
} }
@ -170,6 +169,24 @@ export class BotEquipmentModGenerator
return equipment; 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 * Add mods to a weapon using the provided mod pool
* @param sessionId session id * @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 { Chances, Generation, IBotType, Inventory, Mods } from "@spt-aki/models/eft/common/tables/IBotType";
import { ConfigTypes } from "@spt-aki/models/enums/ConfigTypes"; import { ConfigTypes } from "@spt-aki/models/enums/ConfigTypes";
import { EquipmentSlots } from "@spt-aki/models/enums/EquipmentSlots"; 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 { ILogger } from "@spt-aki/models/spt/utils/ILogger";
import { ConfigServer } from "@spt-aki/servers/ConfigServer"; import { ConfigServer } from "@spt-aki/servers/ConfigServer";
import { DatabaseServer } from "@spt-aki/servers/DatabaseServer"; import { DatabaseServer } from "@spt-aki/servers/DatabaseServer";
@ -159,7 +159,6 @@ export class BotInventoryGenerator
const botEquipConfig = this.botConfig.equipment[this.botGeneratorHelper.getBotEquipmentRole(botRole)]; const botEquipConfig = this.botConfig.equipment[this.botGeneratorHelper.getBotEquipmentRole(botRole)];
const randomistionDetails = this.botHelper.getBotRandomizationDetails(botLevel, botEquipConfig); const randomistionDetails = this.botHelper.getBotRandomizationDetails(botLevel, botEquipConfig);
for (const equipmentSlot in templateInventory.equipment) for (const equipmentSlot in templateInventory.equipment)
{ {
// Weapons have special generation and will be generated separately; ArmorVest should be generated after TactivalVest // Weapons have special generation and will be generated separately; ArmorVest should be generated after TactivalVest
@ -168,63 +167,75 @@ export class BotInventoryGenerator
continue; continue;
} }
this.generateEquipment( this.generateEquipment({
equipmentSlot, equipmentSlot: equipmentSlot,
templateInventory.equipment[equipmentSlot], equipmentPool: templateInventory.equipment[equipmentSlot],
templateInventory.mods, modPool: templateInventory.mods,
equipmentChances, spawnChances: equipmentChances,
botRole, botRole: botRole,
botInventory, botLevel: botLevel,
randomistionDetails, inventory: botInventory,
); botEquipmentConfig: botEquipConfig,
randomisationDetails: randomistionDetails
});
} }
// Generate below in specific order // Generate below in specific order
this.generateEquipment( this.generateEquipment({
EquipmentSlots.FACE_COVER, equipmentSlot: EquipmentSlots.FACE_COVER,
templateInventory.equipment.FaceCover, equipmentPool: templateInventory.equipment.FaceCover,
templateInventory.mods, modPool: templateInventory.mods,
equipmentChances, spawnChances: equipmentChances,
botRole, botRole: botRole,
botInventory, botLevel: botLevel,
randomistionDetails, inventory: botInventory,
); botEquipmentConfig: botEquipConfig,
this.generateEquipment( randomisationDetails: randomistionDetails
EquipmentSlots.HEADWEAR, });
templateInventory.equipment.Headwear, this.generateEquipment({
templateInventory.mods, equipmentSlot: EquipmentSlots.HEADWEAR,
equipmentChances, equipmentPool: templateInventory.equipment.Headwear,
botRole, modPool: templateInventory.mods,
botInventory, spawnChances: equipmentChances,
randomistionDetails, botRole: botRole,
); botLevel: botLevel,
this.generateEquipment( inventory: botInventory,
EquipmentSlots.EARPIECE, botEquipmentConfig: botEquipConfig,
templateInventory.equipment.Earpiece, randomisationDetails: randomistionDetails
templateInventory.mods, });
equipmentChances, this.generateEquipment({
botRole, equipmentSlot: EquipmentSlots.EARPIECE,
botInventory, equipmentPool: templateInventory.equipment.Earpiece,
randomistionDetails, modPool: templateInventory.mods,
); spawnChances: equipmentChances,
this.generateEquipment( botRole: botRole,
EquipmentSlots.TACTICAL_VEST, botLevel: botLevel,
templateInventory.equipment.TacticalVest, inventory: botInventory,
templateInventory.mods, botEquipmentConfig: botEquipConfig,
equipmentChances, randomisationDetails: randomistionDetails
botRole, });
botInventory, this.generateEquipment({
randomistionDetails, equipmentSlot: EquipmentSlots.TACTICAL_VEST,
); equipmentPool: templateInventory.equipment.TacticalVest,
this.generateEquipment( modPool: templateInventory.mods,
EquipmentSlots.ARMOR_VEST, spawnChances: equipmentChances,
templateInventory.equipment.ArmorVest, botRole: botRole,
templateInventory.mods, botLevel: botLevel,
equipmentChances, inventory: botInventory,
botRole, botEquipmentConfig: botEquipConfig,
botInventory, randomisationDetails: randomistionDetails
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 inventory Inventory to add item into
* @param randomisationDetails settings from bot.json to adjust how item is generated * @param randomisationDetails settings from bot.json to adjust how item is generated
*/ */
protected generateEquipment( protected generateEquipment(settings: IGenerateEquipmentProperties): void
equipmentSlot: string,
equipmentPool: Record<string, number>,
modPool: Mods,
spawnChances: Chances,
botRole: string,
inventory: PmcInventory,
randomisationDetails: RandomisationDetails,
): void
{ {
const spawnChance = const spawnChance =
([EquipmentSlots.POCKETS, EquipmentSlots.SECURED_CONTAINER] as string[]).includes(equipmentSlot) ([EquipmentSlots.POCKETS, EquipmentSlots.SECURED_CONTAINER] as string[]).includes(settings.equipmentSlot)
? 100 ? 100
: spawnChances.equipment[equipmentSlot]; : settings.spawnChances.equipment[settings.equipmentSlot];
if (typeof spawnChance === "undefined") if (typeof spawnChance === "undefined")
{ {
this.logger.warning( 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; return;
} }
const shouldSpawn = this.randomUtil.getChance100(spawnChance); 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 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); const itemTemplate = this.itemHelper.getItem(equipmentItemTpl);
if (!itemTemplate[0]) if (!itemTemplate[0])
{ {
this.logger.error(this.localisationService.getText("bot-missing_item_template", equipmentItemTpl)); this.logger.error(this.localisationService.getText("bot-missing_item_template", equipmentItemTpl));
this.logger.info(`EquipmentSlot -> ${equipmentSlot}`); this.logger.info(`EquipmentSlot -> ${settings.equipmentSlot}`);
return; return;
} }
if ( if (
this.botGeneratorHelper.isItemIncompatibleWithCurrentItems( this.botGeneratorHelper.isItemIncompatibleWithCurrentItems(
inventory.items, settings.inventory.items,
equipmentItemTpl, equipmentItemTpl,
equipmentSlot, settings.equipmentSlot,
).incompatible ).incompatible
) )
{ {
@ -290,19 +293,19 @@ export class BotInventoryGenerator
const item = { const item = {
_id: id, _id: id,
_tpl: equipmentItemTpl, _tpl: equipmentItemTpl,
parentId: inventory.equipment, parentId: settings.inventory.equipment,
slotId: equipmentSlot, slotId: settings.equipmentSlot,
...this.botGeneratorHelper.generateExtraPropertiesForItem(itemTemplate[1], botRole), ...this.botGeneratorHelper.generateExtraPropertiesForItem(itemTemplate[1], settings.botRole),
}; };
// use dynamic mod pool if enabled in config // use dynamic mod pool if enabled in config
const botEquipmentRole = this.botGeneratorHelper.getBotEquipmentRole(botRole); const botEquipmentRole = this.botGeneratorHelper.getBotEquipmentRole(settings.botRole);
if ( if (
this.botConfig.equipment[botEquipmentRole] this.botConfig.equipment[botEquipmentRole]
&& randomisationDetails?.randomisedArmorSlots?.includes(equipmentSlot) && settings.randomisationDetails?.randomisedArmorSlots?.includes(settings.equipmentSlot)
) )
{ {
modPool[equipmentItemTpl] = this.getFilteredDynamicModsForItem( settings.modPool[equipmentItemTpl] = this.getFilteredDynamicModsForItem(
equipmentItemTpl, equipmentItemTpl,
this.botConfig.equipment[botEquipmentRole].blacklist, this.botConfig.equipment[botEquipmentRole].blacklist,
); );
@ -313,17 +316,16 @@ export class BotInventoryGenerator
{ {
const items = this.botEquipmentModGenerator.generateModsForEquipment( const items = this.botEquipmentModGenerator.generateModsForEquipment(
[item], [item],
modPool, settings.modPool,
id, id,
itemTemplate[1], itemTemplate[1],
spawnChances.mods, settings
botRole,
); );
inventory.items.push(...items); settings.inventory.items.push(...items);
} }
else 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[]; weightingAdjustmentsByPlayerLevel?: WeightingAdjustmentDetails[];
/** Should the stock mod be forced to spawn on bot */ /** Should the stock mod be forced to spawn on bot */
forceStock: boolean; forceStock: boolean;
armorPlateWeighting?: IArmorPlateWeights[]
} }
export interface ModLimits export interface ModLimits
@ -156,19 +157,28 @@ export interface WeightingAdjustmentDetails
/** Between what levels do these weight settings apply to */ /** Between what levels do these weight settings apply to */
levelRange: MinMax; levelRange: MinMax;
/** Key: ammo type e.g. Caliber556x45NATO, value: item tpl + weight */ /** Key: ammo type e.g. Caliber556x45NATO, value: item tpl + weight */
ammo?: AdjustmentDetails; ammo?: IAdjustmentDetails;
/** Key: equipment slot e.g. TacticalVest, value: item tpl + weight */ /** Key: equipment slot e.g. TacticalVest, value: item tpl + weight */
equipment?: AdjustmentDetails; equipment?: IAdjustmentDetails;
/** Key: clothing slot e.g. feet, value: item tpl + weight */ /** 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>>; add: Record<string, Record<string, number>>;
edit: 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 export interface IRandomisedResourceDetails
{ {
food: IRandomisedResourceValues; food: IRandomisedResourceValues;

View File

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