2023-11-13 17:43:37 +01:00
|
|
|
import { inject, injectAll, injectable } from "tsyringe";
|
2023-03-03 16:23:46 +01:00
|
|
|
|
2023-10-19 19:21:17 +02:00
|
|
|
import { BotEquipmentModGenerator } from "@spt-aki/generators/BotEquipmentModGenerator";
|
|
|
|
import { IInventoryMagGen } from "@spt-aki/generators/weapongen/IInventoryMagGen";
|
|
|
|
import { InventoryMagGen } from "@spt-aki/generators/weapongen/InventoryMagGen";
|
|
|
|
import { BotGeneratorHelper } from "@spt-aki/helpers/BotGeneratorHelper";
|
|
|
|
import { BotWeaponGeneratorHelper } from "@spt-aki/helpers/BotWeaponGeneratorHelper";
|
|
|
|
import { ItemHelper } from "@spt-aki/helpers/ItemHelper";
|
|
|
|
import { WeightedRandomHelper } from "@spt-aki/helpers/WeightedRandomHelper";
|
|
|
|
import { IPreset } from "@spt-aki/models/eft/common/IGlobals";
|
|
|
|
import { Inventory as PmcInventory } from "@spt-aki/models/eft/common/tables/IBotBase";
|
|
|
|
import { GenerationData, Inventory, ModsChances } from "@spt-aki/models/eft/common/tables/IBotType";
|
|
|
|
import { Item } from "@spt-aki/models/eft/common/tables/IItem";
|
|
|
|
import { ITemplateItem } from "@spt-aki/models/eft/common/tables/ITemplateItem";
|
|
|
|
import { ConfigTypes } from "@spt-aki/models/enums/ConfigTypes";
|
|
|
|
import { EquipmentSlots } from "@spt-aki/models/enums/EquipmentSlots";
|
|
|
|
import { GenerateWeaponResult } from "@spt-aki/models/spt/bots/GenerateWeaponResult";
|
|
|
|
import { IBotConfig } from "@spt-aki/models/spt/config/IBotConfig";
|
|
|
|
import { IPmcConfig } from "@spt-aki/models/spt/config/IPmcConfig";
|
|
|
|
import { IRepairConfig } from "@spt-aki/models/spt/config/IRepairConfig";
|
|
|
|
import { ILogger } from "@spt-aki/models/spt/utils/ILogger";
|
|
|
|
import { ConfigServer } from "@spt-aki/servers/ConfigServer";
|
|
|
|
import { DatabaseServer } from "@spt-aki/servers/DatabaseServer";
|
|
|
|
import { BotWeaponModLimitService } from "@spt-aki/services/BotWeaponModLimitService";
|
|
|
|
import { LocalisationService } from "@spt-aki/services/LocalisationService";
|
|
|
|
import { RepairService } from "@spt-aki/services/RepairService";
|
|
|
|
import { HashUtil } from "@spt-aki/utils/HashUtil";
|
|
|
|
import { JsonUtil } from "@spt-aki/utils/JsonUtil";
|
|
|
|
import { RandomUtil } from "@spt-aki/utils/RandomUtil";
|
2023-03-03 16:23:46 +01:00
|
|
|
|
|
|
|
@injectable()
|
|
|
|
export class BotWeaponGenerator
|
|
|
|
{
|
|
|
|
protected readonly modMagazineSlotId = "mod_magazine";
|
|
|
|
protected botConfig: IBotConfig;
|
2023-10-10 13:03:20 +02:00
|
|
|
protected pmcConfig: IPmcConfig;
|
|
|
|
protected repairConfig: IRepairConfig;
|
2023-03-03 16:23:46 +01:00
|
|
|
|
|
|
|
constructor(
|
|
|
|
@inject("JsonUtil") protected jsonUtil: JsonUtil,
|
|
|
|
@inject("WinstonLogger") protected logger: ILogger,
|
|
|
|
@inject("HashUtil") protected hashUtil: HashUtil,
|
|
|
|
@inject("DatabaseServer") protected databaseServer: DatabaseServer,
|
|
|
|
@inject("ItemHelper") protected itemHelper: ItemHelper,
|
|
|
|
@inject("WeightedRandomHelper") protected weightedRandomHelper: WeightedRandomHelper,
|
|
|
|
@inject("BotGeneratorHelper") protected botGeneratorHelper: BotGeneratorHelper,
|
|
|
|
@inject("RandomUtil") protected randomUtil: RandomUtil,
|
|
|
|
@inject("ConfigServer") protected configServer: ConfigServer,
|
|
|
|
@inject("BotWeaponGeneratorHelper") protected botWeaponGeneratorHelper: BotWeaponGeneratorHelper,
|
|
|
|
@inject("BotWeaponModLimitService") protected botWeaponModLimitService: BotWeaponModLimitService,
|
|
|
|
@inject("BotEquipmentModGenerator") protected botEquipmentModGenerator: BotEquipmentModGenerator,
|
|
|
|
@inject("LocalisationService") protected localisationService: LocalisationService,
|
2023-10-10 13:03:20 +02:00
|
|
|
@inject("RepairService") protected repairService: RepairService,
|
2023-11-16 02:35:05 +01:00
|
|
|
@injectAll("InventoryMagGen") protected inventoryMagGenComponents: IInventoryMagGen[],
|
2023-03-03 16:23:46 +01:00
|
|
|
)
|
|
|
|
{
|
|
|
|
this.botConfig = this.configServer.getConfig(ConfigTypes.BOT);
|
2023-10-10 13:03:20 +02:00
|
|
|
this.pmcConfig = this.configServer.getConfig(ConfigTypes.PMC);
|
|
|
|
this.repairConfig = this.configServer.getConfig(ConfigTypes.REPAIR);
|
2023-03-03 16:23:46 +01:00
|
|
|
this.inventoryMagGenComponents.sort((a, b) => a.getPriority() - b.getPriority());
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Pick a random weapon based on weightings and generate a functional weapon
|
|
|
|
* @param equipmentSlot Primary/secondary/holster
|
|
|
|
* @param botTemplateInventory e.g. assault.json
|
2023-11-16 02:35:05 +01:00
|
|
|
* @param weaponParentId
|
|
|
|
* @param modChances
|
2023-03-03 16:23:46 +01:00
|
|
|
* @param botRole role of bot, e.g. assault/followerBully
|
|
|
|
* @param isPmc Is weapon generated for a pmc
|
|
|
|
* @returns GenerateWeaponResult object
|
|
|
|
*/
|
2023-11-16 02:35:05 +01:00
|
|
|
public generateRandomWeapon(
|
|
|
|
sessionId: string,
|
|
|
|
equipmentSlot: string,
|
|
|
|
botTemplateInventory: Inventory,
|
|
|
|
weaponParentId: string,
|
|
|
|
modChances: ModsChances,
|
|
|
|
botRole: string,
|
|
|
|
isPmc: boolean,
|
|
|
|
botLevel: number,
|
|
|
|
): GenerateWeaponResult
|
2023-03-03 16:23:46 +01:00
|
|
|
{
|
|
|
|
const weaponTpl = this.pickWeightedWeaponTplFromPool(equipmentSlot, botTemplateInventory);
|
2023-11-16 02:35:05 +01:00
|
|
|
return this.generateWeaponByTpl(
|
|
|
|
sessionId,
|
|
|
|
weaponTpl,
|
|
|
|
equipmentSlot,
|
|
|
|
botTemplateInventory,
|
|
|
|
weaponParentId,
|
|
|
|
modChances,
|
|
|
|
botRole,
|
|
|
|
isPmc,
|
|
|
|
botLevel,
|
|
|
|
);
|
2023-03-03 16:23:46 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get a random weighted weapon from a bots pool of weapons
|
|
|
|
* @param equipmentSlot Primary/secondary/holster
|
|
|
|
* @param botTemplateInventory e.g. assault.json
|
|
|
|
* @returns weapon tpl
|
|
|
|
*/
|
|
|
|
public pickWeightedWeaponTplFromPool(equipmentSlot: string, botTemplateInventory: Inventory): string
|
|
|
|
{
|
|
|
|
const weaponPool = botTemplateInventory.equipment[equipmentSlot];
|
2023-10-10 13:03:20 +02:00
|
|
|
return this.weightedRandomHelper.getWeightedValue<string>(weaponPool);
|
2023-03-03 16:23:46 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Generated a weapon based on the supplied weapon tpl
|
|
|
|
* @param weaponTpl weapon tpl to generate (use pickWeightedWeaponTplFromPool())
|
|
|
|
* @param equipmentSlot slot to fit into, primary/secondary/holster
|
|
|
|
* @param botTemplateInventory e.g. assault.json
|
|
|
|
* @param weaponParentId ParentId of the weapon being generated
|
|
|
|
* @param modChances Dictionary of item types and % chance weapon will have that mod
|
|
|
|
* @param botRole e.g. assault/exusec
|
2023-10-10 13:03:20 +02:00
|
|
|
* @param isPmc Is weapon being generated for a pmc
|
2023-03-03 16:23:46 +01:00
|
|
|
* @returns GenerateWeaponResult object
|
|
|
|
*/
|
2023-11-16 02:35:05 +01:00
|
|
|
public generateWeaponByTpl(
|
|
|
|
sessionId: string,
|
|
|
|
weaponTpl: string,
|
|
|
|
equipmentSlot: string,
|
|
|
|
botTemplateInventory: Inventory,
|
|
|
|
weaponParentId: string,
|
|
|
|
modChances: ModsChances,
|
|
|
|
botRole: string,
|
|
|
|
isPmc: boolean,
|
|
|
|
botLevel: number,
|
|
|
|
): GenerateWeaponResult
|
2023-03-03 16:23:46 +01:00
|
|
|
{
|
|
|
|
const modPool = botTemplateInventory.mods;
|
|
|
|
const weaponItemTemplate = this.itemHelper.getItem(weaponTpl)[1];
|
|
|
|
|
|
|
|
if (!weaponItemTemplate)
|
|
|
|
{
|
|
|
|
this.logger.error(this.localisationService.getText("bot-missing_item_template", weaponTpl));
|
|
|
|
this.logger.error(`WeaponSlot -> ${equipmentSlot}`);
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Find ammo to use when filling magazines/chamber
|
|
|
|
if (!botTemplateInventory.Ammo)
|
|
|
|
{
|
|
|
|
this.logger.error(this.localisationService.getText("bot-no_ammo_found_in_bot_json", botRole));
|
|
|
|
|
|
|
|
throw new Error(this.localisationService.getText("bot-generation_failed"));
|
|
|
|
}
|
|
|
|
const ammoTpl = this.getWeightedCompatibleAmmo(botTemplateInventory.Ammo, weaponItemTemplate);
|
|
|
|
|
|
|
|
// Create with just base weapon item
|
2023-11-16 02:35:05 +01:00
|
|
|
let weaponWithModsArray = this.constructWeaponBaseArray(
|
|
|
|
weaponTpl,
|
|
|
|
weaponParentId,
|
|
|
|
equipmentSlot,
|
|
|
|
weaponItemTemplate,
|
|
|
|
botRole,
|
|
|
|
);
|
2023-03-03 16:23:46 +01:00
|
|
|
|
2023-10-10 13:03:20 +02:00
|
|
|
// Chance to add randomised weapon enhancement
|
|
|
|
if (isPmc && this.randomUtil.getChance100(this.pmcConfig.weaponHasEnhancementChancePercent))
|
|
|
|
{
|
|
|
|
const weaponConfig = this.repairConfig.repairKit.weapon;
|
|
|
|
this.repairService.addBuff(weaponConfig, weaponWithModsArray[0]);
|
|
|
|
}
|
|
|
|
|
2023-03-03 16:23:46 +01:00
|
|
|
// Add mods to weapon base
|
|
|
|
if (Object.keys(modPool).includes(weaponTpl))
|
|
|
|
{
|
|
|
|
const botEquipmentRole = this.botGeneratorHelper.getBotEquipmentRole(botRole);
|
|
|
|
const modLimits = this.botWeaponModLimitService.getWeaponModLimits(botEquipmentRole);
|
2023-11-16 02:35:05 +01:00
|
|
|
weaponWithModsArray = this.botEquipmentModGenerator.generateModsForWeapon(
|
|
|
|
sessionId,
|
|
|
|
weaponWithModsArray,
|
|
|
|
modPool,
|
|
|
|
weaponWithModsArray[0]._id,
|
|
|
|
weaponItemTemplate,
|
|
|
|
modChances,
|
|
|
|
ammoTpl,
|
|
|
|
botRole,
|
|
|
|
botLevel,
|
|
|
|
modLimits,
|
|
|
|
botEquipmentRole,
|
|
|
|
);
|
2023-03-03 16:23:46 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Use weapon preset from globals.json if weapon isnt valid
|
|
|
|
if (!this.isWeaponValid(weaponWithModsArray, botRole))
|
|
|
|
{
|
|
|
|
// Weapon is bad, fall back to weapons preset
|
2023-11-16 02:35:05 +01:00
|
|
|
weaponWithModsArray = this.getPresetWeaponMods(
|
|
|
|
weaponTpl,
|
|
|
|
equipmentSlot,
|
|
|
|
weaponParentId,
|
|
|
|
weaponItemTemplate,
|
|
|
|
botRole,
|
|
|
|
);
|
2023-03-03 16:23:46 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Fill existing magazines to full and sync ammo type
|
2023-11-16 02:35:05 +01:00
|
|
|
for (const magazine of weaponWithModsArray.filter((x) => x.slotId === this.modMagazineSlotId))
|
2023-03-03 16:23:46 +01:00
|
|
|
{
|
|
|
|
this.fillExistingMagazines(weaponWithModsArray, magazine, ammoTpl);
|
|
|
|
}
|
|
|
|
|
2023-11-29 12:35:34 +01:00
|
|
|
// Add cartridge(s) to gun chamber(s)
|
2023-11-16 02:35:05 +01:00
|
|
|
if (
|
2023-11-29 12:35:34 +01:00
|
|
|
weaponItemTemplate._props.Chambers?.length > 0
|
2023-11-16 02:35:05 +01:00
|
|
|
&& weaponItemTemplate._props.Chambers[0]?._props?.filters[0]?.Filter?.includes(ammoTpl)
|
|
|
|
)
|
2023-07-24 22:24:55 +02:00
|
|
|
{
|
2023-11-29 12:35:34 +01:00
|
|
|
// Guns have variety of possible Chamber ids, patron_in_weapon/patron_in_weapon_000/patron_in_weapon_001
|
|
|
|
const chamberSlotNames = weaponItemTemplate._props.Chambers.map(x => x._name);
|
|
|
|
this.addCartridgeToChamber(weaponWithModsArray, ammoTpl, chamberSlotNames);
|
2023-07-24 22:24:55 +02:00
|
|
|
}
|
|
|
|
|
2023-03-03 16:23:46 +01:00
|
|
|
// Fill UBGL if found
|
2023-11-16 02:35:05 +01:00
|
|
|
const ubglMod = weaponWithModsArray.find((x) => x.slotId === "mod_launcher");
|
2023-03-03 16:23:46 +01:00
|
|
|
let ubglAmmoTpl: string = undefined;
|
|
|
|
if (ubglMod)
|
|
|
|
{
|
|
|
|
const ubglTemplate = this.itemHelper.getItem(ubglMod._tpl)[1];
|
|
|
|
ubglAmmoTpl = this.getWeightedCompatibleAmmo(botTemplateInventory.Ammo, ubglTemplate);
|
|
|
|
this.fillUbgl(weaponWithModsArray, ubglMod, ubglAmmoTpl);
|
|
|
|
}
|
|
|
|
|
|
|
|
return {
|
|
|
|
weapon: weaponWithModsArray,
|
|
|
|
chosenAmmoTpl: ammoTpl,
|
|
|
|
chosenUbglAmmoTpl: ubglAmmoTpl,
|
|
|
|
weaponMods: modPool,
|
2023-11-16 02:35:05 +01:00
|
|
|
weaponTemplate: weaponItemTemplate,
|
2023-03-03 16:23:46 +01:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2023-07-24 22:24:55 +02:00
|
|
|
/**
|
2023-11-29 12:35:34 +01:00
|
|
|
* Insert a cartridge(s) into a weapon
|
|
|
|
* Handles all chambers - patron_in_weapon, patron_in_weapon_000 etc
|
2023-07-24 22:24:55 +02:00
|
|
|
* @param weaponWithModsArray Weapon and mods
|
|
|
|
* @param ammoTpl Cartridge to add to weapon
|
2023-11-29 12:35:34 +01:00
|
|
|
* @param chamberSlotIds name of slots to create or add ammo to
|
2023-07-24 22:24:55 +02:00
|
|
|
*/
|
2023-11-29 12:35:34 +01:00
|
|
|
protected addCartridgeToChamber(weaponWithModsArray: Item[], ammoTpl: string, chamberSlotIds: string[]): void
|
2023-07-24 22:24:55 +02:00
|
|
|
{
|
2023-11-29 12:35:34 +01:00
|
|
|
for (const slotId of chamberSlotIds)
|
2023-07-24 22:24:55 +02:00
|
|
|
{
|
2023-11-29 12:35:34 +01:00
|
|
|
const existingItemWithSlot = weaponWithModsArray.find((x) => x.slotId === slotId);
|
|
|
|
if (!existingItemWithSlot)
|
|
|
|
{
|
|
|
|
// Not found, add new slot to weapon
|
|
|
|
weaponWithModsArray.push({
|
|
|
|
_id: this.hashUtil.generate(),
|
|
|
|
_tpl: ammoTpl,
|
|
|
|
parentId: weaponWithModsArray[0]._id,
|
|
|
|
slotId: slotId,
|
|
|
|
upd: { StackObjectsCount: 1 },
|
|
|
|
});
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Already exists, update values
|
|
|
|
existingItemWithSlot._tpl = ammoTpl;
|
|
|
|
existingItemWithSlot.upd = { StackObjectsCount: 1 };
|
|
|
|
}
|
2023-07-24 22:24:55 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-03-03 16:23:46 +01:00
|
|
|
/**
|
|
|
|
* Create array with weapon base as only element and
|
|
|
|
* add additional properties based on weapon type
|
|
|
|
* @param weaponTpl Weapon tpl to create item with
|
|
|
|
* @param weaponParentId Weapons parent id
|
2023-11-16 02:35:05 +01:00
|
|
|
* @param equipmentSlot e.g. primary/secondary/holster
|
2023-03-03 16:23:46 +01:00
|
|
|
* @param weaponItemTemplate db template for weapon
|
|
|
|
* @param botRole for durability values
|
|
|
|
* @returns Base weapon item in array
|
|
|
|
*/
|
2023-11-16 02:35:05 +01:00
|
|
|
protected constructWeaponBaseArray(
|
|
|
|
weaponTpl: string,
|
|
|
|
weaponParentId: string,
|
|
|
|
equipmentSlot: string,
|
|
|
|
weaponItemTemplate: ITemplateItem,
|
|
|
|
botRole: string,
|
|
|
|
): Item[]
|
2023-03-03 16:23:46 +01:00
|
|
|
{
|
|
|
|
return [{
|
|
|
|
_id: this.hashUtil.generate(),
|
|
|
|
_tpl: weaponTpl,
|
|
|
|
parentId: weaponParentId,
|
|
|
|
slotId: equipmentSlot,
|
2023-11-16 02:35:05 +01:00
|
|
|
...this.botGeneratorHelper.generateExtraPropertiesForItem(weaponItemTemplate, botRole),
|
2023-03-03 16:23:46 +01:00
|
|
|
}];
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the mods necessary to kit out a weapon to its preset level
|
|
|
|
* @param weaponTpl weapon to find preset for
|
|
|
|
* @param equipmentSlot the slot the weapon will be placed in
|
|
|
|
* @param weaponParentId Value used for the parentid
|
|
|
|
* @returns array of weapon mods
|
|
|
|
*/
|
2023-11-16 02:35:05 +01:00
|
|
|
protected getPresetWeaponMods(
|
|
|
|
weaponTpl: string,
|
|
|
|
equipmentSlot: string,
|
|
|
|
weaponParentId: string,
|
|
|
|
itemTemplate: ITemplateItem,
|
|
|
|
botRole: string,
|
|
|
|
): Item[]
|
2023-03-03 16:23:46 +01:00
|
|
|
{
|
|
|
|
// Invalid weapon generated, fallback to preset
|
2023-11-16 02:35:05 +01:00
|
|
|
this.logger.warning(
|
|
|
|
this.localisationService.getText("bot-weapon_generated_incorrect_using_default", weaponTpl),
|
|
|
|
);
|
2023-03-03 16:23:46 +01:00
|
|
|
const weaponMods = [];
|
|
|
|
|
|
|
|
// TODO: Right now, preset weapons trigger a lot of warnings regarding missing ammo in magazines & such
|
2023-07-18 16:44:14 +02:00
|
|
|
let preset: IPreset;
|
2023-03-03 16:23:46 +01:00
|
|
|
for (const presetObj of Object.values(this.databaseServer.getTables().globals.ItemPresets))
|
|
|
|
{
|
|
|
|
if (presetObj._items[0]._tpl === weaponTpl)
|
|
|
|
{
|
|
|
|
preset = this.jsonUtil.clone(presetObj);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (preset)
|
|
|
|
{
|
|
|
|
const parentItem = preset._items[0];
|
|
|
|
preset._items[0] = {
|
2023-11-16 02:35:05 +01:00
|
|
|
...parentItem,
|
|
|
|
...{
|
|
|
|
parentId: weaponParentId,
|
|
|
|
slotId: equipmentSlot,
|
|
|
|
...this.botGeneratorHelper.generateExtraPropertiesForItem(itemTemplate, botRole),
|
|
|
|
},
|
2023-03-03 16:23:46 +01:00
|
|
|
};
|
|
|
|
weaponMods.push(...preset._items);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
throw new Error(this.localisationService.getText("bot-missing_weapon_preset", weaponTpl));
|
|
|
|
}
|
|
|
|
|
|
|
|
return weaponMods;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Checks if all required slots are occupied on a weapon and all it's mods
|
|
|
|
* @param weaponItemArray Weapon + mods
|
|
|
|
* @param botRole role of bot weapon is for
|
|
|
|
* @returns true if valid
|
|
|
|
*/
|
|
|
|
protected isWeaponValid(weaponItemArray: Item[], botRole: string): boolean
|
|
|
|
{
|
|
|
|
for (const mod of weaponItemArray)
|
|
|
|
{
|
|
|
|
const modDbTemplate = this.itemHelper.getItem(mod._tpl)[1];
|
|
|
|
if (!modDbTemplate._props.Slots?.length)
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Iterate over slots in db item, if required, check tpl in that slot matches the filter list
|
|
|
|
for (const modSlot of modDbTemplate._props.Slots)
|
|
|
|
{
|
2023-12-28 11:58:29 +01:00
|
|
|
// Ignore optional mods
|
2023-03-03 16:23:46 +01:00
|
|
|
if (!modSlot._required)
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
const allowedTpls = modSlot._props.filters[0].Filter;
|
|
|
|
const slotName = modSlot._name;
|
|
|
|
|
2023-11-16 02:35:05 +01:00
|
|
|
const weaponSlotItem = weaponItemArray.find((x) => x.parentId === mod._id && x.slotId === slotName);
|
2023-03-03 16:23:46 +01:00
|
|
|
if (!weaponSlotItem)
|
|
|
|
{
|
2023-11-16 02:35:05 +01:00
|
|
|
this.logger.warning(
|
|
|
|
this.localisationService.getText("bot-weapons_required_slot_missing_item", {
|
|
|
|
modSlot: modSlot._name,
|
|
|
|
modName: modDbTemplate._name,
|
|
|
|
slotId: mod.slotId,
|
|
|
|
botRole: botRole,
|
|
|
|
}),
|
|
|
|
);
|
2023-03-03 16:23:46 +01:00
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!allowedTpls.includes(weaponSlotItem._tpl))
|
|
|
|
{
|
2023-11-16 02:35:05 +01:00
|
|
|
this.logger.warning(
|
|
|
|
this.localisationService.getText("bot-weapon_contains_invalid_item", {
|
|
|
|
modSlot: modSlot._name,
|
|
|
|
modName: modDbTemplate._name,
|
|
|
|
weaponTpl: weaponSlotItem._tpl,
|
|
|
|
}),
|
|
|
|
);
|
2023-03-03 16:23:46 +01:00
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Generates extra magazines or bullets (if magazine is internal) and adds them to TacticalVest and Pockets.
|
|
|
|
* Additionally, adds extra bullets to SecuredContainer
|
|
|
|
* @param generatedWeaponResult object with properties for generated weapon (weapon mods pool / weapon template / ammo tpl)
|
2023-10-10 13:03:20 +02:00
|
|
|
* @param magWeights Magazine weights for count to add to inventory
|
2023-03-03 16:23:46 +01:00
|
|
|
* @param inventory Inventory to add magazines to
|
|
|
|
* @param botRole The bot type we're getting generating extra mags for
|
|
|
|
*/
|
2023-11-16 02:35:05 +01:00
|
|
|
public addExtraMagazinesToInventory(
|
|
|
|
generatedWeaponResult: GenerateWeaponResult,
|
|
|
|
magWeights: GenerationData,
|
|
|
|
inventory: PmcInventory,
|
|
|
|
botRole: string,
|
|
|
|
): void
|
2023-03-03 16:23:46 +01:00
|
|
|
{
|
2023-07-24 22:24:55 +02:00
|
|
|
const weaponAndMods = generatedWeaponResult.weapon;
|
2023-03-03 16:23:46 +01:00
|
|
|
const weaponTemplate = generatedWeaponResult.weaponTemplate;
|
2023-07-24 22:24:55 +02:00
|
|
|
const magazineTpl = this.getMagazineTplFromWeaponTemplate(weaponAndMods, weaponTemplate, botRole);
|
2023-11-16 02:35:05 +01:00
|
|
|
|
2023-03-03 16:23:46 +01:00
|
|
|
const magTemplate = this.itemHelper.getItem(magazineTpl)[1];
|
|
|
|
if (!magTemplate)
|
|
|
|
{
|
|
|
|
this.logger.error(this.localisationService.getText("bot-unable_to_find_magazine_item", magazineTpl));
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-07-24 22:24:55 +02:00
|
|
|
const ammoTemplate = this.itemHelper.getItem(generatedWeaponResult.chosenAmmoTpl)[1];
|
2023-03-03 16:23:46 +01:00
|
|
|
if (!ammoTemplate)
|
|
|
|
{
|
2023-11-16 02:35:05 +01:00
|
|
|
this.logger.error(
|
|
|
|
this.localisationService.getText("bot-unable_to_find_ammo_item", generatedWeaponResult.chosenAmmoTpl),
|
|
|
|
);
|
2023-03-03 16:23:46 +01:00
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Has an UBGL
|
|
|
|
if (generatedWeaponResult.chosenUbglAmmoTpl)
|
|
|
|
{
|
2023-07-24 22:24:55 +02:00
|
|
|
this.addUbglGrenadesToBotInventory(weaponAndMods, generatedWeaponResult, inventory);
|
2023-03-03 16:23:46 +01:00
|
|
|
}
|
|
|
|
|
2023-11-16 02:35:05 +01:00
|
|
|
const inventoryMagGenModel = new InventoryMagGen(
|
|
|
|
magWeights,
|
|
|
|
magTemplate,
|
|
|
|
weaponTemplate,
|
|
|
|
ammoTemplate,
|
|
|
|
inventory,
|
|
|
|
);
|
|
|
|
this.inventoryMagGenComponents.find((v) => v.canHandleInventoryMagGen(inventoryMagGenModel)).process(
|
|
|
|
inventoryMagGenModel,
|
|
|
|
);
|
2023-03-03 16:23:46 +01:00
|
|
|
|
|
|
|
// Add x stacks of bullets to SecuredContainer (bots use a magic mag packing skill to reload instantly)
|
2023-11-16 02:35:05 +01:00
|
|
|
this.addAmmoToSecureContainer(
|
|
|
|
this.botConfig.secureContainerAmmoStackCount,
|
|
|
|
generatedWeaponResult.chosenAmmoTpl,
|
|
|
|
ammoTemplate._props.StackMaxSize,
|
|
|
|
inventory,
|
|
|
|
);
|
2023-03-03 16:23:46 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Add Grendaes for UBGL to bots vest and secure container
|
|
|
|
* @param weaponMods Weapon array with mods
|
|
|
|
* @param generatedWeaponResult result of weapon generation
|
|
|
|
* @param inventory bot inventory to add grenades to
|
|
|
|
*/
|
2023-11-16 02:35:05 +01:00
|
|
|
protected addUbglGrenadesToBotInventory(
|
|
|
|
weaponMods: Item[],
|
|
|
|
generatedWeaponResult: GenerateWeaponResult,
|
|
|
|
inventory: PmcInventory,
|
|
|
|
): void
|
2023-03-03 16:23:46 +01:00
|
|
|
{
|
|
|
|
// Find ubgl mod item + get details of it from db
|
2023-11-16 02:35:05 +01:00
|
|
|
const ubglMod = weaponMods.find((x) => x.slotId === "mod_launcher");
|
2023-03-03 16:23:46 +01:00
|
|
|
const ubglDbTemplate = this.itemHelper.getItem(ubglMod._tpl)[1];
|
|
|
|
|
|
|
|
// Define min/max of how many grenades bot will have
|
2023-11-16 02:35:05 +01:00
|
|
|
const ubglMinMax: GenerationData = {
|
2023-11-16 16:09:01 +01:00
|
|
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
2023-11-16 02:35:05 +01:00
|
|
|
weights: { "1": 1, "2": 1 },
|
|
|
|
whitelist: [],
|
2023-10-10 13:03:20 +02:00
|
|
|
};
|
2023-03-03 16:23:46 +01:00
|
|
|
|
|
|
|
// get ammo template from db
|
|
|
|
const ubglAmmoDbTemplate = this.itemHelper.getItem(generatedWeaponResult.chosenUbglAmmoTpl)[1];
|
|
|
|
|
|
|
|
// Add greandes to bot inventory
|
2023-11-16 02:35:05 +01:00
|
|
|
const ubglAmmoGenModel = new InventoryMagGen(
|
|
|
|
ubglMinMax,
|
|
|
|
ubglDbTemplate,
|
|
|
|
ubglDbTemplate,
|
|
|
|
ubglAmmoDbTemplate,
|
|
|
|
inventory,
|
|
|
|
);
|
|
|
|
this.inventoryMagGenComponents.find((v) => v.canHandleInventoryMagGen(ubglAmmoGenModel)).process(
|
|
|
|
ubglAmmoGenModel,
|
|
|
|
);
|
2023-03-03 16:23:46 +01:00
|
|
|
|
|
|
|
// Store extra grenades in secure container
|
2023-07-18 20:14:52 +02:00
|
|
|
this.addAmmoToSecureContainer(5, generatedWeaponResult.chosenUbglAmmoTpl, 20, inventory);
|
2023-03-03 16:23:46 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Add ammo to the secure container
|
|
|
|
* @param stackCount How many stacks of ammo to add
|
|
|
|
* @param ammoTpl Ammo type to add
|
|
|
|
* @param stackSize Size of the ammo stack to add
|
|
|
|
* @param inventory Player inventory
|
|
|
|
*/
|
2023-11-16 02:35:05 +01:00
|
|
|
protected addAmmoToSecureContainer(
|
|
|
|
stackCount: number,
|
|
|
|
ammoTpl: string,
|
|
|
|
stackSize: number,
|
|
|
|
inventory: PmcInventory,
|
|
|
|
): void
|
2023-03-03 16:23:46 +01:00
|
|
|
{
|
|
|
|
for (let i = 0; i < stackCount; i++)
|
|
|
|
{
|
|
|
|
const id = this.hashUtil.generate();
|
2023-11-16 02:35:05 +01:00
|
|
|
this.botWeaponGeneratorHelper.addItemWithChildrenToEquipmentSlot(
|
|
|
|
[EquipmentSlots.SECURED_CONTAINER],
|
|
|
|
id,
|
|
|
|
ammoTpl,
|
|
|
|
[{ _id: id, _tpl: ammoTpl, upd: { StackObjectsCount: stackSize } }],
|
|
|
|
inventory,
|
|
|
|
);
|
2023-03-03 16:23:46 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get a weapons magazine tpl from a weapon template
|
|
|
|
* @param weaponMods mods from a weapon template
|
|
|
|
* @param weaponTemplate Weapon to get magazine tpl for
|
|
|
|
* @param botRole the bot type we are getting the magazine for
|
|
|
|
* @returns magazine tpl string
|
|
|
|
*/
|
2023-11-16 02:35:05 +01:00
|
|
|
protected getMagazineTplFromWeaponTemplate(
|
|
|
|
weaponMods: Item[],
|
|
|
|
weaponTemplate: ITemplateItem,
|
|
|
|
botRole: string,
|
|
|
|
): string
|
2023-03-03 16:23:46 +01:00
|
|
|
{
|
2023-11-16 02:35:05 +01:00
|
|
|
const magazine = weaponMods.find((m) => m.slotId === this.modMagazineSlotId);
|
2023-03-03 16:23:46 +01:00
|
|
|
if (!magazine)
|
|
|
|
{
|
|
|
|
// Edge case - magazineless chamber loaded weapons dont have magazines, e.g. mp18
|
|
|
|
// return default mag tpl
|
|
|
|
if (weaponTemplate._props.ReloadMode === "OnlyBarrel")
|
|
|
|
{
|
|
|
|
return this.botWeaponGeneratorHelper.getWeaponsDefaultMagazineTpl(weaponTemplate);
|
|
|
|
}
|
|
|
|
|
|
|
|
// log error if no magazine AND not a chamber loaded weapon (e.g. shotgun revolver)
|
|
|
|
if (!weaponTemplate._props.isChamberLoad)
|
|
|
|
{
|
|
|
|
// Shouldn't happen
|
2023-11-16 02:35:05 +01:00
|
|
|
this.logger.warning(
|
2023-12-30 11:45:12 +01:00
|
|
|
this.localisationService.getText("bot-weapon_missing_magazine_or_chamber", {weaponId: weaponTemplate._id, botRole: botRole}),
|
2023-11-16 02:35:05 +01:00
|
|
|
);
|
2023-03-03 16:23:46 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
const defaultMagTplId = this.botWeaponGeneratorHelper.getWeaponsDefaultMagazineTpl(weaponTemplate);
|
2023-11-16 02:35:05 +01:00
|
|
|
this.logger.debug(
|
2023-12-30 11:45:12 +01:00
|
|
|
`[${botRole}] Unable to find magazine for weapon: ${weaponTemplate._id} ${weaponTemplate._name}, using mag template default: ${defaultMagTplId}.`,
|
2023-11-16 02:35:05 +01:00
|
|
|
);
|
2023-03-03 16:23:46 +01:00
|
|
|
|
|
|
|
return defaultMagTplId;
|
|
|
|
}
|
|
|
|
|
|
|
|
return magazine._tpl;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Finds and return a compatible ammo tpl based on the bots ammo weightings (x.json/inventory/equipment/ammo)
|
|
|
|
* @param ammo a list of ammo tpls the weapon can use
|
|
|
|
* @param weaponTemplate the weapon we want to pick ammo for
|
|
|
|
* @returns an ammo tpl that works with the desired gun
|
|
|
|
*/
|
2023-11-16 02:35:05 +01:00
|
|
|
protected getWeightedCompatibleAmmo(
|
|
|
|
ammo: Record<string, Record<string, number>>,
|
|
|
|
weaponTemplate: ITemplateItem,
|
|
|
|
): string
|
2023-03-03 16:23:46 +01:00
|
|
|
{
|
|
|
|
const desiredCaliber = this.getWeaponCaliber(weaponTemplate);
|
|
|
|
|
2023-11-18 19:46:03 +01:00
|
|
|
const compatibleCartridges = this.jsonUtil.clone(ammo[desiredCaliber]);
|
2023-03-03 16:23:46 +01:00
|
|
|
if (!compatibleCartridges || compatibleCartridges?.length === 0)
|
|
|
|
{
|
2023-11-16 02:35:05 +01:00
|
|
|
this.logger.debug(
|
|
|
|
this.localisationService.getText("bot-no_caliber_data_for_weapon_falling_back_to_default", {
|
|
|
|
weaponId: weaponTemplate._id,
|
|
|
|
weaponName: weaponTemplate._name,
|
|
|
|
defaultAmmo: weaponTemplate._props.defAmmo,
|
|
|
|
}),
|
|
|
|
);
|
2023-03-03 16:23:46 +01:00
|
|
|
|
|
|
|
// Immediately returns, as default ammo is guaranteed to be compatible
|
|
|
|
return weaponTemplate._props.defAmmo;
|
|
|
|
}
|
|
|
|
|
2023-11-18 19:46:03 +01:00
|
|
|
let chosenAmmoTpl: string;
|
|
|
|
while (!chosenAmmoTpl)
|
2023-03-03 16:23:46 +01:00
|
|
|
{
|
2023-11-18 19:46:03 +01:00
|
|
|
const possibleAmmo = this.weightedRandomHelper.getWeightedValue<string>(compatibleCartridges);
|
|
|
|
|
2023-11-29 12:35:34 +01:00
|
|
|
// Weapon has chamber but does not support cartridge
|
2023-11-18 19:46:03 +01:00
|
|
|
if (weaponTemplate._props.Chambers[0]
|
|
|
|
&& !weaponTemplate._props.Chambers[0]._props.filters[0].Filter.includes(possibleAmmo)
|
|
|
|
)
|
|
|
|
{
|
|
|
|
// Ran out of possible choices, use default ammo
|
|
|
|
if (Object.keys(compatibleCartridges).length === 0)
|
|
|
|
{
|
|
|
|
this.logger.debug(
|
|
|
|
this.localisationService.getText("bot-incompatible_ammo_for_weapon_falling_back_to_default", {
|
|
|
|
chosenAmmo: chosenAmmoTpl,
|
|
|
|
weaponId: weaponTemplate._id,
|
|
|
|
weaponName: weaponTemplate._name,
|
|
|
|
defaultAmmo: weaponTemplate._props.defAmmo,
|
|
|
|
}),
|
|
|
|
);
|
2023-03-03 16:23:46 +01:00
|
|
|
|
2023-11-18 19:46:03 +01:00
|
|
|
// Set ammo to default and exit
|
|
|
|
chosenAmmoTpl = weaponTemplate._props.defAmmo;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Not compatible, remove item from possible list and try again
|
|
|
|
delete compatibleCartridges[possibleAmmo];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Compatible ammo found
|
|
|
|
chosenAmmoTpl = possibleAmmo;
|
|
|
|
break;
|
|
|
|
}
|
2023-03-03 16:23:46 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return chosenAmmoTpl;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get a weapons compatible cartridge caliber
|
|
|
|
* @param weaponTemplate Weapon to look up caliber of
|
|
|
|
* @returns caliber as string
|
|
|
|
*/
|
|
|
|
protected getWeaponCaliber(weaponTemplate: ITemplateItem): string
|
|
|
|
{
|
|
|
|
if (weaponTemplate._props.Caliber)
|
|
|
|
{
|
|
|
|
return weaponTemplate._props.Caliber;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (weaponTemplate._props.ammoCaliber)
|
|
|
|
{
|
2024-01-09 16:31:56 +01:00
|
|
|
// 9x18pmm has a typo, should be Caliber9x18PM
|
|
|
|
return weaponTemplate._props.ammoCaliber === "Caliber9x18PMM"
|
|
|
|
? "Caliber9x18PM"
|
|
|
|
: weaponTemplate._props.ammoCaliber;
|
2023-03-03 16:23:46 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (weaponTemplate._props.LinkedWeapon)
|
|
|
|
{
|
2023-11-16 02:35:05 +01:00
|
|
|
const ammoInChamber = this.itemHelper.getItem(
|
|
|
|
weaponTemplate._props.Chambers[0]._props.filters[0].Filter[0],
|
|
|
|
);
|
2023-10-10 13:03:20 +02:00
|
|
|
if (!ammoInChamber[0])
|
2023-03-03 16:23:46 +01:00
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-10-10 13:03:20 +02:00
|
|
|
return ammoInChamber[1]._props.Caliber;
|
2023-03-03 16:23:46 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Fill existing magazines to full, while replacing their contents with specified ammo
|
2023-03-03 18:53:28 +01:00
|
|
|
* @param weaponMods Weapon with children
|
|
|
|
* @param magazine Magazine item
|
|
|
|
* @param cartridgeTpl Cartridge to insert into magazine
|
2023-03-03 16:23:46 +01:00
|
|
|
*/
|
2023-03-03 18:53:28 +01:00
|
|
|
protected fillExistingMagazines(weaponMods: Item[], magazine: Item, cartridgeTpl: string): void
|
2023-03-03 16:23:46 +01:00
|
|
|
{
|
2023-03-03 18:53:28 +01:00
|
|
|
const magazineTemplate = this.itemHelper.getItem(magazine._tpl)[1];
|
|
|
|
if (!magazineTemplate)
|
2023-03-03 16:23:46 +01:00
|
|
|
{
|
|
|
|
this.logger.error(this.localisationService.getText("bot-unable_to_find_magazine_item", magazine._tpl));
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
2023-07-24 22:24:55 +02:00
|
|
|
// Magazine, usually
|
2023-03-03 18:53:28 +01:00
|
|
|
const parentItem = this.itemHelper.getItem(magazineTemplate._parent)[1];
|
2023-03-03 16:23:46 +01:00
|
|
|
|
|
|
|
// the revolver shotgun uses a magazine with chambers, not cartridges ("camora_xxx")
|
|
|
|
// Exchange of the camora ammo is not necessary we could also just check for stackSize > 0 here
|
|
|
|
// and remove the else
|
|
|
|
if (this.botWeaponGeneratorHelper.magazineIsCylinderRelated(parentItem._name))
|
|
|
|
{
|
2023-03-03 18:53:28 +01:00
|
|
|
this.fillCamorasWithAmmo(weaponMods, magazine._id, cartridgeTpl);
|
2023-03-03 16:23:46 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2023-03-03 18:53:28 +01:00
|
|
|
this.addOrUpdateMagazinesChildWithAmmo(weaponMods, magazine, cartridgeTpl, magazineTemplate);
|
2023-03-03 16:23:46 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Add desired ammo tpl as item to weaponmods array, placed as child to UBGL
|
2023-03-03 18:53:28 +01:00
|
|
|
* @param weaponMods Weapon with children
|
|
|
|
* @param ubglMod UBGL item
|
|
|
|
* @param ubglAmmoTpl Grenade ammo tpl
|
2023-03-03 16:23:46 +01:00
|
|
|
*/
|
|
|
|
protected fillUbgl(weaponMods: Item[], ubglMod: Item, ubglAmmoTpl: string): void
|
|
|
|
{
|
2023-11-16 02:35:05 +01:00
|
|
|
weaponMods.push({
|
|
|
|
_id: this.hashUtil.generate(),
|
|
|
|
_tpl: ubglAmmoTpl,
|
|
|
|
parentId: ubglMod._id,
|
|
|
|
slotId: "patron_in_weapon",
|
|
|
|
upd: { StackObjectsCount: 1 },
|
|
|
|
});
|
2023-03-03 16:23:46 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Add cartridge item to weapon Item array, if it already exists, update
|
2023-03-03 18:53:28 +01:00
|
|
|
* @param weaponWithMods Weapon items array to amend
|
2023-03-03 16:23:46 +01:00
|
|
|
* @param magazine magazine item details we're adding cartridges to
|
2023-03-03 18:53:28 +01:00
|
|
|
* @param chosenAmmoTpl cartridge to put into the magazine
|
2023-03-03 16:23:46 +01:00
|
|
|
* @param newStackSize how many cartridges should go into the magazine
|
2023-03-03 18:53:28 +01:00
|
|
|
* @param magazineTemplate magazines db template
|
2023-03-03 16:23:46 +01:00
|
|
|
*/
|
2023-11-16 02:35:05 +01:00
|
|
|
protected addOrUpdateMagazinesChildWithAmmo(
|
|
|
|
weaponWithMods: Item[],
|
|
|
|
magazine: Item,
|
|
|
|
chosenAmmoTpl: string,
|
|
|
|
magazineTemplate: ITemplateItem,
|
|
|
|
): void
|
2023-03-03 16:23:46 +01:00
|
|
|
{
|
2023-11-16 02:35:05 +01:00
|
|
|
const magazineCartridgeChildItem = weaponWithMods.find((m) =>
|
|
|
|
m.parentId === magazine._id && m.slotId === "cartridges"
|
|
|
|
);
|
2023-03-03 18:53:28 +01:00
|
|
|
if (magazineCartridgeChildItem)
|
2023-03-03 16:23:46 +01:00
|
|
|
{
|
2023-07-24 22:24:55 +02:00
|
|
|
// Delete the existing cartridge object and create fresh below
|
|
|
|
weaponWithMods.splice(weaponWithMods.indexOf(magazineCartridgeChildItem), 1);
|
2023-03-03 16:23:46 +01:00
|
|
|
}
|
2023-03-03 18:53:28 +01:00
|
|
|
|
|
|
|
// Create array with just magazine
|
|
|
|
const magazineWithCartridges = [magazine];
|
|
|
|
|
|
|
|
// Add full cartridge child items to above array
|
|
|
|
this.itemHelper.fillMagazineWithCartridge(magazineWithCartridges, magazineTemplate, chosenAmmoTpl, 1);
|
|
|
|
|
|
|
|
// Replace existing magazine with above array of mag + cartridge stacks
|
|
|
|
weaponWithMods.splice(weaponWithMods.indexOf(magazine), 1, ...magazineWithCartridges);
|
2023-03-03 16:23:46 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Fill each Camora with a bullet
|
|
|
|
* @param weaponMods Weapon mods to find and update camora mod(s) from
|
|
|
|
* @param magazineId magazine id to find and add to
|
|
|
|
* @param ammoTpl ammo template id to hydate with
|
|
|
|
*/
|
|
|
|
protected fillCamorasWithAmmo(weaponMods: Item[], magazineId: string, ammoTpl: string): void
|
|
|
|
{
|
|
|
|
// for CylinderMagazine we exchange the ammo in the "camoras".
|
|
|
|
// This might not be necessary since we already filled the camoras with a random whitelisted and compatible ammo type,
|
|
|
|
// but I'm not sure whether this is also used elsewhere
|
2023-11-16 02:35:05 +01:00
|
|
|
const camoras = weaponMods.filter((x) => x.parentId === magazineId && x.slotId.startsWith("camora"));
|
2023-03-03 16:23:46 +01:00
|
|
|
for (const camora of camoras)
|
|
|
|
{
|
|
|
|
camora._tpl = ammoTpl;
|
2023-07-24 22:44:00 +02:00
|
|
|
if (camora.upd)
|
|
|
|
{
|
|
|
|
camora.upd.StackObjectsCount = 1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
camora.upd = { StackObjectsCount: 1 };
|
|
|
|
}
|
2023-03-03 16:23:46 +01:00
|
|
|
}
|
|
|
|
}
|
2023-11-16 02:35:05 +01:00
|
|
|
}
|