Updated Progressive Bot System

This commit is contained in:
2025-01-07 19:03:11 -05:00
parent 79c3c5eec2
commit 7ff0b39d88
151 changed files with 600497 additions and 14180 deletions
+1 -1
View File
@@ -1,7 +1,7 @@
[General]
gameName=spt
modid=0
version=d2025.1.3.0
version=d2025.1.7.0
newestVersion=
category="1,"
nexusFileStatus=1
@@ -1,6 +1,6 @@
{
"name": "Acids Progressive Bot System",
"version": "1.3.1",
"version": "1.4.0-alpha",
"sptVersion": "~3.10",
"loadBefore": [],
"loadAfter": [
@@ -558,6 +558,7 @@
"56e335e4d2720b6c058b456d": 20,
"5ca20d5986f774331e7c9602": 10,
"5e9dcf5986f7746c417435b3": 10,
"59e763f286f7742ee57895da": 15,
"6038d614d10cbf667352dd44": 1,
"6034d103ca006d2dca39b3f0": 1,
"656ddcf0f02d7bcea90bf395": 1,
@@ -614,6 +614,7 @@
"56e335e4d2720b6c058b456d": 20,
"5ca20d5986f774331e7c9602": 10,
"5e9dcf5986f7746c417435b3": 10,
"59e763f286f7742ee57895da": 15,
"6038d614d10cbf667352dd44": 1,
"6034d103ca006d2dca39b3f0": 1,
"656ddcf0f02d7bcea90bf395": 1,
@@ -866,6 +866,7 @@
"56e335e4d2720b6c058b456d": 20,
"5ca20d5986f774331e7c9602": 10,
"5e9dcf5986f7746c417435b3": 10,
"59e763f286f7742ee57895da": 15,
"6038d614d10cbf667352dd44": 1,
"6034d103ca006d2dca39b3f0": 1,
"656ddcf0f02d7bcea90bf395": 1,
@@ -877,7 +878,6 @@
"5c0e805e86f774683f3dd637": 1,
"5ab8ebf186f7742d8b372e80": 1,
"639346cc1c8f182ad90c8972": 1,
"59e763f286f7742ee57895da": 1,
"5b44c6ae86f7742d1627baea": 1,
"5d5d940f86f7742797262046": 1,
"5df8a4d786f77412672a1e3b": 1,
@@ -798,6 +798,7 @@
"56e335e4d2720b6c058b456d": 20,
"5ca20d5986f774331e7c9602": 10,
"5e9dcf5986f7746c417435b3": 10,
"59e763f286f7742ee57895da": 15,
"6038d614d10cbf667352dd44": 1,
"6034d103ca006d2dca39b3f0": 1,
"656ddcf0f02d7bcea90bf395": 1,
@@ -809,7 +810,6 @@
"5c0e805e86f774683f3dd637": 1,
"5ab8ebf186f7742d8b372e80": 1,
"639346cc1c8f182ad90c8972": 1,
"59e763f286f7742ee57895da": 1,
"5b44c6ae86f7742d1627baea": 1,
"5d5d940f86f7742797262046": 1,
"5df8a4d786f77412672a1e3b": 1,
@@ -784,6 +784,7 @@
"56e335e4d2720b6c058b456d": 20,
"5ca20d5986f774331e7c9602": 10,
"5e9dcf5986f7746c417435b3": 10,
"59e763f286f7742ee57895da": 15,
"6038d614d10cbf667352dd44": 1,
"6034d103ca006d2dca39b3f0": 1,
"656ddcf0f02d7bcea90bf395": 1,
@@ -795,7 +796,6 @@
"5c0e805e86f774683f3dd637": 1,
"5ab8ebf186f7742d8b372e80": 1,
"639346cc1c8f182ad90c8972": 1,
"59e763f286f7742ee57895da": 1,
"5b44c6ae86f7742d1627baea": 1,
"5d5d940f86f7742797262046": 1,
"5df8a4d786f77412672a1e3b": 1,
@@ -764,6 +764,7 @@
"56e335e4d2720b6c058b456d": 20,
"5ca20d5986f774331e7c9602": 10,
"5e9dcf5986f7746c417435b3": 10,
"59e763f286f7742ee57895da": 15,
"6038d614d10cbf667352dd44": 1,
"6034d103ca006d2dca39b3f0": 1,
"656ddcf0f02d7bcea90bf395": 1,
@@ -775,7 +776,6 @@
"5c0e805e86f774683f3dd637": 1,
"5ab8ebf186f7742d8b372e80": 1,
"639346cc1c8f182ad90c8972": 1,
"59e763f286f7742ee57895da": 1,
"5b44c6ae86f7742d1627baea": 1,
"5d5d940f86f7742797262046": 1,
"5df8a4d786f77412672a1e3b": 1,
@@ -23,6 +23,33 @@ export class BotConfigs
protected botConfig: IBotConfig;
protected pmcConfig: IPmcConfig;
private pmcLimitedCategories = {
"5448e8d04bdc2ddf718b4569": 1,
"5448e8d64bdc2dce718b4568": 1,
"5448f39d4bdc2d0a728b4568": 1,
"5448f3a64bdc2d60728b456a": 2,
"5448f3ac4bdc2dce718b4569": 1,
"5448f3a14bdc2d27728b4569": 1,
"5c99f98d86f7745c314214b3": 1,
"5c164d2286f774194c5e69fa": 1,
"550aa4cd4bdc2dd8348b456c": 2,
"55818add4bdc2d5b648b456f": 1,
"55818ad54bdc2ddc698b4569": 1,
"55818aeb4bdc2ddc698b456a": 1,
"55818ae44bdc2dde698b456c": 1,
"55818af64bdc2d5b648b4570": 1,
"5448e54d4bdc2dcc718b4568": 1,
"5447e1d04bdc2dff2f8b4567": 1,
"5a341c4686f77469e155819e": 1,
"55818b164bdc2ddc698b456c": 2,
"5448bc234bdc2d3c308b4569": 2,
"543be5dd4bdc2deb348b4569": 1,
"543be5cb4bdc2deb348b4568": 2,
"5485a8684bdc2da71d8b4567": 2,
"5d650c3e815116009f6201d2": 2,
"543be6564bdc2df4348b4568": 1
}
constructor(
@inject("IDatabaseTables") protected tables: IDatabaseTables,
@inject("DatabaseService") protected database: DatabaseService,
@@ -40,25 +67,33 @@ export class BotConfigs
public initialize(): void
{
if (!ModConfig.config.disablePMCTierGeneration)
{
this.setPMCItemLimits();
this.setPMCLoot();
this.setPMCScopeWhitelist();
this.setPMCSlotIDsToMakeRequired();
if (ModConfig.config.gameVersionWeight) this.setPMCGameVersionWeights();
}
if (!ModConfig.config.disableScavTierGeneration)
{
if (ModConfig.config.addAllKeysToScavs || ModConfig.config.addOnlyKeyCardsToScavs || ModConfig.config.addOnlyMechanicalKeysToScavs) this.pushScavKeys();
if (!ModConfig.config.scavLoot) this.removeScavLoot();
}
this.clearNoLongerNeededBotDetails();
this.configureBotExperienceLevels();
this.configurePlateWeightings();
this.configureWeaponDurability();
this.adjustNVG();
this.setLootItemResourceRandomization();
this.setPMCItemLimits();
this.setPMCLoot();
this.setPMCScopeWhitelist();
this.setPMCSlotIDsToMakeRequired();
this.removeThermalGoggles(ModConfig.config.enableT7Thermals);
if (ModConfig.config.gameVersionWeight) this.setPMCGameVersionWeights();
if (ModConfig.config.addAllKeysToScavs || ModConfig.config.addOnlyKeyCardsToScavs || ModConfig.config.addOnlyMechanicalKeysToScavs) this.pushScavKeys();
if (ModConfig.config.enableCustomPlateChances) this.setPlateChances();
if (ModConfig.config.forceStock) this.setForceStock();
if (ModConfig.config.forceDustCover) this.setForceDustCover();
if (ModConfig.config.forceScopeSlot) this.setForceScopes();
if (ModConfig.config.forceWeaponModLimits) this.setWeaponModLimits();
if (!ModConfig.config.scavLoot) this.removeScavLoot();
if (ModConfig.config.enableScavEqualEquipmentTiering) this.setIdenticalScavWeights();
if (ModConfig.config.enableCustomLevelDeltas) this.setLevelDeltas();
if (ModConfig.config.enableScavCustomLevelDeltas) this.setScavLevelDeltas();
@@ -319,21 +354,24 @@ export class BotConfigs
private setPMCItemLimits(): void
{
this.botConfig.itemSpawnLimits.pmc["60098ad7c2240c0fe85c570a"] = 1;
this.botConfig.itemSpawnLimits.pmc["590c678286f77426c9660122"] = 1;
this.botConfig.itemSpawnLimits.pmc["5e831507ea0a7c419c2f9bd9"] = 1;
this.botConfig.itemSpawnLimits.pmc["590c661e86f7741e566b646a"] = 1;
this.botConfig.itemSpawnLimits.pmc["544fb45d4bdc2dee738b4568"] = 1;
this.botConfig.itemSpawnLimits.pmc["5e8488fa988a8701445df1e4"] = 1;
this.botConfig.itemSpawnLimits.pmc["544fb37f4bdc2dee738b4567"] = 1;
this.botConfig.itemSpawnLimits.pmc["5448e8d04bdc2ddf718b4569"] = 1;
this.botConfig.itemSpawnLimits.pmc["5448e8d64bdc2dce718b4568"] = 1;
// Clear PMC item limits
this.botConfig.itemSpawnLimits.pmc = {}
// Go through custom limits and add them
for (const [item, count] of Object.entries(this.pmcLimitedCategories))
{
this.botConfig.itemSpawnLimits.pmc[item] = count;
}
}
private setPMCLoot(): void
{
const allBots = this.database.getTables().bots.types;
this.pmcConfig.looseWeaponInBackpackLootMinMax.min = 0;
this.pmcConfig.looseWeaponInBackpackLootMinMax.max = 0;
this.botConfig.equipment.pmc.randomisation = [];
if (ModConfig.config.pmcLoot)
{
if (ModConfig.config.pmcLootBlacklistItems.length > 0)
@@ -345,77 +383,22 @@ export class BotConfigs
this.pmcConfig.pocketLoot.blacklist.push(item);
}
}
for (const level in this.tierInformation.lootRandomization)
{
this.tierInformation.lootRandomization[level].generation.backpackLoot.weights["0"] = 1
this.tierInformation.lootRandomization[level].generation.backpackLoot.weights["3"] = 2
this.tierInformation.lootRandomization[level].generation.backpackLoot.weights["5"] = 5
this.tierInformation.lootRandomization[level].generation.backpackLoot.weights["8"] = 6
this.tierInformation.lootRandomization[level].generation.backpackLoot.weights["10"] = 5
this.tierInformation.lootRandomization[level].generation.backpackLoot.weights["12"] = 4
this.tierInformation.lootRandomization[level].generation.backpackLoot.weights["15"] = 4
this.tierInformation.lootRandomization[level].generation.backpackLoot.weights["20"] = 3
this.tierInformation.lootRandomization[level].generation.backpackLoot.weights["23"] = 1
this.tierInformation.lootRandomization[level].generation.pocketLoot.weights["0"] = 1
this.tierInformation.lootRandomization[level].generation.pocketLoot.weights["1"] = 3
this.tierInformation.lootRandomization[level].generation.pocketLoot.weights["2"] = 4
this.tierInformation.lootRandomization[level].generation.pocketLoot.weights["3"] = 2
this.tierInformation.lootRandomization[level].generation.pocketLoot.weights["4"] = 1
this.tierInformation.lootRandomization[level].generation.vestLoot.weights["0"] = 1
this.tierInformation.lootRandomization[level].generation.vestLoot.weights["1"] = 2
this.tierInformation.lootRandomization[level].generation.vestLoot.weights["2"] = 3
this.tierInformation.lootRandomization[level].generation.vestLoot.weights["3"] = 2
this.tierInformation.lootRandomization[level].generation.vestLoot.weights["4"] = 1
}
for (const tierObject in this.tierInformation.tiers)
{
const tierNumber = this.tierInformation.tiers[tierObject].tier
const tierJson = this.apbsEquipmentGetter.getTierChancesJson(tierNumber);
tierJson.pmcUSEC.chances.generation.items.backpackLoot.weights["0"] = 4
tierJson.pmcUSEC.chances.generation.items.backpackLoot.weights["1"] = 15
tierJson.pmcUSEC.chances.generation.items.backpackLoot.weights["2"] = 40
tierJson.pmcUSEC.chances.generation.items.backpackLoot.weights["3"] = 10
tierJson.pmcUSEC.chances.generation.items.backpackLoot.weights["4"] = 8
tierJson.pmcUSEC.chances.generation.items.backpackLoot.weights["5"] = 2
tierJson.pmcUSEC.chances.generation.items.backpackLoot.weights["10"] = 1
tierJson.pmcBEAR.chances.generation.items.backpackLoot.weights["0"] = 4
tierJson.pmcBEAR.chances.generation.items.backpackLoot.weights["1"] = 15
tierJson.pmcBEAR.chances.generation.items.backpackLoot.weights["2"] = 40
tierJson.pmcBEAR.chances.generation.items.backpackLoot.weights["3"] = 10
tierJson.pmcBEAR.chances.generation.items.backpackLoot.weights["4"] = 8
tierJson.pmcBEAR.chances.generation.items.backpackLoot.weights["5"] = 2
tierJson.pmcBEAR.chances.generation.items.backpackLoot.weights["10"] = 1
tierJson.pmcUSEC.chances.generation.items.pocketLoot.weights["0"] = 4
tierJson.pmcUSEC.chances.generation.items.pocketLoot.weights["1"] = 9
tierJson.pmcUSEC.chances.generation.items.pocketLoot.weights["2"] = 1
tierJson.pmcUSEC.chances.generation.items.pocketLoot.weights["3"] = 1
tierJson.pmcBEAR.chances.generation.items.pocketLoot.weights["0"] = 4
tierJson.pmcBEAR.chances.generation.items.pocketLoot.weights["1"] = 9
tierJson.pmcBEAR.chances.generation.items.pocketLoot.weights["2"] = 1
tierJson.pmcBEAR.chances.generation.items.pocketLoot.weights["3"] = 1
tierJson.pmcUSEC.chances.generation.items.vestLoot.weights["0"] = 2
tierJson.pmcUSEC.chances.generation.items.vestLoot.weights["1"] = 12
tierJson.pmcUSEC.chances.generation.items.vestLoot.weights["2"] = 1
tierJson.pmcUSEC.chances.generation.items.vestLoot.weights["3"] = 1
tierJson.pmcUSEC.chances.generation.items.vestLoot.weights["4"] = 1
tierJson.pmcBEAR.chances.generation.items.vestLoot.weights["0"] = 2
tierJson.pmcBEAR.chances.generation.items.vestLoot.weights["1"] = 12
tierJson.pmcBEAR.chances.generation.items.vestLoot.weights["2"] = 1
tierJson.pmcBEAR.chances.generation.items.vestLoot.weights["3"] = 1
tierJson.pmcBEAR.chances.generation.items.vestLoot.weights["4"] = 1
}
this.botConfig.equipment.pmc.randomisation = this.tierInformation.lootRandomization;
}
else
if (!ModConfig.config.pmcLoot)
{
this.botConfig.disableLootOnBotTypes.push("pmcusec", "pmcbear")
}
for (const botType in allBots)
{
this.botConfig.equipment.pmc.randomisation = this.tierInformation.lootRandomization;
if (botType == "pmcbear" || botType == "pmcusec")
{
allBots[botType].inventory.items.Backpack = {};
allBots[botType].inventory.items.Pockets = {};
allBots[botType].inventory.items.TacticalVest = {};
allBots[botType].inventory.items.SpecialLoot = {};
}
}
}
private setPMCScopeWhitelist(): void
@@ -514,12 +497,7 @@ export class BotConfigs
private removeScavLoot(): void
{
this.tables.bots.types.assault.inventory.items.Backpack = {}
this.tables.bots.types.assault.inventory.items.Pockets = {}
this.tables.bots.types.assault.inventory.items.TacticalVest = {}
this.tables.bots.types.marksman.inventory.items.Backpack = {}
this.tables.bots.types.marksman.inventory.items.Pockets = {}
this.tables.bots.types.marksman.inventory.items.TacticalVest = {}
this.botConfig.disableLootOnBotTypes.push("assault", "marksman");
}
private setIdenticalScavWeights(): void
@@ -40,6 +40,8 @@ import { RaidInformation } from "../Globals/RaidInformation";
import { ModInformation } from "../Globals/ModInformation";
import { APBSTester } from "../Utils/APBSTester";
import { vanillaButtpads } from "../Globals/VanillaItemLists";
import { APBSLogger } from "../Utils/APBSLogger";
import { Logging } from "../Enums/Logging";
/** Handle profile related client events */
@injectable()
@@ -70,7 +72,8 @@ export class APBSBotEquipmentModGenerator extends BotEquipmentModGenerator
@inject("APBSTierGetter") protected apbsTierGetter: APBSTierGetter,
@inject("RaidInformation") protected raidInformation: RaidInformation,
@inject("ModInformation") protected modInformation: ModInformation,
@inject("APBSTester") protected apbsTester: APBSTester
@inject("APBSTester") protected apbsTester: APBSTester,
@inject("APBSLogger") protected apbsLogger: APBSLogger
)
{
super(logger,
@@ -101,43 +104,14 @@ export class APBSBotEquipmentModGenerator extends BotEquipmentModGenerator
const botRole = settings.botData.role;
const tier = this.apbsTierGetter.getTierByLevel(settings.botData.level);
const tieredModPool = this.apbsEquipmentGetter.getModsByBotRole(botRole, tier)
let spawnChances = this.apbsEquipmentGetter.getSpawnChancesByBotRole(botRole, tier);
let compatibleModsPool = tieredModPool[parentTemplate._id]
let actualModPool = tieredModPool;
let spawnChances = this.apbsEquipmentGetter.getSpawnChancesByBotRole(botRole, tier);
// Roll weapon spawns (primary/secondary/holster) and generate a weapon for each roll that passed
if ((ModConfig.config.disableBossTierGeneration && (botRole.includes("boss") || botRole.includes("sectant") || botRole.includes("arena"))) || botRole == "bosslegion" || botRole == "bosspunisher")
{
spawnChances = settings.spawnChances;
compatibleModsPool = settings.modPool[parentTemplate._id];
actualModPool = settings.modPool;
}
if (ModConfig.config.disableBossFollowerTierGeneration && botRole.includes("follower"))
{
spawnChances = settings.spawnChances;
compatibleModsPool = settings.modPool[parentTemplate._id];
actualModPool = settings.modPool;
}
if (ModConfig.config.disableRaiderRogueTierGeneration && (botRole.includes("exusec") || botRole.includes("pmcbot")))
{
spawnChances = settings.spawnChances;
compatibleModsPool = settings.modPool[parentTemplate._id];
actualModPool = settings.modPool;
}
if (ModConfig.config.disablePMCTierGeneration && (botRole.includes("pmcusec") || botRole.includes("pmcbear")))
{
spawnChances = settings.spawnChances;
compatibleModsPool = settings.modPool[parentTemplate._id];
actualModPool = settings.modPool;
}
if (ModConfig.config.disableScavTierGeneration && (botRole.includes("assault") || botRole.includes("marksman")))
{
spawnChances = settings.spawnChances;
compatibleModsPool = settings.modPool[parentTemplate._id];
actualModPool = settings.modPool;
}
if (botRole.includes("infected") || botRole.includes("spirit") || botRole.includes("skier") || botRole.includes("peacemaker") || botRole.includes("gifter"))
if (!this.raidInformation.isBotEnabled(botRole))
{
this.apbsLogger.log(Logging.DEBUG, `${botRole} is disabled - TRACE: generateModsForEquipment`);
spawnChances = settings.spawnChances;
compatibleModsPool = settings.modPool[parentTemplate._id];
actualModPool = settings.modPool;
@@ -31,6 +31,9 @@ import { ProfileHelper } from "@spt/helpers/ProfileHelper";
import { WeatherHelper } from "@spt/helpers/WeatherHelper";
import { BotEquipmentFilterService } from "@spt/services/BotEquipmentFilterService";
import { IGetRaidConfigurationRequestData } from "@spt/models/eft/match/IGetRaidConfigurationRequestData";
import { RaidInformation } from "../Globals/RaidInformation";
import { APBSLogger } from "../Utils/APBSLogger";
import { Logging } from "../Enums/Logging";
/** Handle profile related client events */
@injectable()
@@ -57,7 +60,9 @@ export class APBSBotInventoryGenerator extends BotInventoryGenerator
@inject("ConfigServer") protected configServer: ConfigServer,
@inject("APBSEquipmentGetter") protected apbsEquipmentGetter: APBSEquipmentGetter,
@inject("APBSTierGetter") protected apbsTierGetter: APBSTierGetter,
@inject("APBSBotWeaponGenerator") protected apbsBotWeaponGenerator: APBSBotWeaponGenerator
@inject("APBSBotWeaponGenerator") protected apbsBotWeaponGenerator: APBSBotWeaponGenerator,
@inject("RaidInformation") protected raidInformation: RaidInformation,
@inject("APBSLogger") protected apbsLogger: APBSLogger
)
{
super(logger,
@@ -90,8 +95,8 @@ export class APBSBotInventoryGenerator extends BotInventoryGenerator
): PmcInventory
{
const templateInventory = botJsonTemplate.inventory;
let wornItemChances = botJsonTemplate.chances;
const itemGenerationLimitsMinMax = botJsonTemplate.generation;
const wornItemChances = botJsonTemplate.chances;
const itemGenerationLimitsMinMax: IGeneration = botJsonTemplate.generation;
// Generate base inventory with no items
const botInventory = this.generateInventoryBase();
@@ -110,55 +115,22 @@ export class APBSBotInventoryGenerator extends BotInventoryGenerator
chosenGameVersion,
raidConfig
);
// Roll weapon spawns (primary/secondary/holster) and generate a weapon for each roll that passed
if (((botRole.includes("boss") || botRole.includes("sectant") || botRole.includes("arena")) && ModConfig.config.disableBossTierGeneration) || botRole == "bosslegion" || botRole == "bosspunisher")
{
this.generateAndAddWeaponsToBot(templateInventory, wornItemChances, sessionId, botInventory, botRole, isPmc, itemGenerationLimitsMinMax, botLevel);
this.botLootGenerator.generateLoot(sessionId, botJsonTemplate, isPmc, botRole, botInventory, botLevel);
return botInventory;
}
if (botRole.includes("follower") && ModConfig.config.disableBossFollowerTierGeneration)
{
this.generateAndAddWeaponsToBot(templateInventory, wornItemChances, sessionId, botInventory, botRole, isPmc, itemGenerationLimitsMinMax, botLevel);
this.botLootGenerator.generateLoot(sessionId, botJsonTemplate, isPmc, botRole, botInventory, botLevel);
return botInventory;
}
if ((botRole.includes("exusec") || botRole.includes("pmcbot")) && !ModConfig.config.disableRaiderRogueTierGeneration)
{
this.generateAndAddWeaponsToBot(templateInventory, wornItemChances, sessionId, botInventory, botRole, isPmc, itemGenerationLimitsMinMax, botLevel);
this.botLootGenerator.generateLoot(sessionId, botJsonTemplate, isPmc, botRole, botInventory, botLevel);
return botInventory;
}
if (botRole.includes("pmc") && ModConfig.config.disablePMCTierGeneration)
{
this.generateAndAddWeaponsToBot(templateInventory, wornItemChances, sessionId, botInventory, botRole, isPmc, itemGenerationLimitsMinMax, botLevel);
this.botLootGenerator.generateLoot(sessionId, botJsonTemplate, isPmc, botRole, botInventory, botLevel);
return botInventory;
}
if ((botRole.includes("assault") || botRole.includes("marksman")) && ModConfig.config.disableScavTierGeneration)
{
this.generateAndAddWeaponsToBot(templateInventory, wornItemChances, sessionId, botInventory, botRole, isPmc, itemGenerationLimitsMinMax, botLevel);
this.botLootGenerator.generateLoot(sessionId, botJsonTemplate, isPmc, botRole, botInventory, botLevel);
return botInventory;
}
if (botRole.includes("infected") || botRole.includes("spirit") || botRole.includes("skier") || botRole.includes("peacemaker") || botRole.includes("gifter"))
if (!this.raidInformation.isBotEnabled(botRole))
{
this.apbsLogger.log(Logging.DEBUG, `${botRole} is disabled - TRACE: generateInventory`);
this.generateAndAddWeaponsToBot(templateInventory, wornItemChances, sessionId, botInventory, botRole, isPmc, itemGenerationLimitsMinMax, botLevel);
this.botLootGenerator.generateLoot(sessionId, botJsonTemplate, isPmc, botRole, botInventory, botLevel);
return botInventory;
}
// APBS generation chances instead
const tierInfo = this.apbsTierGetter.getTierByLevel(botLevel);
wornItemChances = this.apbsEquipmentGetter.getSpawnChancesByBotRole(botRole, tierInfo);
this.generateAndAddWeaponsToBot(templateInventory, wornItemChances, sessionId, botInventory, botRole, isPmc, itemGenerationLimitsMinMax, botLevel);
const chances = this.apbsEquipmentGetter.getSpawnChancesByBotRole(botRole, tierInfo);
const generation = chances.generation;
this.generateAndAddWeaponsToBot(templateInventory, chances, sessionId, botInventory, botRole, isPmc, generation, botLevel);
this.botLootGenerator.generateLoot(sessionId, botJsonTemplate, isPmc, botRole, botInventory, botLevel);
return botInventory;
@@ -178,48 +150,9 @@ export class APBSBotInventoryGenerator extends BotInventoryGenerator
let modPool = this.apbsEquipmentGetter.getModsByBotRole(botRole, tierInfo);
let apbsBot = true;
if ((ModConfig.config.disableBossTierGeneration && (botRole.includes("boss") || botRole.includes("sectant") || botRole.includes("arena"))) || botRole == "bosslegion" || botRole == "bosspunisher")
{
equipmentPool = settings.rootEquipmentPool;
randomisationDetails = settings.randomisationDetails;
wornItemChances = settings.spawnChances;
modPool = settings.modPool;
apbsBot = false;
}
if (ModConfig.config.disableBossFollowerTierGeneration && botRole.includes("follower"))
{
equipmentPool = settings.rootEquipmentPool;
randomisationDetails = settings.randomisationDetails;
wornItemChances = settings.spawnChances;
modPool = settings.modPool;
apbsBot = false;
}
if (ModConfig.config.disableRaiderRogueTierGeneration && (botRole.includes("exusec") || botRole.includes("pmcbot")))
{
equipmentPool = settings.rootEquipmentPool;
randomisationDetails = settings.randomisationDetails;
wornItemChances = settings.spawnChances;
modPool = settings.modPool;
apbsBot = false;
}
if (ModConfig.config.disablePMCTierGeneration && (botRole.includes("pmcusec") || botRole.includes("pmcbear")))
{
equipmentPool = settings.rootEquipmentPool;
randomisationDetails = settings.randomisationDetails;
wornItemChances = settings.spawnChances;
modPool = settings.modPool;
apbsBot = false;
}
if (ModConfig.config.disableScavTierGeneration && (botRole.includes("assault") || botRole.includes("marksman")))
{
equipmentPool = settings.rootEquipmentPool;
randomisationDetails = settings.randomisationDetails;
wornItemChances = settings.spawnChances;
modPool = settings.modPool;
apbsBot = false;
}
if (botRole.includes("infected") || botRole.includes("spirit") || botRole.includes("skier") || botRole.includes("peacemaker") || botRole.includes("gifter"))
if (!this.raidInformation.isBotEnabled(botRole))
{
this.apbsLogger.log(Logging.DEBUG, `${botRole} is disabled - TRACE: generateEquipment`);
equipmentPool = settings.rootEquipmentPool;
randomisationDetails = settings.randomisationDetails;
wornItemChances = settings.spawnChances;
@@ -0,0 +1,475 @@
import { inject, injectable } from "tsyringe";
import { ItemHelper } from "@spt/helpers/ItemHelper";
import { IBotType, IGenerationWeightingItems } from "@spt/models/eft/common/tables/IBotType";
import { APBSEquipmentGetter } from "../Utils/APBSEquipmentGetter";
import { APBSTierGetter } from "../Utils/APBSTierGetter";
import { RagfairPriceService } from "@spt/services/RagfairPriceService";
import { PMCLootGenerator } from "@spt/generators/PMCLootGenerator";
import { BotLootCacheService } from "@spt/services/BotLootCacheService";
import { ILogger } from "@spt/models/spt/utils/ILogger";
import { DatabaseServer } from "@spt/servers/DatabaseServer";
import { LocalisationService } from "@spt/services/LocalisationService";
import { ICloner } from "@spt/utils/cloners/ICloner";
import { BaseClasses } from "@spt/models/enums/BaseClasses";
import { IBotLootCache, LootCacheType } from "@spt/models/spt/bots/IBotLootCache";
import { RaidInformation } from "../Globals/RaidInformation";
import { APBSLogger } from "../Utils/APBSLogger";
import { Logging } from "../Enums/Logging";
@injectable()
export class APBSBotLootCacheService extends BotLootCacheService
{
protected apbsLootCache: Record<string, IBotLootCache>;
constructor(
@inject("PrimaryLogger") protected logger: ILogger,
@inject("ItemHelper") protected itemHelper: ItemHelper,
@inject("DatabaseServer") protected databaseServer: DatabaseServer,
@inject("PMCLootGenerator") protected pmcLootGenerator: PMCLootGenerator,
@inject("LocalisationService") protected localisationService: LocalisationService,
@inject("RagfairPriceService") protected ragfairPriceService: RagfairPriceService,
@inject("PrimaryCloner") protected cloner: ICloner,
@inject("APBSEquipmentGetter") protected apbsEquipmentGetter: APBSEquipmentGetter,
@inject("APBSTierGetter") protected apbsTierGetter: APBSTierGetter,
@inject("RaidInformation") protected raidInformation: RaidInformation,
@inject("APBSLogger") protected apbsLogger: APBSLogger
)
{
super(logger,
itemHelper,
databaseServer,
pmcLootGenerator,
localisationService,
ragfairPriceService,
cloner)
this.apbsClearCache();
this.clearCache();
}
public apbsClearCache(): void
{
this.apbsLootCache = {};
}
public apbsBotRoleExistsInCache(combinedBotRoleTier: string): boolean
{
return !!this.apbsLootCache[combinedBotRoleTier];
}
public apbsInitCacheForBotRole(combinedBotRoleTier: string): void
{
this.apbsLootCache[combinedBotRoleTier] = {
backpackLoot: {},
pocketLoot: {},
vestLoot: {},
secureLoot: {},
combinedPoolLoot: {},
specialItems: {},
grenadeItems: {},
drugItems: {},
foodItems: {},
drinkItems: {},
currencyItems: {},
healingItems: {},
stimItems: {}
};
}
public apbsGetLootFromCache(
botRole: string,
isPmc: boolean,
lootType: LootCacheType,
botJsonTemplate: IBotType,
botLevel: number
): Record<string, number>
{
const tierInfo = this.apbsTierGetter.getTierByLevel(botLevel).toString();
const combinedBotRoleTier = botRole + tierInfo;
if (!this.apbsBotRoleExistsInCache(combinedBotRoleTier))
{
this.apbsInitCacheForBotRole(combinedBotRoleTier);
this.apbsAddLootToCache(botRole, isPmc, botJsonTemplate, botLevel);
}
let result = undefined;
switch (lootType)
{
case LootCacheType.SPECIAL:
result = this.apbsLootCache[combinedBotRoleTier].specialItems;
break;
case LootCacheType.BACKPACK:
result = this.apbsLootCache[combinedBotRoleTier].backpackLoot;
break;
case LootCacheType.POCKET:
result = this.apbsLootCache[combinedBotRoleTier].pocketLoot;
break;
case LootCacheType.VEST:
result = this.apbsLootCache[combinedBotRoleTier].vestLoot;
break;
case LootCacheType.SECURE:
result = this.apbsLootCache[combinedBotRoleTier].secureLoot;
break;
case LootCacheType.COMBINED:
result = this.apbsLootCache[combinedBotRoleTier].combinedPoolLoot;
break;
case LootCacheType.HEALING_ITEMS:
result = this.apbsLootCache[combinedBotRoleTier].healingItems;
break;
case LootCacheType.GRENADE_ITEMS:
result = this.apbsLootCache[combinedBotRoleTier].grenadeItems;
break;
case LootCacheType.DRUG_ITEMS:
result = this.apbsLootCache[combinedBotRoleTier].drugItems;
break;
case LootCacheType.FOOD_ITEMS:
result = this.apbsLootCache[combinedBotRoleTier].foodItems;
break;
case LootCacheType.DRINK_ITEMS:
result = this.apbsLootCache[combinedBotRoleTier].drinkItems;
break;
case LootCacheType.CURRENCY_ITEMS:
result = this.apbsLootCache[combinedBotRoleTier].currencyItems;
break;
case LootCacheType.STIM_ITEMS:
result = this.apbsLootCache[combinedBotRoleTier].stimItems;
break;
default:
this.logger.error(
this.localisationService.getText("bot-loot_type_not_found", {
lootType: lootType,
botRole: botRole,
isPmc: isPmc
})
);
break;
}
return this.cloner.clone(result);
}
public apbsAddLootToCache(botRole: string, isPmc: boolean, botJsonTemplate: IBotType, botLevel: number): void
{
const tierInfo = this.apbsTierGetter.getTierByLevel(botLevel);
const combinedBotRoleTier = botRole + tierInfo;
const chances = this.apbsEquipmentGetter.getSpawnChancesByBotRole(botRole, tierInfo);
let realWhitelist: IGenerationWeightingItems = chances.generation.items;
if (!this.raidInformation.isBotEnabled(botRole))
{
this.apbsLogger.log(Logging.DEBUG, `${botRole} is disabled - TRACE: addLootToCache`);
realWhitelist = botJsonTemplate.generation.items;
}
// the full pool of loot we use to create the various sub-categories with
const lootPool = botJsonTemplate.inventory.items;
// Flatten all individual slot loot pools into one big pool, while filtering out potentially missing templates
const specialLootPool: Record<string, number> = {};
const backpackLootPool: Record<string, number> = {};
const pocketLootPool: Record<string, number> = {};
const vestLootPool: Record<string, number> = {};
const secureLootTPool: Record<string, number> = {};
const combinedLootPool: Record<string, number> = {};
if (isPmc)
{
// Replace lootPool from bot json with our own generated list for PMCs
lootPool.Backpack = this.cloner.clone(this.pmcLootGenerator.generatePMCBackpackLootPool(botRole));
lootPool.Pockets = this.cloner.clone(this.pmcLootGenerator.generatePMCPocketLootPool(botRole));
lootPool.TacticalVest = this.cloner.clone(this.pmcLootGenerator.generatePMCVestLootPool(botRole));
}
// Backpack/Pockets etc
for (const [slot, pool] of Object.entries(lootPool))
{
// No items to add, skip
if (Object.keys(pool).length === 0)
{
continue;
}
// Sort loot pool into separate buckets
switch (slot.toLowerCase())
{
case "specialloot":
this.addItemsToPool(specialLootPool, pool);
break;
case "pockets":
this.addItemsToPool(pocketLootPool, pool);
break;
case "tacticalvest":
this.addItemsToPool(vestLootPool, pool);
break;
case "securedcontainer":
this.addItemsToPool(secureLootTPool, pool);
break;
case "backpack":
this.addItemsToPool(backpackLootPool, pool);
break;
default:
this.logger.warning(`How did you get here ${slot}`);
}
// Add all items (if any) to combined pool (excluding secure)
if (Object.keys(pool).length > 0 && slot.toLowerCase() !== "securedcontainer")
{
this.addItemsToPool(combinedLootPool, pool);
}
}
// Assign whitelisted special items to bot if any exist
const specialLootItems: Record<string, number> =
Object.keys(realWhitelist.specialItems.whitelist)?.length > 0
? realWhitelist.specialItems.whitelist
: {};
// no whitelist, find and assign from combined item pool
if (Object.keys(specialLootItems).length === 0)
{
for (const [tpl, weight] of Object.entries(specialLootPool))
{
const itemTemplate = this.itemHelper.getItem(tpl)[1];
if (!(this.isBulletOrGrenade(itemTemplate._props) || this.isMagazine(itemTemplate._props)))
{
specialLootItems[tpl] = weight;
}
}
}
// Assign whitelisted healing items to bot if any exist
const healingItems: Record<string, number> =
Object.keys(realWhitelist.healing.whitelist)?.length > 0
? realWhitelist.healing.whitelist
: {};
// No whitelist, find and assign from combined item pool
if (Object.keys(healingItems).length === 0)
{
for (const [tpl, weight] of Object.entries(combinedLootPool))
{
const itemTemplate = this.itemHelper.getItem(tpl)[1];
if (
this.isMedicalItem(itemTemplate._props) &&
itemTemplate._parent !== BaseClasses.STIMULATOR &&
itemTemplate._parent !== BaseClasses.DRUGS
)
{
healingItems[tpl] = weight;
}
}
}
// Assign whitelisted drugs to bot if any exist
const drugItems: Record<string, number> =
Object.keys(realWhitelist.drugs.whitelist)?.length > 0
? realWhitelist.drugs.whitelist
: {};
// no drugs whitelist, find and assign from combined item pool
if (Object.keys(drugItems).length === 0)
{
for (const [tpl, weight] of Object.entries(combinedLootPool))
{
const itemTemplate = this.itemHelper.getItem(tpl)[1];
if (this.isMedicalItem(itemTemplate._props) && itemTemplate._parent === BaseClasses.DRUGS)
{
drugItems[tpl] = weight;
}
}
}
// Assign whitelisted food to bot if any exist
const foodItems: Record<string, number> =
Object.keys(realWhitelist.food.whitelist)?.length > 0
? realWhitelist.food.whitelist
: {};
// No food whitelist, find and assign from combined item pool
if (Object.keys(foodItems).length === 0)
{
for (const [tpl, weight] of Object.entries(combinedLootPool))
{
const itemTemplate = this.itemHelper.getItem(tpl)[1];
if (this.itemHelper.isOfBaseclass(itemTemplate._id, BaseClasses.FOOD))
{
foodItems[tpl] = weight;
}
}
}
// Assign whitelisted drink to bot if any exist
const drinkItems: Record<string, number> =
Object.keys(realWhitelist.food.whitelist)?.length > 0
? realWhitelist.food.whitelist
: {};
// No drink whitelist, find and assign from combined item pool
if (Object.keys(drinkItems).length === 0)
{
for (const [tpl, weight] of Object.entries(combinedLootPool))
{
const itemTemplate = this.itemHelper.getItem(tpl)[1];
if (this.itemHelper.isOfBaseclass(itemTemplate._id, BaseClasses.DRINK))
{
drinkItems[tpl] = weight;
}
}
}
// Assign whitelisted currency to bot if any exist
const currencyItems: Record<string, number> =
Object.keys(realWhitelist.currency.whitelist)?.length > 0
? realWhitelist.currency.whitelist
: {};
// No currency whitelist, find and assign from combined item pool
if (Object.keys(currencyItems).length === 0)
{
for (const [tpl, weight] of Object.entries(combinedLootPool))
{
const itemTemplate = this.itemHelper.getItem(tpl)[1];
if (this.itemHelper.isOfBaseclass(itemTemplate._id, BaseClasses.MONEY))
{
currencyItems[tpl] = weight;
}
}
}
// Assign whitelisted stims to bot if any exist
const stimItems: Record<string, number> =
Object.keys(realWhitelist.stims.whitelist)?.length > 0
? realWhitelist.stims.whitelist
: {};
// No whitelist, find and assign from combined item pool
if (Object.keys(stimItems).length === 0)
{
for (const [tpl, weight] of Object.entries(combinedLootPool))
{
const itemTemplate = this.itemHelper.getItem(tpl)[1];
if (this.isMedicalItem(itemTemplate._props) && itemTemplate._parent === BaseClasses.STIMULATOR)
{
stimItems[tpl] = weight;
}
}
}
// Assign whitelisted grenades to bot if any exist
const grenadeItems: Record<string, number> =
Object.keys(realWhitelist.grenades.whitelist)?.length > 0
? realWhitelist.grenades.whitelist
: {};
// no whitelist, find and assign from combined item pool
if (Object.keys(grenadeItems).length === 0)
{
for (const [tpl, weight] of Object.entries(combinedLootPool))
{
const itemTemplate = this.itemHelper.getItem(tpl)[1];
if (this.isGrenade(itemTemplate._props))
{
grenadeItems[tpl] = weight;
}
}
}
// Get backpack loot (excluding magazines, bullets, grenades, drink, food and healing/stim items)
const filteredBackpackItems = {};
for (const itemKey of Object.keys(backpackLootPool))
{
const itemResult = this.itemHelper.getItem(itemKey);
if (!itemResult[0])
{
continue;
}
const itemTemplate = itemResult[1];
if (
this.isBulletOrGrenade(itemTemplate._props) ||
this.isMagazine(itemTemplate._props) ||
this.isMedicalItem(itemTemplate._props) ||
this.isGrenade(itemTemplate._props) ||
this.isFood(itemTemplate._id) ||
this.isDrink(itemTemplate._id) ||
this.isCurrency(itemTemplate._id)
)
{
// Is type we dont want as backpack loot, skip
continue;
}
filteredBackpackItems[itemKey] = backpackLootPool[itemKey];
}
// Get pocket loot (excluding magazines, bullets, grenades, drink, food medical and healing/stim items)
const filteredPocketItems = {};
for (const itemKey of Object.keys(pocketLootPool))
{
const itemResult = this.itemHelper.getItem(itemKey);
if (!itemResult[0])
{
continue;
}
const itemTemplate = itemResult[1];
if (
this.isBulletOrGrenade(itemTemplate._props) ||
this.isMagazine(itemTemplate._props) ||
this.isMedicalItem(itemTemplate._props) ||
this.isGrenade(itemTemplate._props) ||
this.isFood(itemTemplate._id) ||
this.isDrink(itemTemplate._id) ||
this.isCurrency(itemTemplate._id) ||
!("Height" in itemTemplate._props) || // lacks height
!("Width" in itemTemplate._props) // lacks width
)
{
continue;
}
filteredPocketItems[itemKey] = pocketLootPool[itemKey];
}
// Get vest loot (excluding magazines, bullets, grenades, medical and healing/stim items)
const filteredVestItems = {};
for (const itemKey of Object.keys(vestLootPool))
{
const itemResult = this.itemHelper.getItem(itemKey);
if (!itemResult[0])
{
continue;
}
const itemTemplate = itemResult[1];
if (
this.isBulletOrGrenade(itemTemplate._props) ||
this.isMagazine(itemTemplate._props) ||
this.isMedicalItem(itemTemplate._props) ||
this.isGrenade(itemTemplate._props) ||
this.isFood(itemTemplate._id) ||
this.isDrink(itemTemplate._id) ||
this.isCurrency(itemTemplate._id)
)
{
continue;
}
filteredVestItems[itemKey] = vestLootPool[itemKey];
}
this.apbsLootCache[combinedBotRoleTier].healingItems = healingItems;
this.apbsLootCache[combinedBotRoleTier].drugItems = drugItems;
this.apbsLootCache[combinedBotRoleTier].foodItems = foodItems;
this.apbsLootCache[combinedBotRoleTier].drinkItems = drinkItems;
this.apbsLootCache[combinedBotRoleTier].currencyItems = currencyItems;
this.apbsLootCache[combinedBotRoleTier].stimItems = stimItems;
this.apbsLootCache[combinedBotRoleTier].grenadeItems = grenadeItems;
this.apbsLootCache[combinedBotRoleTier].specialItems = specialLootItems;
this.apbsLootCache[combinedBotRoleTier].backpackLoot = filteredBackpackItems;
this.apbsLootCache[combinedBotRoleTier].pocketLoot = filteredPocketItems;
this.apbsLootCache[combinedBotRoleTier].vestLoot = filteredVestItems;
this.apbsLootCache[combinedBotRoleTier].secureLoot = secureLootTPool;
}
}
@@ -23,6 +23,12 @@ import { BotLootGenerator } from "@spt/generators/BotLootGenerator";
import { APBSEquipmentGetter } from "../Utils/APBSEquipmentGetter";
import { APBSTierGetter } from "../Utils/APBSTierGetter";
import { APBSBotLootCacheService } from "./APBSBotLootCacheService";
import { RaidInformation } from "../Globals/RaidInformation";
import { APBSLogger } from "../Utils/APBSLogger";
import { Logging } from "../Enums/Logging";
import { ITemplateItem } from "@spt/models/eft/common/tables/ITemplateItem";
import { IItemSpawnLimitSettings } from "@spt/models/spt/bots/IItemSpawnLimitSettings";
/** Handle profile related client events */
@injectable()
@@ -45,7 +51,10 @@ export class APBSBotLootGenerator extends BotLootGenerator
@inject("ConfigServer") protected configServer: ConfigServer,
@inject("PrimaryCloner") protected cloner: ICloner,
@inject("APBSEquipmentGetter") protected apbsEquipmentGetter: APBSEquipmentGetter,
@inject("APBSTierGetter") protected apbsTierGetter: APBSTierGetter
@inject("APBSTierGetter") protected apbsTierGetter: APBSTierGetter,
@inject("RaidInformation") protected raidInformation: RaidInformation,
@inject("APBSBotLootCacheService") protected apbsBotLootCacheService: APBSBotLootCacheService,
@inject("APBSLogger") protected apbsLogger: APBSLogger
)
{
super(logger,
@@ -73,11 +82,19 @@ export class APBSBotLootGenerator extends BotLootGenerator
botInventory: PmcInventory,
botLevel: number
): void
{// Limits on item types to be added as loot
{
// Limits on item types to be added as loot
const tierInfo = this.apbsTierGetter.getTierByLevel(botLevel);
const chances = this.apbsEquipmentGetter.getSpawnChancesByBotRole(botRole, tierInfo);
const itemCounts: IGenerationWeightingItems = chances.generation.items;
let itemCounts: IGenerationWeightingItems = chances.generation.items;
let useOriginalLootCache = false;
if (!this.raidInformation.isBotEnabled(botRole))
{
this.apbsLogger.log(Logging.DEBUG, `${botRole} is disabled - TRACE: generateLoot`);
itemCounts = botJsonTemplate.generation.items;
useOriginalLootCache = true;
}
if (
!itemCounts.backpackLoot.weights
@@ -101,22 +118,16 @@ export class APBSBotLootGenerator extends BotLootGenerator
let backpackLootCount = Number(
this.weightedRandomHelper.getWeightedValue<number>(itemCounts.backpackLoot.weights)
);
let pocketLootCount = Number(
this.weightedRandomHelper.getWeightedValue<number>(itemCounts.pocketLoot.weights)
);
let pocketLootCount = Number(this.weightedRandomHelper.getWeightedValue<number>(itemCounts.pocketLoot.weights));
let vestLootCount = this.weightedRandomHelper.getWeightedValue<number>(itemCounts.vestLoot.weights);
const specialLootItemCount = Number(
this.weightedRandomHelper.getWeightedValue<number>(itemCounts.specialItems.weights)
);
const specialLootItemCount = Number(this.weightedRandomHelper.getWeightedValue<number>(itemCounts.specialItems.weights));
const healingItemCount = Number(this.weightedRandomHelper.getWeightedValue<number>(itemCounts.healing.weights));
const drugItemCount = Number(this.weightedRandomHelper.getWeightedValue<number>(itemCounts.drugs.weights));
const foodItemCount = Number(this.weightedRandomHelper.getWeightedValue<number>(itemCounts.food.weights));
const drinkItemCount = Number(this.weightedRandomHelper.getWeightedValue<number>(itemCounts.drink.weights));
let currencyItemCount = Number(
this.weightedRandomHelper.getWeightedValue<number>(itemCounts.currency.weights)
);
let currencyItemCount = Number(this.weightedRandomHelper.getWeightedValue<number>(itemCounts.currency.weights));
const stimItemCount = Number(this.weightedRandomHelper.getWeightedValue<number>(itemCounts.stims.weights));
const grenadeCount = Number(this.weightedRandomHelper.getWeightedValue<number>(itemCounts.grenades.weights));
@@ -146,7 +157,9 @@ export class APBSBotLootGenerator extends BotLootGenerator
// Special items
this.addLootFromPool(
this.botLootCacheService.getLootFromCache(botRole, isPmc, LootCacheType.SPECIAL, botJsonTemplate),
useOriginalLootCache ?
this.botLootCacheService.getLootFromCache(botRole, isPmc, LootCacheType.SPECIAL, botJsonTemplate) :
this.apbsBotLootCacheService.apbsGetLootFromCache(botRole, isPmc, LootCacheType.SPECIAL, botJsonTemplate, botLevel),
containersBotHasAvailable,
specialLootItemCount,
botInventory,
@@ -159,12 +172,14 @@ export class APBSBotLootGenerator extends BotLootGenerator
// Healing items / Meds
this.addLootFromPool(
this.botLootCacheService.getLootFromCache(botRole, isPmc, LootCacheType.HEALING_ITEMS, botJsonTemplate),
useOriginalLootCache ?
this.botLootCacheService.getLootFromCache(botRole, isPmc, LootCacheType.HEALING_ITEMS, botJsonTemplate) :
this.apbsBotLootCacheService.apbsGetLootFromCache(botRole, isPmc, LootCacheType.HEALING_ITEMS, botJsonTemplate, botLevel),
containersBotHasAvailable,
healingItemCount,
botInventory,
botRole,
undefined,
botItemLimits,
0,
isPmc,
containersIdFull
@@ -172,12 +187,14 @@ export class APBSBotLootGenerator extends BotLootGenerator
// Drugs
this.addLootFromPool(
this.botLootCacheService.getLootFromCache(botRole, isPmc, LootCacheType.DRUG_ITEMS, botJsonTemplate),
useOriginalLootCache ?
this.botLootCacheService.getLootFromCache(botRole, isPmc, LootCacheType.DRUG_ITEMS, botJsonTemplate) :
this.apbsBotLootCacheService.apbsGetLootFromCache(botRole, isPmc, LootCacheType.DRUG_ITEMS, botJsonTemplate, botLevel),
containersBotHasAvailable,
drugItemCount,
botInventory,
botRole,
undefined,
botItemLimits,
0,
isPmc,
containersIdFull
@@ -185,12 +202,14 @@ export class APBSBotLootGenerator extends BotLootGenerator
// Food
this.addLootFromPool(
this.botLootCacheService.getLootFromCache(botRole, isPmc, LootCacheType.FOOD_ITEMS, botJsonTemplate),
useOriginalLootCache ?
this.botLootCacheService.getLootFromCache(botRole, isPmc, LootCacheType.FOOD_ITEMS, botJsonTemplate) :
this.apbsBotLootCacheService.apbsGetLootFromCache(botRole, isPmc, LootCacheType.FOOD_ITEMS, botJsonTemplate, botLevel),
containersBotHasAvailable,
foodItemCount,
botInventory,
botRole,
undefined,
botItemLimits,
0,
isPmc,
containersIdFull
@@ -198,12 +217,14 @@ export class APBSBotLootGenerator extends BotLootGenerator
// Drink
this.addLootFromPool(
this.botLootCacheService.getLootFromCache(botRole, isPmc, LootCacheType.DRINK_ITEMS, botJsonTemplate),
useOriginalLootCache ?
this.botLootCacheService.getLootFromCache(botRole, isPmc, LootCacheType.DRINK_ITEMS, botJsonTemplate) :
this.apbsBotLootCacheService.apbsGetLootFromCache(botRole, isPmc, LootCacheType.DRINK_ITEMS, botJsonTemplate, botLevel),
containersBotHasAvailable,
drinkItemCount,
botInventory,
botRole,
undefined,
botItemLimits,
0,
isPmc,
containersIdFull
@@ -211,12 +232,14 @@ export class APBSBotLootGenerator extends BotLootGenerator
// Currency
this.addLootFromPool(
this.botLootCacheService.getLootFromCache(botRole, isPmc, LootCacheType.CURRENCY_ITEMS, botJsonTemplate),
useOriginalLootCache ?
this.botLootCacheService.getLootFromCache(botRole, isPmc, LootCacheType.CURRENCY_ITEMS, botJsonTemplate) :
this.apbsBotLootCacheService.apbsGetLootFromCache(botRole, isPmc, LootCacheType.CURRENCY_ITEMS, botJsonTemplate, botLevel),
containersBotHasAvailable,
currencyItemCount,
botInventory,
botRole,
undefined,
botItemLimits,
0,
isPmc,
containersIdFull
@@ -224,7 +247,9 @@ export class APBSBotLootGenerator extends BotLootGenerator
// Stims
this.addLootFromPool(
this.botLootCacheService.getLootFromCache(botRole, isPmc, LootCacheType.STIM_ITEMS, botJsonTemplate),
useOriginalLootCache ?
this.botLootCacheService.getLootFromCache(botRole, isPmc, LootCacheType.STIM_ITEMS, botJsonTemplate) :
this.apbsBotLootCacheService.apbsGetLootFromCache(botRole, isPmc, LootCacheType.STIM_ITEMS, botJsonTemplate, botLevel),
containersBotHasAvailable,
stimItemCount,
botInventory,
@@ -237,12 +262,14 @@ export class APBSBotLootGenerator extends BotLootGenerator
// Grenades
this.addLootFromPool(
this.botLootCacheService.getLootFromCache(botRole, isPmc, LootCacheType.GRENADE_ITEMS, botJsonTemplate),
useOriginalLootCache ?
this.botLootCacheService.getLootFromCache(botRole, isPmc, LootCacheType.GRENADE_ITEMS, botJsonTemplate) :
this.apbsBotLootCacheService.apbsGetLootFromCache(botRole, isPmc, LootCacheType.GRENADE_ITEMS, botJsonTemplate, botLevel),
[EquipmentSlots.POCKETS, EquipmentSlots.TACTICAL_VEST], // Can't use containersBotHasEquipped as we dont want grenades added to backpack
grenadeCount,
botInventory,
botRole,
undefined,
botItemLimits,
0,
isPmc,
containersIdFull
@@ -269,7 +296,9 @@ export class APBSBotLootGenerator extends BotLootGenerator
const backpackLootRoubleTotal = this.getBackpackRoubleTotalByLevel(botLevel, isPmc);
this.addLootFromPool(
this.botLootCacheService.getLootFromCache(botRole, isPmc, LootCacheType.BACKPACK, botJsonTemplate),
useOriginalLootCache ?
this.botLootCacheService.getLootFromCache(botRole, isPmc, LootCacheType.BACKPACK, botJsonTemplate) :
this.apbsBotLootCacheService.apbsGetLootFromCache(botRole, isPmc, LootCacheType.BACKPACK, botJsonTemplate, botLevel),
[EquipmentSlots.BACKPACK],
backpackLootCount,
botInventory,
@@ -286,7 +315,9 @@ export class APBSBotLootGenerator extends BotLootGenerator
{
// Vest
this.addLootFromPool(
this.botLootCacheService.getLootFromCache(botRole, isPmc, LootCacheType.VEST, botJsonTemplate),
useOriginalLootCache ?
this.botLootCacheService.getLootFromCache(botRole, isPmc, LootCacheType.VEST, botJsonTemplate) :
this.apbsBotLootCacheService.apbsGetLootFromCache(botRole, isPmc, LootCacheType.VEST, botJsonTemplate, botLevel),
[EquipmentSlots.TACTICAL_VEST],
vestLootCount,
botInventory,
@@ -300,7 +331,9 @@ export class APBSBotLootGenerator extends BotLootGenerator
// Pockets
this.addLootFromPool(
this.botLootCacheService.getLootFromCache(botRole, isPmc, LootCacheType.POCKET, botJsonTemplate),
useOriginalLootCache ?
this.botLootCacheService.getLootFromCache(botRole, isPmc, LootCacheType.POCKET, botJsonTemplate) :
this.apbsBotLootCacheService.apbsGetLootFromCache(botRole, isPmc, LootCacheType.POCKET, botJsonTemplate, botLevel),
[EquipmentSlots.POCKETS],
pocketLootCount,
botInventory,
@@ -317,7 +350,9 @@ export class APBSBotLootGenerator extends BotLootGenerator
if (!isPmc || (isPmc && this.pmcConfig.addSecureContainerLootFromBotConfig))
{
this.addLootFromPool(
this.botLootCacheService.getLootFromCache(botRole, isPmc, LootCacheType.SECURE, botJsonTemplate),
useOriginalLootCache ?
this.botLootCacheService.getLootFromCache(botRole, isPmc, LootCacheType.SECURE, botJsonTemplate) :
this.apbsBotLootCacheService.apbsGetLootFromCache(botRole, isPmc, LootCacheType.SECURE, botJsonTemplate, botLevel),
[EquipmentSlots.SECURED_CONTAINER],
50,
botInventory,
@@ -328,5 +363,57 @@ export class APBSBotLootGenerator extends BotLootGenerator
containersIdFull
);
}
}
protected override itemHasReachedSpawnLimit(
itemTemplate: ITemplateItem,
botRole: string,
itemSpawnLimits: IItemSpawnLimitSettings
): boolean
{
// PMCs and scavs have different sections of bot config for spawn limits
if (!!itemSpawnLimits && Object.keys(itemSpawnLimits.globalLimits).length === 0)
{
// No items found in spawn limit, drop out
return false;
}
// No spawn limits, skipping
if (!itemSpawnLimits)
{
return false;
}
const idToCheckFor = this.getMatchingIdFromSpawnLimits(itemTemplate, itemSpawnLimits.globalLimits);
if (!idToCheckFor)
{
// ParentId or tplid not found in spawnLimits, not a spawn limited item, skip
return false;
}
// Increment item count with this bot type
itemSpawnLimits.currentLimits[idToCheckFor]++;
// Check if over limit
if (itemSpawnLimits.currentLimits[idToCheckFor] > itemSpawnLimits.globalLimits[idToCheckFor])
{
// Prevent edge-case of small loot pools + code trying to add limited item over and over infinitely
if (itemSpawnLimits.currentLimits[idToCheckFor] > itemSpawnLimits.globalLimits[idToCheckFor] * 10)
{
this.logger.debug(
this.localisationService.getText("bot-item_spawn_limit_reached_skipping_item", {
botRole: botRole,
itemName: itemTemplate._name,
attempts: itemSpawnLimits.currentLimits[idToCheckFor]
})
);
return false;
}
return true;
}
return false;
}
}
@@ -85,37 +85,13 @@ export class APBSBotWeaponGenerator extends BotWeaponGenerator
return this.generateWeaponByTpl(sessionId, weaponTpl, equipmentSlot, botTemplateInventory, weaponParentId, modChances, botRole, isPmc, botLevel);
}
// Config disable checks to flip to default weapon gen
if ((ModConfig.config.disableBossTierGeneration && (botRole.includes("boss") || botRole.includes("sectant") || botRole.includes("arena"))) || botRole == "bosslegion" || botRole == "bosspunisher")
// Check if bot disabled, if it is - use SPT code
if (!this.raidInformation.isBotEnabled(botRole))
{
this.apbsLogger.log(Logging.DEBUG, `${botRole} is disabled - TRACE: generateRandomWeapon`);
const weaponTpl = this.pickWeightedWeaponTplFromPool(equipmentSlot, botTemplateInventory);
return this.generateWeaponByTpl(sessionId, weaponTpl, equipmentSlot, botTemplateInventory, weaponParentId, modChances, botRole, isPmc, botLevel);
}
if (ModConfig.config.disableBossFollowerTierGeneration && botRole.includes("follower"))
{
const weaponTpl = this.pickWeightedWeaponTplFromPool(equipmentSlot, botTemplateInventory);
return this.generateWeaponByTpl(sessionId, weaponTpl, equipmentSlot, botTemplateInventory, weaponParentId, modChances, botRole, isPmc, botLevel);
}
if (ModConfig.config.disableRaiderRogueTierGeneration && (botRole.includes("exusec") || botRole.includes("pmcbot")))
{
const weaponTpl = this.pickWeightedWeaponTplFromPool(equipmentSlot, botTemplateInventory);
return this.generateWeaponByTpl(sessionId, weaponTpl, equipmentSlot, botTemplateInventory, weaponParentId, modChances, botRole, isPmc, botLevel);
}
if (ModConfig.config.disablePMCTierGeneration && (botRole.includes("pmcusec") || botRole.includes("pmcbear")))
{
const weaponTpl = this.pickWeightedWeaponTplFromPool(equipmentSlot, botTemplateInventory);
return this.generateWeaponByTpl(sessionId, weaponTpl, equipmentSlot, botTemplateInventory, weaponParentId, modChances, botRole, isPmc, botLevel);
}
if (ModConfig.config.disableScavTierGeneration && (botRole.includes("assault") || botRole.includes("marksman")))
{
const weaponTpl = this.pickWeightedWeaponTplFromPool(equipmentSlot, botTemplateInventory);
return this.generateWeaponByTpl(sessionId, weaponTpl, equipmentSlot, botTemplateInventory, weaponParentId, modChances, botRole, isPmc, botLevel);
}
if (botRole.includes("infected") || botRole.includes("spirit") || botRole.includes("skier") || botRole.includes("peacemaker") || botRole.includes("gifter"))
{
const weaponTpl = this.pickWeightedWeaponTplFromPool(equipmentSlot, botTemplateInventory);
return this.generateWeaponByTpl(sessionId, weaponTpl, equipmentSlot, botTemplateInventory, weaponParentId, modChances, botRole, isPmc, botLevel);
}
// If not disabled via config, all bots follow this custom generation
const tierInfo = this.apbsTierGetter.getTierByLevel(botLevel);
@@ -0,0 +1,143 @@
import { inject, injectable } from "tsyringe";
import { BotGenerator } from "@spt/generators/BotGenerator";
import { BotGeneratorHelper } from "@spt/helpers/BotGeneratorHelper";
import { BotHelper } from "@spt/helpers/BotHelper";
import { ItemHelper } from "@spt/helpers/ItemHelper";
import { ProfileHelper } from "@spt/helpers/ProfileHelper";
import { IPmcData } from "@spt/models/eft/common/IPmcData";
import { IBotInfoSettings } from "@spt/models/eft/common/tables/IBotBase";
import { IBotType } from "@spt/models/eft/common/tables/IBotType";
import { MemberCategory } from "@spt/models/enums/MemberCategory";
import { ILogger } from "@spt/models/spt/utils/ILogger";
import { ConfigServer } from "@spt/servers/ConfigServer";
import { SaveServer } from "@spt/servers/SaveServer";
import { BotLootCacheService } from "@spt/services/BotLootCacheService";
import { DatabaseService } from "@spt/services/DatabaseService";
import { FenceService } from "@spt/services/FenceService";
import { LocalisationService } from "@spt/services/LocalisationService";
import { HashUtil } from "@spt/utils/HashUtil";
import { RandomUtil } from "@spt/utils/RandomUtil";
import { ICloner } from "@spt/utils/cloners/ICloner";
import { PlayerScavGenerator } from "@spt/generators/PlayerScavGenerator";
import { APBSBotLootCacheService } from "./APBSBotLootCacheService";
@injectable()
export class APBSPlayerScavGenerator extends PlayerScavGenerator
{
constructor(
@inject("PrimaryLogger") protected logger: ILogger,
@inject("RandomUtil") protected randomUtil: RandomUtil,
@inject("DatabaseService") protected databaseService: DatabaseService,
@inject("HashUtil") protected hashUtil: HashUtil,
@inject("ItemHelper") protected itemHelper: ItemHelper,
@inject("BotGeneratorHelper") protected botGeneratorHelper: BotGeneratorHelper,
@inject("SaveServer") protected saveServer: SaveServer,
@inject("ProfileHelper") protected profileHelper: ProfileHelper,
@inject("BotHelper") protected botHelper: BotHelper,
@inject("FenceService") protected fenceService: FenceService,
@inject("BotLootCacheService") protected botLootCacheService: BotLootCacheService,
@inject("LocalisationService") protected localisationService: LocalisationService,
@inject("BotGenerator") protected botGenerator: BotGenerator,
@inject("ConfigServer") protected configServer: ConfigServer,
@inject("PrimaryCloner") protected cloner: ICloner,
@inject("APBSBotLootCacheService") protected apbsBotLootCacheService: APBSBotLootCacheService
)
{
super(logger,
randomUtil,
databaseService,
hashUtil,
itemHelper,
botGeneratorHelper,
saveServer,
profileHelper,
botHelper,
fenceService,
botLootCacheService,
localisationService,
botGenerator,
configServer,
cloner)
}
public generate(sessionID: string): IPmcData
{
// get karma level from profile
const profile = this.saveServer.getProfile(sessionID);
const profileCharactersClone = this.cloner.clone(profile.characters);
const pmcDataClone = profileCharactersClone.pmc;
const existingScavDataClone = profileCharactersClone.scav;
const scavKarmaLevel = this.getScavKarmaLevel(pmcDataClone);
// use karma level to get correct karmaSettings
const playerScavKarmaSettings = this.playerScavConfig.karmaLevel[scavKarmaLevel];
if (!playerScavKarmaSettings)
{
this.logger.error(this.localisationService.getText("scav-missing_karma_settings", scavKarmaLevel));
}
this.logger.debug(`generated player scav loadout with karma level ${scavKarmaLevel}`);
// Edit baseBotNode values
const baseBotNode: IBotType = this.constructBotBaseTemplate(playerScavKarmaSettings.botTypeForLoot);
this.adjustBotTemplateWithKarmaSpecificSettings(playerScavKarmaSettings, baseBotNode);
let scavData = this.botGenerator.generatePlayerScav(
sessionID,
playerScavKarmaSettings.botTypeForLoot.toLowerCase(),
"easy",
baseBotNode,
pmcDataClone
);
// Remove cached bot data after scav was generated
this.botLootCacheService.clearCache();
this.apbsBotLootCacheService.apbsClearCache();
// Add scav metadata
scavData.savage = undefined;
scavData.aid = pmcDataClone.aid;
scavData.TradersInfo = pmcDataClone.TradersInfo;
scavData.Info.Settings = {} as IBotInfoSettings;
scavData.Info.Bans = [];
scavData.Info.RegistrationDate = pmcDataClone.Info.RegistrationDate;
scavData.Info.GameVersion = pmcDataClone.Info.GameVersion;
scavData.Info.MemberCategory = MemberCategory.UNIQUE_ID;
scavData.Info.lockedMoveCommands = true;
scavData.RagfairInfo = pmcDataClone.RagfairInfo;
scavData.UnlockedInfo = pmcDataClone.UnlockedInfo;
// Persist previous scav data into new scav
scavData._id = existingScavDataClone._id ?? pmcDataClone.savage;
scavData.sessionId = existingScavDataClone.sessionId ?? pmcDataClone.sessionId;
scavData.Skills = this.getScavSkills(existingScavDataClone);
scavData.Stats = this.getScavStats(existingScavDataClone);
scavData.Info.Level = this.getScavLevel(existingScavDataClone);
scavData.Info.Experience = this.getScavExperience(existingScavDataClone);
scavData.Quests = existingScavDataClone.Quests ?? [];
scavData.TaskConditionCounters = existingScavDataClone.TaskConditionCounters ?? {};
scavData.Notes = existingScavDataClone.Notes ?? { Notes: [] };
scavData.WishList = existingScavDataClone.WishList ?? {};
scavData.Encyclopedia = pmcDataClone.Encyclopedia ?? {};
// Add additional items to player scav as loot
this.addAdditionalLootToPlayerScavContainers(playerScavKarmaSettings.lootItemsToAddChancePercent, scavData, [
"TacticalVest",
"Pockets",
"Backpack"
]);
// Remove secure container
scavData = this.profileHelper.removeSecureContainer(scavData);
// Set cooldown timer
scavData = this.setScavCooldownTimer(scavData, pmcDataClone);
// Add scav to the profile
this.saveServer.getProfile(sessionID).characters.scav = scavData;
return scavData;
}
}
@@ -38,20 +38,7 @@ export class APBSBotLevelGenerator
container.afterResolution("BotLevelGenerator", (_t, result: BotLevelGenerator) =>
{
result.generateBotLevel = (levelDetails: MinMax, botGenerationDetails: IBotGenerationDetails, bot: APBSIBotBase): IRandomisedBotLevelResult =>
{
/*
TESTING TIER DEVIATION - Since botGenerationDetails isn't passed to the relevant methods, this is more difficult that anticipated. This logic works for the tier, but since selection is based on level..oof.
-2 to +1 tier
const lowerDeviation = (Math.floor(Math.random() * 2) - 2);
const upperDeviation = (Math.floor(Math.random() * 2));
const minTier = (tier + lowerDeviation) <= 0 ? 1 : tier + lowerDeviation
const maxTier = (tier + upperDeviation) >= 7 ? 7 : tier + upperDeviation
const newTier = this.randomUtil.getInt(minTier, maxTier)
console.log(`Original Tier: ${tier} - New Tier ${newTier}`)
*/
{
if (this.modInformation.testMode && this.modInformation.testBotRole.includes(botGenerationDetails.role.toLowerCase()))
{
const level = this.profileHelper.getPmcProfile(this.raidInformation.sessionId)?.Info?.Level;
@@ -1,4 +1,5 @@
import { injectable, inject } from "tsyringe";
import { injectable } from "tsyringe";
import { ModConfig } from "./ModConfig";
@injectable()
export class RaidInformation
@@ -81,4 +82,88 @@ export class RaidInformation
"ShortRange": 80
}
}
public alwaysDisabledBots = [
"shooterbtr",
"skier",
"peacemaker",
"gifter",
"infectedassault",
"infectedcivil",
"infectedlaborant",
"infectedpmc",
"infectedtagilla",
"bosslegion",
"bosspunisher"
]
public isBotEnabled(botType: string): boolean
{
botType = botType.toLowerCase();
switch (botType)
{
case "pmcbear":
case "pmcusec":
if (ModConfig.config.disablePMCTierGeneration) return false;
return true;
case "cursedassault":
case "marksman":
case "assault":
if (ModConfig.config.disableScavTierGeneration) return false;
return true;
case "arenafighterevent":
case "exusec":
if (ModConfig.config.disableRaiderRogueTierGeneration) return false;
return true;
case "bossbully":
case "bosstagilla":
case "bosspartisan":
case "bossgluhar":
case "bosskilla":
case "bosskojaniy":
case "bosssanitar":
case "bossknight":
case "bosszryachiy":
case "bosstest":
case "bosskolontay":
case "bossboar":
case "bossboarSniper":
case "sectantpriest":
if (ModConfig.config.disableBossTierGeneration) return false;
return true;
case "sectantwarrior":
case "followerboarblose1":
case "followerboarclose2":
case "followerkolontayassault":
case "followerkolontaysecurity":
case "followerbully":
case "followergluharassault":
case "followergluharscout":
case "followergluharsecurity":
case "followergluharsnipe":
case "followerkojaniy":
case "followersanitar":
case "followertagilla":
case "followerbirdeye":
case "followerbigpipe":
case "followerzryachiy":
case "followertest":
case "followerboar":
if (ModConfig.config.disableBossFollowerTierGeneration) return false;
return true;
case "shooterbtr":
case "skier":
case "peacemaker":
case "gifter":
case "infectedassault":
case "infectedcivil":
case "infectedlaborant":
case "infectedpmc":
case "infectedtagilla":
case "bosslegion":
case "bosspunisher":
return false;
default:
return false;
}
}
}
@@ -370,343 +370,4 @@ export class TierInformation
}
}
]
public lootRandomization = [
{
"levelRange": {
"min": 1,
"max": 15
},
"generation": {
"drugs": {
"weights": {
"0": 1
},
"whitelist": {}
},
"grenades": {
"weights": {
"0": 45,
"1": 35,
"2": 1
},
"whitelist": {
"5710c24ad2720bc3458b45a3": 25,
"58d3db5386f77426186285a0": 25,
"5448be9a4bdc2dfd2f8b456a": 25
}
},
"healing": {
"weights": {
"0": 10,
"1": 85,
"2": 20
},
"whitelist": {
"5755356824597772cb798962": 4,
"590c661e86f7741e566b646a": 9,
"544fb45d4bdc2dee738b4568": 2,
"5e831507ea0a7c419c2f9bd9": 6
}
},
"backpackLoot": {
"weights": {
"0": 1
},
"whitelist": {}
},
"pocketLoot": {
"weights": {
"0": 1
},
"whitelist": {}
},
"vestLoot": {
"weights": {
"0": 1
},
"whitelist": {}
},
"magazines": {
"weights": {
"0": 0,
"1": 10,
"2": 55,
"3": 25
},
"whitelist": {}
},
"stims": {
"weights": {
"0": 100,
"1": 1
},
"whitelist": {
"544fb3f34bdc2d03748b456a": 25,
"5c0e530286f7747fa1419862": 65
}
}
}
},
{
"levelRange": {
"min": 16,
"max": 30
},
"generation": {
"drugs": {
"weights": {
"0": 1,
"1": 1
},
"whitelist": {}
},
"grenades": {
"weights": {
"0": 15,
"1": 35,
"2": 5,
"3": 0
},
"whitelist": {
"5710c24ad2720bc3458b45a3": 25,
"58d3db5386f77426186285a0": 25,
"5448be9a4bdc2dfd2f8b456a": 25,
"5e32f56fcb6d5863cc5e5ee4": 5,
"5e340dcdcb6d5863cc5e5efb": 1
}
},
"healing": {
"weights": {
"0": 1,
"1": 25,
"2": 65,
"3": 10
},
"whitelist": {
"60098ad7c2240c0fe85c570a": 15,
"590c678286f77426c9660122": 10,
"5e8488fa988a8701445df1e4": 12
}
},
"backpackLoot": {
"weights": {
"0": 1
},
"whitelist": {}
},
"pocketLoot": {
"weights": {
"0": 1
},
"whitelist": {}
},
"vestLoot": {
"weights": {
"0": 1
},
"whitelist": {}
},
"magazines": {
"weights": {
"0": 0,
"1": 1,
"2": 55,
"3": 25
},
"whitelist": {}
},
"stims": {
"weights": {
"0": 75,
"1": 20,
"2": 5
},
"whitelist": {
"544fb3f34bdc2d03748b456a": 1,
"5c0e530286f7747fa1419862": 65,
"5c0e534186f7747fa1419867": 12,
"5c0e531d86f7747fa23f4d42": 8,
"5ed51652f6c34d2cc26336a1": 17
}
}
}
},
{
"levelRange": {
"min": 31,
"max": 45
},
"generation": {
"drugs": {
"weights": {
"0": 1,
"1": 1
},
"whitelist": {}
},
"grenades": {
"weights": {
"0": 1,
"1": 15,
"2": 25,
"3": 10
},
"whitelist": {
"5710c24ad2720bc3458b45a3": 15,
"58d3db5386f77426186285a0": 15,
"5448be9a4bdc2dfd2f8b456a": 15,
"5e32f56fcb6d5863cc5e5ee4": 45,
"5e340dcdcb6d5863cc5e5efb": 20,
"617fd91e5539a84ec44ce155": 5,
"618a431df1eb8e24b8741deb": 5
}
},
"healing": {
"weights": {
"0": 1,
"1": 15,
"2": 65,
"3": 30,
"4": 5
},
"whitelist": {
"60098ad7c2240c0fe85c570a": 45,
"590c678286f77426c9660122": 20,
"5e8488fa988a8701445df1e4": 45
}
},
"backpackLoot": {
"weights": {
"0": 1
},
"whitelist": {}
},
"pocketLoot": {
"weights": {
"0": 1
},
"whitelist": {}
},
"vestLoot": {
"weights": {
"0": 1
},
"whitelist": {}
},
"magazines": {
"weights": {
"0": 0,
"1": 1,
"2": 55,
"3": 25
},
"whitelist": {}
},
"stims": {
"weights": {
"0": 50,
"1": 20,
"2": 5
},
"whitelist": {
"544fb3f34bdc2d03748b456a": 1,
"5c0e530286f7747fa1419862": 65,
"5c0e534186f7747fa1419867": 12,
"5c0e531d86f7747fa23f4d42": 8,
"5ed51652f6c34d2cc26336a1": 17
}
}
}
},
{
"levelRange": {
"min": 46,
"max": 100
},
"generation": {
"drugs": {
"weights": {
"0": 1,
"1": 1
},
"whitelist": {}
},
"grenades": {
"weights": {
"0": 1,
"1": 15,
"2": 20,
"3": 15
},
"whitelist": {
"5710c24ad2720bc3458b45a3": 1,
"58d3db5386f77426186285a0": 1,
"5448be9a4bdc2dfd2f8b456a": 1,
"5e32f56fcb6d5863cc5e5ee4": 35,
"5e340dcdcb6d5863cc5e5efb": 35,
"617fd91e5539a84ec44ce155": 10,
"618a431df1eb8e24b8741deb": 10
}
},
"healing": {
"weights": {
"0": 1,
"1": 15,
"2": 35,
"3": 75,
"4": 20
},
"whitelist": {
"60098ad7c2240c0fe85c570a": 45,
"590c678286f77426c9660122": 20,
"5e8488fa988a8701445df1e4": 45
}
},
"backpackLoot": {
"weights": {
"0": 1
},
"whitelist": {}
},
"pocketLoot": {
"weights": {
"0": 1
},
"whitelist": {}
},
"vestLoot": {
"weights": {
"0": 1
},
"whitelist": {}
},
"magazines": {
"weights": {
"0": 0,
"1": 1,
"2": 25,
"3": 55
},
"whitelist": {}
},
"stims": {
"weights": {
"0": 50,
"1": 20,
"2": 5
},
"whitelist": {
"544fb3f34bdc2d03748b456a": 1,
"5c0e530286f7747fa1419862": 65,
"5c0e534186f7747fa1419867": 12,
"5c0e531d86f7747fa23f4d42": 8,
"5ed51652f6c34d2cc26336a1": 17
}
}
}
}
]
}
@@ -27,6 +27,7 @@ import { BotGeneratorHelper } from "@spt/helpers/BotGeneratorHelper";
import { BotWeaponGeneratorHelper } from "@spt/helpers/BotWeaponGeneratorHelper";
import { BotWeaponModLimitService } from "@spt/services/BotWeaponModLimitService";
import { RepairService } from "@spt/services/RepairService";
import { SeasonalEventService } from "@spt/services/SeasonalEventService";
import { VFS } from "@spt/utils/VFS";
// Custom
@@ -52,7 +53,8 @@ import { BlacklistHelper } from "./Helpers/BlacklistHelper";
import { RealismHelper } from "./Helpers/RealismHelper";
import { APBSTester } from "./Utils/APBSTester";
import { APBSAttachmentChecker } from "./Utils/APBSAttachmentChecker";
import { SeasonalEventService } from "@spt/services/SeasonalEventService";
import { APBSBotLootCacheService } from "./ClassExtensions/APBSBotLootCacheService";
import { APBSPlayerScavGenerator } from "./ClassExtensions/APBSPlayerScavGenerator";
export class InstanceManager
{
@@ -182,6 +184,10 @@ export class InstanceManager
this.container.register("BotInventoryGenerator", { useToken: "APBSBotInventoryGenerator" });
this.container.register<APBSBotEquipmentModGenerator>("APBSBotEquipmentModGenerator", APBSBotEquipmentModGenerator);
this.container.register("BotEquipmentModGenerator", { useToken: "APBSBotEquipmentModGenerator" });
this.container.register<APBSPlayerScavGenerator>("APBSPlayerScavGenerator", APBSPlayerScavGenerator);
this.container.register("PlayerScavGenerator", { useToken: "APBSPlayerScavGenerator" });
this.container.register<APBSBotLootCacheService>("APBSBotLootCacheService", APBSBotLootCacheService);
this.container.register("BotLootCacheService", { useToken: "APBSBotLootCacheService" });
this.container.register<APBSBotLootGenerator>("APBSBotLootGenerator", APBSBotLootGenerator);
this.container.register("BotLootGenerator", { useToken: "APBSBotLootGenerator" });
this.container.register<APBSBotWeaponGenerator>("APBSBotWeaponGenerator", APBSBotWeaponGenerator);
@@ -178,6 +178,27 @@ export class APBSEquipmentGetter
const tierJson = this.getTierModsJson(tierInfo)
switch (botRole)
{
case "bossboar":
case "bossboarsniper":
case "bossbully":
case "bossgluhar":
case "bosskilla":
case "bosskojaniy":
case "bosskolontay":
case "bosssanitar":
case "bosstagilla":
case "bosspartisan":
case "bossknight":
case "followerbigpipe":
case "followerbirdeye":
case "sectantpriest":
case "sectantwarrior":
case "exusec":
case "arenafighterevent":
case "arenafighter":
case "pmcbot":
if (tierInfo < 4) return this.tierInformation.tier4mods;
else return tierJson;
case "marksman":
case "cursedassault":
case "assault":
@@ -558,6 +558,7 @@
"56e335e4d2720b6c058b456d": 20,
"5ca20d5986f774331e7c9602": 10,
"5e9dcf5986f7746c417435b3": 10,
"59e763f286f7742ee57895da": 15,
"6038d614d10cbf667352dd44": 1,
"6034d103ca006d2dca39b3f0": 1,
"656ddcf0f02d7bcea90bf395": 1,
@@ -614,6 +614,7 @@
"56e335e4d2720b6c058b456d": 20,
"5ca20d5986f774331e7c9602": 10,
"5e9dcf5986f7746c417435b3": 10,
"59e763f286f7742ee57895da": 15,
"6038d614d10cbf667352dd44": 1,
"6034d103ca006d2dca39b3f0": 1,
"656ddcf0f02d7bcea90bf395": 1,
@@ -866,6 +866,7 @@
"56e335e4d2720b6c058b456d": 20,
"5ca20d5986f774331e7c9602": 10,
"5e9dcf5986f7746c417435b3": 10,
"59e763f286f7742ee57895da": 15,
"6038d614d10cbf667352dd44": 1,
"6034d103ca006d2dca39b3f0": 1,
"656ddcf0f02d7bcea90bf395": 1,
@@ -877,7 +878,6 @@
"5c0e805e86f774683f3dd637": 1,
"5ab8ebf186f7742d8b372e80": 1,
"639346cc1c8f182ad90c8972": 1,
"59e763f286f7742ee57895da": 1,
"5b44c6ae86f7742d1627baea": 1,
"5d5d940f86f7742797262046": 1,
"5df8a4d786f77412672a1e3b": 1,
@@ -798,6 +798,7 @@
"56e335e4d2720b6c058b456d": 20,
"5ca20d5986f774331e7c9602": 10,
"5e9dcf5986f7746c417435b3": 10,
"59e763f286f7742ee57895da": 15,
"6038d614d10cbf667352dd44": 1,
"6034d103ca006d2dca39b3f0": 1,
"656ddcf0f02d7bcea90bf395": 1,
@@ -809,7 +810,6 @@
"5c0e805e86f774683f3dd637": 1,
"5ab8ebf186f7742d8b372e80": 1,
"639346cc1c8f182ad90c8972": 1,
"59e763f286f7742ee57895da": 1,
"5b44c6ae86f7742d1627baea": 1,
"5d5d940f86f7742797262046": 1,
"5df8a4d786f77412672a1e3b": 1,
@@ -784,6 +784,7 @@
"56e335e4d2720b6c058b456d": 20,
"5ca20d5986f774331e7c9602": 10,
"5e9dcf5986f7746c417435b3": 10,
"59e763f286f7742ee57895da": 15,
"6038d614d10cbf667352dd44": 1,
"6034d103ca006d2dca39b3f0": 1,
"656ddcf0f02d7bcea90bf395": 1,
@@ -795,7 +796,6 @@
"5c0e805e86f774683f3dd637": 1,
"5ab8ebf186f7742d8b372e80": 1,
"639346cc1c8f182ad90c8972": 1,
"59e763f286f7742ee57895da": 1,
"5b44c6ae86f7742d1627baea": 1,
"5d5d940f86f7742797262046": 1,
"5df8a4d786f77412672a1e3b": 1,
@@ -764,6 +764,7 @@
"56e335e4d2720b6c058b456d": 20,
"5ca20d5986f774331e7c9602": 10,
"5e9dcf5986f7746c417435b3": 10,
"59e763f286f7742ee57895da": 15,
"6038d614d10cbf667352dd44": 1,
"6034d103ca006d2dca39b3f0": 1,
"656ddcf0f02d7bcea90bf395": 1,
@@ -775,7 +776,6 @@
"5c0e805e86f774683f3dd637": 1,
"5ab8ebf186f7742d8b372e80": 1,
"639346cc1c8f182ad90c8972": 1,
"59e763f286f7742ee57895da": 1,
"5b44c6ae86f7742d1627baea": 1,
"5d5d940f86f7742797262046": 1,
"5df8a4d786f77412672a1e3b": 1,