508 lines
17 KiB
TypeScript
Raw Normal View History

2022-07-30 23:04:18 -04:00
import { IBotConfig } from "@spt-aki/models/spt/config/IBotConfig";
import { DatabaseServer } from "@spt-aki/servers/DatabaseServer";
2022-08-03 21:57:37 -04:00
import { Logger } from "./logger";
import type { BossLocationSpawn } from "@spt-aki/models/eft/common/ILocationBase";
import { Money } from "@spt-aki/models/enums/Money"
import { Config } from "../config/config";
2022-08-04 18:05:23 -04:00
import { WeightedRandomHelper } from "@spt-aki/helpers/WeightedRandomHelper";
import { ITrader } from "@spt-aki/models/eft/common/tables/ITrader";
2022-07-30 23:04:18 -04:00
export class Bots
{
private modConfig: Config = require("../config/config.json");
2022-08-03 21:57:37 -04:00
private logger: Logger;
2022-07-30 23:04:18 -04:00
private botConfig: IBotConfig;
2022-08-08 20:03:48 -04:00
private tables: DatabaseServer;
2022-08-04 18:05:23 -04:00
private traders: Record<string, ITrader>;
private weightedRandomHelper: WeightedRandomHelper;
2022-07-30 23:04:18 -04:00
2022-08-04 18:05:23 -04:00
constructor(logger: Logger, databaseServer: DatabaseServer, botConfig: IBotConfig, weightedRandomHelper: WeightedRandomHelper)
2022-07-30 23:04:18 -04:00
{
this.logger = logger;
this.botConfig = botConfig;
this.tables = databaseServer.getTables();
2022-08-08 20:03:48 -04:00
this.traders = this.tables.traders;
2022-08-04 18:05:23 -04:00
this.weightedRandomHelper = weightedRandomHelper;
2022-07-30 23:04:18 -04:00
}
public updateBots(): void
{
2022-08-08 20:03:48 -04:00
// modConfig variables
2022-08-03 21:57:37 -04:00
const mod = this.modConfig.bots;
2022-08-08 20:03:48 -04:00
const modPMC = this.modConfig.bots.pmc;
const modScav = this.modConfig.bots.scav;
const preWipe = this.modConfig.prewipeEvents;
// Server side variables
const pmc = this.botConfig.pmc;
const lootNValue = this.botConfig.lootNValue;
// Start modifications
2022-08-03 21:57:37 -04:00
2022-08-08 20:03:48 -04:00
// Checks if straight up difficulty selection is used or not to determine if it should use that or if it should then go to use weighted difficulties.
if (modPMC.difficultyWeights.difficulty != "asonline")
2022-07-30 23:04:18 -04:00
{
2022-08-08 20:03:48 -04:00
// Uses PMC difficulty weighting if straight up difficulty selection is not used.
if (modPMC.difficultyWeights.useWeights)
2022-08-04 18:05:23 -04:00
{
const chosenDifficulty = this.chooseRandomWeightedDifficulty();
this.logger.info("PMC Difficulty Chance Weights Patched");
this.logger.info(`PMC Difficulty Chosen: ${chosenDifficulty}`);
}
2022-08-08 20:03:48 -04:00
// Uses PMC difficulty if weighting is disabled.
2022-08-04 18:05:23 -04:00
else
{
2022-08-08 20:03:48 -04:00
pmc.difficulty = modPMC.difficultyWeights.difficulty;
this.logger.info(`PMC Bot Difficulty set to ${modPMC.difficultyWeights.difficulty}`);
2022-08-04 18:05:23 -04:00
}
2022-07-30 23:04:18 -04:00
}
2022-08-08 20:03:48 -04:00
// Enables common and secure containers to spawn on PMCs by whitelisting the parent IDs. Rarity adjusted via PMC lootNValue. Default true.
if (modPMC.containersOnPMCs)
2022-07-30 23:04:18 -04:00
{
2022-08-03 21:57:37 -04:00
this.containersOnPMCs();
2022-08-08 20:03:48 -04:00
this.logger.info(`Containers On PMCs: ${modPMC.containersOnPMCs}`);
2022-07-30 23:04:18 -04:00
}
2022-08-08 20:03:48 -04:00
// Chance that PMC bot will be USEC or BEAR. Default is 50%
if (modPMC.isUsec != 50)
2022-07-30 23:04:18 -04:00
{
2022-08-08 20:03:48 -04:00
pmc.isUsec = modPMC.isUsec;
this.logger.info(`PMC isUsec Chance is: ${modPMC.isUsec}`);
2022-07-30 23:04:18 -04:00
}
2022-08-08 20:03:48 -04:00
2022-08-03 21:57:37 -04:00
// Max Loot Value in Rubles for PMC bots in Backpack, Pockets, and Vest respectively. Default is 150,000/50,000/50,000
2022-08-08 20:03:48 -04:00
if (modPMC.maxBackpackLootTotalRub != 150000
|| modPMC.maxPocketLootTotalRub !=50000
|| modPMC.maxVestLootTotalRub != 50000)
2022-07-30 23:04:18 -04:00
{
2022-08-08 20:03:48 -04:00
this.changeMaxLootvalue();
this.logger.info(`PMC Loot Value totals changed! \n Max Backpack Total Value: ${modPMC.maxBackpackLootTotalRub} \n Max Pocket Total Value: ${modPMC.maxPocketLootTotalRub} \n Max Vest Total Value: ${modPMC.maxVestLootTotalRub}`);
2022-07-30 23:04:18 -04:00
}
2022-08-08 20:03:48 -04:00
// Chance that the PMC bot of your faction (BEAR/USEC) will be hostile or not. Default is 50%.
if (modPMC.chanceSameSideIsHostile != 50)
2022-07-30 23:04:18 -04:00
{
2022-08-08 20:03:48 -04:00
pmc.chanceSameSideIsHostilePercent = modPMC.chanceSameSideIsHostile;
this.logger.info(`Chance Same Side Is Hostle is ${modPMC.chanceSameSideIsHostile}`);
2022-07-30 23:04:18 -04:00
}
2022-08-08 20:03:48 -04:00
// Adjusts the Max Bot Cap located in configs/bot.json/maxBotCap
2022-08-03 21:57:37 -04:00
if (mod.maxBotCap != 20)
2022-07-30 23:04:18 -04:00
{
2022-08-03 21:57:37 -04:00
this.botConfig.maxBotCap = mod.maxBotCap;
this.logger.info(`Bot Cap is now ${mod.maxBotCap}`);
2022-07-30 23:04:18 -04:00
}
2022-08-08 20:03:48 -04:00
// Modifies the lootNValue of PMC or Scav if configured outside of the defaults.
if (modPMC.lootNValue != 3 || modScav.lootNValue != 4)
{
2022-08-08 20:03:48 -04:00
lootNValue.scav = modScav.lootNValue;
lootNValue.pmc = modPMC.lootNValue;
this.logger.info(`lootNValue for bots has been changed! \n Scav lootNValue set to ${modScav.lootNValue} \n PMC lootNValue set to ${modPMC.lootNValue}`);
}
2022-08-08 20:03:48 -04:00
// Adjusts the chance for PMC to spawn instead of the default bot type if configured outside of the default values.
const pmcChance = modPMC.convertIntoPmcChance;
if (pmcChance.assault.min != 15 || pmcChance.assault.max != 40
|| pmcChance.cursedAssault.min != 15 || pmcChance.cursedAssault.max != 40
|| pmcChance.pmcBot.min != 15 || pmcChance.pmcBot.max != 30
|| pmcChance.exUsec.min != 5 || pmcChance.exUsec.max != 20)
2022-08-03 21:57:37 -04:00
{
2022-08-08 20:03:48 -04:00
this.adjustPmcChance();
2022-08-03 21:57:37 -04:00
this.logger.info("Chance to Convert Bots into PMC Patched");
2022-08-08 20:03:48 -04:00
}
2022-08-04 18:05:23 -04:00
2022-07-30 23:04:18 -04:00
2022-08-08 20:03:48 -04:00
// Makes *all* bosses spawn chance configurable.
2022-08-03 21:57:37 -04:00
if (mod.bossChance.activated)
{
2022-08-08 20:03:48 -04:00
this.configureBossChance();
this.logger.info(`Boss Chance set to ${mod.bossChance.chance}`)
2022-08-03 21:57:37 -04:00
}
2022-07-30 23:04:18 -04:00
2022-08-08 20:03:48 -04:00
2022-08-03 21:57:37 -04:00
// Prewipe Events
2022-08-08 20:03:48 -04:00
// Spawn Killa On Factory
if (preWipe.killaOnFactory)
2022-08-03 21:57:37 -04:00
{
2022-08-08 20:03:48 -04:00
this.spawnKillaOnFactory();
this.logger.info("Killa On Factry Enabled");
2022-08-03 21:57:37 -04:00
}
2022-07-30 23:04:18 -04:00
2022-08-08 20:03:48 -04:00
// Spawns All Bosses On Reserve
if (preWipe.allBossesOnReserve)
2022-08-03 21:57:37 -04:00
{
2022-08-08 20:03:48 -04:00
this.spawnAllBossesOnReserve();
2022-08-03 21:57:37 -04:00
this.logger.info("Bosses On Reserve Prewipe Event Enabled");
}
2022-08-08 20:03:48 -04:00
2022-08-03 21:57:37 -04:00
2022-08-08 20:03:48 -04:00
// Spawns Gluhar On Labs
if (preWipe.gluharOnLabs)
2022-08-03 21:57:37 -04:00
{
2022-08-08 20:03:48 -04:00
this.spawnGluharOnLabs();
this.logger.info("Gluhar On Labs Prewipe Event Enabled");
2022-08-03 21:57:37 -04:00
}
2022-07-30 23:04:18 -04:00
2022-08-08 20:03:48 -04:00
2022-08-03 21:57:37 -04:00
// All cheap items on traders
2022-08-08 20:03:48 -04:00
if (preWipe.allTradersSellCheapItems)
2022-08-03 21:57:37 -04:00
{
2022-08-08 20:03:48 -04:00
this.allTradersSellCheapItems();
2022-08-03 21:57:37 -04:00
this.logger.info("Cheap Items On Traders Prewipe Event Enabled");
}
2022-07-30 23:04:18 -04:00
2022-08-08 20:03:48 -04:00
// Makes Obdolbos Super Powered
if (preWipe.makeObdolbosPowerful)
2022-08-03 21:57:37 -04:00
{
2022-08-08 20:03:48 -04:00
this.makeObdolbosPowerful();
2022-08-03 21:57:37 -04:00
this.logger.info("Make Obdolbos Powerful Prewipe Event Enabled");
}
2022-08-08 20:03:48 -04:00
if (modPMC.looseWeaponInBackpackChance != 15 || modPMC.looseWeaponInBackpackLoot.max != 1 || modPMC.looseWeaponInBackpackLoot.min != 1)
{
this.changeLooseWeapon();
this.logger.info("Loose Weapon In PMC Backpack Values Patched");
}
2022-07-30 23:04:18 -04:00
}
2022-08-08 20:03:48 -04:00
// Functions start here.
// Function to enable secured and common containers on PMCs.
2022-08-03 21:57:37 -04:00
private containersOnPMCs(): void
2022-07-30 23:04:18 -04:00
{
2022-08-03 21:57:37 -04:00
const dynaLoot = this.botConfig.pmc.dynamicLoot.whitelist;
2022-07-30 23:04:18 -04:00
2022-08-03 21:57:37 -04:00
dynaLoot.push("5448bf274bdc2dfc2f8b456a");
dynaLoot.push("5795f317245977243854e041");
}
2022-07-30 23:04:18 -04:00
2022-08-04 18:05:23 -04:00
private chooseRandomWeightedDifficulty(): string
{
const chosenDifficulty = this.weightedRandomHelper.getWeightedInventoryItem(this.modConfig.bots.pmc.difficultyWeights.weights);
this.botConfig.pmc.difficulty = chosenDifficulty;
return chosenDifficulty;
}
2022-08-08 20:03:48 -04:00
public createBossWave(role: string, chance: number, followers: string, escortAmount: number, zones: string): any
{
return {
"BossName": role,
"BossChance": chance,
"BossZone": zones,
"BossPlayer": false,
"BossDifficult": "normal",
"BossEscortType": followers,
"BossEscortDifficult": "normal",
"BossEscortAmount": escortAmount,
"Time": -1
}
}
2022-08-08 20:03:48 -04:00
private adjustPmcChance(): void
{
const pmcConfig = this.botConfig.pmc.convertIntoPmcChance;
const modConfig = this.modConfig.bots.pmc.convertIntoPmcChance;
pmcConfig.assault.min = modConfig.assault.min;
pmcConfig.assault.max = modConfig.assault.max;
pmcConfig.cursedassault.min = modConfig.cursedAssault.min;
pmcConfig.cursedassault.max = modConfig.cursedAssault.max;
pmcConfig.pmcbot.min = modConfig.pmcBot.min;
pmcConfig.pmcbot.max = modConfig.pmcBot.max;
pmcConfig.exusec.min = modConfig.exUsec.min;
pmcConfig.exusec.max = modConfig.exUsec.max;
}
private changeMaxLootvalue(): void
{
const lootConfig = this.botConfig.pmc;
const modConfig = this.modConfig.bots.pmc;
lootConfig.maxBackpackLootTotalRub = modConfig.maxBackpackLootTotalRub;
lootConfig.maxPocketLootTotalRub = modConfig.maxPocketLootTotalRub;
lootConfig.maxVestLootTotalRub = modConfig.maxVestLootTotalRub;
}
private configureBossChance(): void
{
const locations = this.tables.locations;
for (const i in locations)
{
if (i !== "base")
{
if (locations[i].base.BossLocationSpawn !== [])
{
for (const x in locations[i].base.BossLocationSpawn)
{
locations[i].base.BossLocationSpawn[x].BossChance = this.modConfig.bots.bossChance.chance;
}
}
}
}
}
private spawnKillaOnFactory(): void
{
const locations = this.tables.locations;
const killaWave = this.createBossWave("bossKilla", 100, "followerBully", 0, locations.factory4_day.base.OpenZones);
this.tables.locations.factory4_day.base.BossLocationSpawn.push(killaWave);
locations.factory4_night.base.BossLocationSpawn.push(killaWave);
}
private spawnAllBossesOnReserve(): void
{
const locations = this.tables.locations;
let bossWave = this.createBossWave("bossKilla", 100, "followerBully", 0, locations.rezervbase.base.OpenZones);
locations.rezervbase.base.BossLocationSpawn.push(bossWave);
bossWave = this.createBossWave("bossBully", 100, "followerBully", 4, locations.rezervbase.base.OpenZones);
locations.rezervbase.base.BossLocationSpawn.push(bossWave);
bossWave = this.createBossWave("bossKojaniy", 100, "followerKojaniy", 2, locations.rezervbase.base.OpenZones);
locations.rezervbase.base.BossLocationSpawn.push(bossWave);
bossWave = this.createBossWave("bossSanitar", 100, "followerSanitar", 2, locations.rezervbase.base.OpenZones);
locations.rezervbase.base.BossLocationSpawn.push(bossWave);
}
private spawnGluharOnLabs(): void
{
const locations = this.tables.locations;
const glugluWave: BossLocationSpawn =
{
"BossName": "bossGluhar",
"BossChance": 43,
"BossZone": "ZoneRailStrorage,ZoneRailStrorage,ZoneRailStrorage,ZonePTOR1,ZonePTOR2,ZoneBarrack,ZoneBarrack,ZoneBarrack,ZoneSubStorage",
"BossPlayer": false,
"BossDifficult": "normal",
"BossEscortType": "followerGluharAssault",
"BossEscortDifficult": "normal",
"BossEscortAmount": "0",
"Time": -1,
"TriggerId": "",
"TriggerName": "",
"Supports": [
{
"BossEscortType": "followerGluharAssault",
"BossEscortDifficult": [
"normal"
],
"BossEscortAmount": "2"
},
{
"BossEscortType": "followerGluharSecurity",
"BossEscortDifficult": [
"normal"
],
"BossEscortAmount": "2"
},
{
"BossEscortType": "followerGluharScout",
"BossEscortDifficult": [
"normal"
],
"BossEscortAmount": "2"
}
],
RandomTimeSpawn: false
}
glugluWave.BossZone = locations.laboratory.base.OpenZones;
locations.laboratory.base.BossLocationSpawn.push(glugluWave);
}
private allTradersSellCheapItems(): void
{
for (const trader in this.traders)
{
for (const assort in this.traders[trader].assort.barter_scheme)
{
const itemScheme = this.traders[trader].assort.barter_scheme[assort];
switch (itemScheme[0][0]._tpl)
{
case Money.ROUBLES:
itemScheme[0][0].count = itemScheme[0][0].count * 0.01;
break;
case Money.DOLLARS:
itemScheme[0][0].count = itemScheme[0][0].count * 0.1;
break;
case Money.EUROS:
itemScheme[0][0].count = itemScheme[0][0].count * 0.05;
break;
default:
break;
}
}
}
}
private makeObdolbosPowerful(): void
{
const obdolbosBuff = [
{
"BuffType": "StaminaRate",
"Chance": 1,
"Delay": 1,
"Duration": 1800,
"Value": 0.5,
"AbsoluteValue": true,
"SkillName": ""
},
{
"BuffType": "SkillRate",
"Chance": 1,
"Delay": 1,
"Duration": 1800,
"Value": 10,
"AbsoluteValue": true,
"SkillName": "Endurance"
},
{
"BuffType": "SkillRate",
"Chance": 1,
"Delay": 1,
"Duration": 1800,
"Value": 10,
"AbsoluteValue": true,
"SkillName": "Strength"
},
{
"BuffType": "SkillRate",
"Chance": 1,
"Delay": 1,
"Duration": 1800,
"Value": 20,
"AbsoluteValue": true,
"SkillName": "StressResistance"
},
{
"BuffType": "SkillRate",
"Chance": 1,
"Delay": 1,
"Duration": 1800,
"Value": 20,
"AbsoluteValue": true,
"SkillName": "Charisma"
},
{
"BuffType": "SkillRate",
"Chance": 1,
"Delay": 1,
"Duration": 1800,
"Value": -20,
"AbsoluteValue": true,
"SkillName": "Memory"
},
{
"BuffType": "SkillRate",
"Chance": 1,
"Delay": 1,
"Duration": 1800,
"Value": -20,
"AbsoluteValue": true,
"SkillName": "Intellect"
},
{
"BuffType": "SkillRate",
"Chance": 1,
"Delay": 1,
"Duration": 1800,
"Value": -20,
"AbsoluteValue": true,
"SkillName": "Attention"
},
{
"BuffType": "Pain",
"Chance": 0.25,
"Delay": 1,
"Duration": 1800,
"Value": 0,
"AbsoluteValue": false,
"SkillName": ""
},
{
"BuffType": "StomachBloodloss",
"Chance": 0.25,
"Delay": 1,
"Duration": 1800,
"Value": 0,
"AbsoluteValue": false,
"SkillName": ""
},
{
"BuffType": "HydrationRate",
"Chance": 0.25,
"Delay": 1,
"Duration": 1800,
"Value": -0.05,
"AbsoluteValue": true,
"SkillName": ""
},
{
"BuffType": "EnergyRate",
"Chance": 0.25,
"Delay": 1,
"Duration": 1800,
"Value": -0.05,
"AbsoluteValue": true,
"SkillName": ""
},
{
"BuffType": "DamageModifier",
"Chance": 0.25,
"Delay": 1,
"Duration": 1800,
"Value": 0.2,
"AbsoluteValue": false,
"SkillName": ""
},
{
"BuffType": "QuantumTunnelling",
"Chance": 0.25,
"Delay": 1,
"Duration": 1800,
"Value": 0,
"AbsoluteValue": false,
"SkillName": ""
}]
this.tables.globals.config.Health.Effects.Stimulator.Buffs.Buffs_Obdolbos = obdolbosBuff;
}
private changeLooseWeapon():void
{
const pmcConfig = this.botConfig.pmc;
const modConfig = this.modConfig.bots.pmc;
pmcConfig.looseWeaponInBackpackChancePercent = modConfig.looseWeaponInBackpackChance;
pmcConfig.looseWeaponInBackpackLootMinMax.min = modConfig.looseWeaponInBackpackLoot.min;
pmcConfig.looseWeaponInBackpackLootMinMax.max = modConfig.looseWeaponInBackpackLoot.max;
}
2022-08-03 21:57:37 -04:00
}