rename file

This commit is contained in:
2024-06-09 14:50:47 +08:00
parent 138a16c289
commit 0e22d8dbce
376 changed files with 3 additions and 3 deletions
+170
View File
@@ -0,0 +1,170 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.aiAmountProper = exports.validMaps = exports.pmcType = exports.diffProper = exports.reverseMapNames = exports.reverseBossNames = exports.roleCase = exports.Props = exports.Center = exports.ColliderParams = exports.Position = exports.SpawnPointParam = exports.MapWrapper = exports.GroupPattern = exports.Bot = void 0;
class Bot {
BotType;
MaxBotCount;
}
exports.Bot = Bot;
class GroupPattern {
Name;
Bots;
Time_min;
Time_max;
BotZone;
RandomTimeSpawn;
OnlySpawnOnce;
}
exports.GroupPattern = GroupPattern;
class MapWrapper {
MapName;
MapGroups;
MapBosses;
}
exports.MapWrapper = MapWrapper;
class SpawnPointParam {
Id;
Position;
Rotation;
Sides;
Categories;
Infiltration;
DelayToCanSpawnSec;
ColliderParams;
BotZoneName;
}
exports.SpawnPointParam = SpawnPointParam;
class Position {
x;
y;
z;
}
exports.Position = Position;
class ColliderParams {
_parent;
_props;
}
exports.ColliderParams = ColliderParams;
class Center {
x;
y;
z;
}
exports.Center = Center;
class Props {
Center;
Radius;
}
exports.Props = Props;
exports.roleCase = {
assault: "assault",
exusec: "exUsec",
marksman: "marksman",
pmcbot: "pmcBot",
sectantpriest: "sectantPriest",
sectantwarrior: "sectantWarrior",
assaultgroup: "assaultGroup",
bossbully: "bossBully",
bosstagilla: "bossTagilla",
bossgluhar: "bossGluhar",
bosskilla: "bossKilla",
bosskojaniy: "bossKojaniy",
bosssanitar: "bossSanitar",
bossboar: "bossBoar",
bossboarsniper: "bossBoarSniper",
bosskolontay: "bossKolontay",
bosspunisher: "bosspunisher",
bosslegion: "bosslegion",
followerboar: "followerBoar",
followerboarclose1: "followerBoarClose1",
followerboarclose2: "followerBoarClose2",
followerbully: "followerBully",
followergluharassault: "followerGluharAssault",
followergluharscout: "followerGluharScout",
followergluharsecurity: "followerGluharSecurity",
followergluharsnipe: "followerGluharSnipe",
followerkojaniy: "followerKojaniy",
followersanitar: "followerSanitar",
followertagilla: "followerTagilla",
followerkolontayassault: "followerKolontayAssault",
followerkolontaysecurity: "followerKolontaySecurity",
cursedassault: "cursedAssault",
pmc: "pmc",
usec: "usec",
bear: "bear",
sptbear: "sptBear",
sptusec: "sptUsec",
bosstest: "bossTest",
followertest: "followerTest",
gifter: "gifter",
bossknight: "bossKnight",
followerbigpipe: "followerBigPipe",
followerbirdeye: "followerBirdEye",
bosszryachiy: "bossZryachiy",
followerzryachiy: "followerZryachiy",
arenafighterevent: "arenaFighterEvent",
crazyassaultevent: "crazyAssaultEvent"
};
exports.reverseBossNames = {
bossboar: "kaban",
bossbully: "reshala",
bosstagilla: "tagilla",
bossgluhar: "gluhar",
bosskilla: "killa",
bosskojaniy: "shturman",
bosssanitar: "sanitar",
bossknight: "goons",
bosszryachiy: "zryachiy",
bosskolontay: "kolontay",
marksman: "scav_snipers",
sectantpriest: "cultists",
exusec: "rogues",
pmcbot: "raiders",
crazyassaultevent: "crazyscavs",
arenafighterevent: "bloodhounds",
bosspunisher: "punisher",
bosslegion: "legion",
gifter: "santa"
};
exports.reverseMapNames = {
factory4_day: "factory",
factory4_night: "factory_night",
bigmap: "customs",
woods: "woods",
shoreline: "shoreline",
lighthouse: "lighthouse",
rezervbase: "reserve",
interchange: "interchange",
laboratory: "laboratory",
tarkovstreets: "streets",
sandbox: "groundzero"
};
exports.diffProper = {
easy: "easy",
asonline: "random",
normal: "normal",
hard: "hard",
impossible: "impossible"
};
exports.pmcType = ["sptbear", "sptusec"];
exports.validMaps = [
"bigmap",
"factory4_day",
"factory4_night",
"interchange",
"laboratory",
"lighthouse",
"rezervbase",
"shoreline",
"tarkovstreets",
"woods",
"sandbox"
];
exports.aiAmountProper = {
low: 0.5,
asonline: 1,
medium: 1,
high: 2,
horde: 4,
};
//# sourceMappingURL=ClassDef.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"ClassDef.js","sourceRoot":"","sources":["ClassDef.ts"],"names":[],"mappings":"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"}
+180
View File
@@ -0,0 +1,180 @@
import {
BossLocationSpawn,
} from "@spt-aki/models/eft/common/ILocationBase";
export interface BossPattern extends BossLocationSpawn {
OnlySpawnOnce?: boolean;
}
export class Bot {
BotType: string;
MaxBotCount: number;
}
export class GroupPattern {
Name: string;
Bots: Bot[];
Time_min: number;
Time_max: number;
BotZone: string;
RandomTimeSpawn?: boolean;
OnlySpawnOnce?: boolean;
}
export class MapWrapper {
MapName: string;
MapGroups: GroupPattern[];
MapBosses: BossPattern[];
}
export class SpawnPointParam {
Id: string;
Position: Position;
Rotation: number;
Sides: string[];
Categories: string[];
Infiltration: string;
DelayToCanSpawnSec: number;
ColliderParams: ColliderParams;
BotZoneName: string;
}
export class Position {
x: number;
y: number;
z: number;
}
export class ColliderParams {
_parent: string;
_props: Props;
}
export class Center {
x: number;
y: number;
z: number;
}
export class Props {
Center: Center;
Radius: number;
}
export const roleCase: object = {
assault: "assault",
exusec: "exUsec",
marksman: "marksman",
pmcbot: "pmcBot",
sectantpriest: "sectantPriest",
sectantwarrior: "sectantWarrior",
assaultgroup: "assaultGroup",
bossbully: "bossBully",
bosstagilla: "bossTagilla",
bossgluhar: "bossGluhar",
bosskilla: "bossKilla",
bosskojaniy: "bossKojaniy",
bosssanitar: "bossSanitar",
bossboar: "bossBoar",
bossboarsniper: "bossBoarSniper",
bosskolontay: "bossKolontay",
bosspunisher: "bosspunisher",
bosslegion: "bosslegion",
followerboar: "followerBoar",
followerboarclose1: "followerBoarClose1",
followerboarclose2: "followerBoarClose2",
followerbully: "followerBully",
followergluharassault: "followerGluharAssault",
followergluharscout: "followerGluharScout",
followergluharsecurity: "followerGluharSecurity",
followergluharsnipe: "followerGluharSnipe",
followerkojaniy: "followerKojaniy",
followersanitar: "followerSanitar",
followertagilla: "followerTagilla",
followerkolontayassault: "followerKolontayAssault",
followerkolontaysecurity: "followerKolontaySecurity",
cursedassault: "cursedAssault",
pmc: "pmc",
usec: "usec",
bear: "bear",
sptbear: "sptBear",
sptusec: "sptUsec",
bosstest: "bossTest",
followertest: "followerTest",
gifter: "gifter",
bossknight: "bossKnight",
followerbigpipe: "followerBigPipe",
followerbirdeye: "followerBirdEye",
bosszryachiy: "bossZryachiy",
followerzryachiy: "followerZryachiy",
arenafighterevent: "arenaFighterEvent",
crazyassaultevent: "crazyAssaultEvent"
};
export const reverseBossNames: object = {
bossboar: "kaban",
bossbully: "reshala",
bosstagilla: "tagilla",
bossgluhar: "gluhar",
bosskilla: "killa",
bosskojaniy: "shturman",
bosssanitar: "sanitar",
bossknight: "goons",
bosszryachiy: "zryachiy",
bosskolontay: "kolontay",
marksman: "scav_snipers",
sectantpriest: "cultists",
exusec: "rogues",
pmcbot: "raiders",
crazyassaultevent: "crazyscavs",
arenafighterevent: "bloodhounds",
bosspunisher: "punisher",
bosslegion: "legion",
gifter: "santa"
};
export const reverseMapNames: object = {
factory4_day: "factory",
factory4_night: "factory_night",
bigmap: "customs",
woods: "woods",
shoreline: "shoreline",
lighthouse: "lighthouse",
rezervbase: "reserve",
interchange: "interchange",
laboratory: "laboratory",
tarkovstreets: "streets",
sandbox: "groundzero"
};
export const diffProper = {
easy: "easy",
asonline: "random",
normal: "normal",
hard: "hard",
impossible: "impossible"
};
export const pmcType: string[] = ["sptbear", "sptusec"];
export const validMaps: string[] = [
"bigmap",
"factory4_day",
"factory4_night",
"interchange",
"laboratory",
"lighthouse",
"rezervbase",
"shoreline",
"tarkovstreets",
"woods",
"sandbox"
];
export const aiAmountProper = {
low: 0.5,
asonline: 1,
medium: 1,
high: 2,
horde: 4,
};
+599
View File
@@ -0,0 +1,599 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const ConfigTypes_1 = require("/snapshot/project/obj/models/enums/ConfigTypes");
const ContextVariableType_1 = require("/snapshot/project/obj/context/ContextVariableType");
const JsonUtil_1 = require("/snapshot/project/obj/utils/JsonUtil");
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
const ClassDef = __importStar(require("./ClassDef"));
const ClassDef_1 = require("./ClassDef");
// General SWAG Config
const config_json_1 = __importDefault(require("../config/config.json"));
const bossConfig_json_1 = __importDefault(require("../config/bossConfig.json"));
// Bosses
const gluhar_json_1 = __importDefault(require("../config/bosses/gluhar.json"));
const goons_json_1 = __importDefault(require("../config/bosses/goons.json"));
const kaban_json_1 = __importDefault(require("../config/bosses/kaban.json"));
const killa_json_1 = __importDefault(require("../config/bosses/killa.json"));
const kolontay_json_1 = __importDefault(require("../config/bosses/kolontay.json"));
const reshala_json_1 = __importDefault(require("../config/bosses/reshala.json"));
const sanitar_json_1 = __importDefault(require("../config/bosses/sanitar.json"));
const shturman_json_1 = __importDefault(require("../config/bosses/shturman.json"));
const tagilla_json_1 = __importDefault(require("../config/bosses/tagilla.json"));
const zryachiy_json_1 = __importDefault(require("../config/bosses/zryachiy.json"));
// Spawn Configs
const bloodhounds_json_1 = __importDefault(require("../config/other/bloodhounds.json"));
const cultists_json_1 = __importDefault(require("../config/other/cultists.json"));
const raiders_json_1 = __importDefault(require("../config/other/raiders.json"));
const rogues_json_1 = __importDefault(require("../config/other/rogues.json"));
const scav_snipers_json_1 = __importDefault(require("../config/other/scav_snipers.json"));
// Custom
const punisher_json_1 = __importDefault(require("../config/custom/punisher.json"));
const legion_json_1 = __importDefault(require("../config/custom/legion.json"));
const otherSpawnConfigs = [
bloodhounds_json_1.default,
cultists_json_1.default,
scav_snipers_json_1.default,
raiders_json_1.default,
rogues_json_1.default
];
const bossSpawnConfigs = [
gluhar_json_1.default,
goons_json_1.default,
kaban_json_1.default,
killa_json_1.default,
kolontay_json_1.default,
reshala_json_1.default,
sanitar_json_1.default,
shturman_json_1.default,
tagilla_json_1.default,
zryachiy_json_1.default
];
const customSpawnConfigs = [
punisher_json_1.default,
legion_json_1.default
];
const modName = "SWAG";
let logger;
let LocationCallbacks;
LocationCallbacks;
let jsonUtil;
JsonUtil_1.JsonUtil;
let botConfig;
let pmcConfig;
let iGlobals;
let configServer;
let databaseServer;
let locations;
let seasonalEvents;
let randomUtil;
let profileHelper;
let sessionId;
let BossWaveSpawnedOnceAlready;
const customPatterns = {};
class SWAG {
static savedLocationData = {
factory4_day: undefined,
factory4_night: undefined,
bigmap: undefined,
interchange: undefined,
laboratory: undefined,
lighthouse: undefined,
rezervbase: undefined,
shoreline: undefined,
tarkovstreets: undefined,
woods: undefined,
sandbox: undefined,
// unused
develop: undefined,
hideout: undefined,
privatearea: undefined,
suburbs: undefined,
terminal: undefined,
town: undefined,
};
static pmcType = ["sptbear", "sptusec"];
static randomWaveTimer = {
time_min: 0,
time_max: 0,
};
static actual_timers = {
time_min: 0,
time_max: 0,
};
static waveCounter = {
count: 1,
};
static raid_time = {
time_of_day: "day",
};
static bossCount = {
count: 0,
};
preAkiLoad(container) {
const HttpResponse = container.resolve("HttpResponseUtil");
const staticRouterModService = container.resolve("StaticRouterModService");
staticRouterModService.registerStaticRouter(`${modName}/client/match/offline/end`, [
{
url: "/client/match/offline/end",
action: (url, info, sessionID, output) => {
sessionId = sessionID;
SWAG.ClearDefaultSpawns();
SWAG.ConfigureMaps();
return LocationCallbacks.getLocationData(url, info, sessionID);
},
},
], "SWAG");
staticRouterModService.registerStaticRouter(`${modName}/client/locations`, [
{
url: "/client/locations",
action: (url, info, sessionID, output) => {
sessionId = sessionID;
SWAG.ClearDefaultSpawns();
SWAG.ConfigureMaps();
return LocationCallbacks.getLocationData(url, info, sessionID);
},
},
], "SWAG");
staticRouterModService.registerStaticRouter(`${modName}/client/items`, [
{
url: "/client/items",
action: (url, info, sessionID, output) => {
sessionId = sessionID;
const locationConfig = container.resolve("ConfigServer").getConfig(ConfigTypes_1.ConfigTypes.LOCATION);
// as of SPT 3.6.0 we need to disable the new spawn system so that SWAG can clear spawns properly
if (!config_json_1.default?.UseDefaultSpawns?.Waves ||
!config_json_1.default?.UseDefaultSpawns?.Bosses ||
!config_json_1.default?.UseDefaultSpawns?.TriggeredWaves) {
SWAG.disableSpawnSystems();
}
// disable more vanilla spawn stuff
locationConfig.splitWaveIntoSingleSpawnsSettings.enabled = false;
locationConfig.rogueLighthouseSpawnTimeSettings.enabled = false;
locationConfig.fixEmptyBotWavesSettings.enabled = false;
locationConfig.addOpenZonesToAllMaps = false;
locationConfig.addCustomBotWavesToMaps = false;
locationConfig.enableBotTypeLimits = false;
logger.info("SWAG: Vanilla spawn systems disabled");
return output;
},
},
], "SWAG");
staticRouterModService.registerStaticRouter(`${modName}/client/raid/configuration`, [{
url: "/client/raid/configuration",
action: (url, info, sessionID, output) => {
try {
// Retrieve configurations
const botConfig = container.resolve("ConfigServer").getConfig(ConfigTypes_1.ConfigTypes.BOT);
const pmcConfig = container.resolve("ConfigServer").getConfig(ConfigTypes_1.ConfigTypes.PMC);
// Disable PMC conversion
const conversionTypes = ["assault", "cursedassault", "pmcbot", "exusec", "arenafighter", "arenafighterevent", "crazyassaultevent"];
conversionTypes.forEach(type => {
pmcConfig.convertIntoPmcChance[type] = { min: 0, max: 0 };
});
logger.info("SWAG: PMC conversion is OFF (this is good - be sure this loads AFTER Realism/SVM)");
// Adjust time and map caps
const appContext = container.resolve("ApplicationContext");
const weatherController = container.resolve("WeatherController");
const matchInfoStartOff = appContext.getLatestValue(ContextVariableType_1.ContextVariableType.RAID_CONFIGURATION).getValue();
const time = weatherController.generate().time;
let realTime = time;
if (matchInfoStartOff.timeVariant === "PAST") {
let [hours, minutes] = time.split(":").map(Number);
hours = (hours - 12 + 24) % 24; // Adjust time backwards by 12 hours and ensure it wraps correctly
realTime = `${hours}:${minutes}`;
}
// Determine Time of Day
let TOD = "day";
let [hours] = realTime.split(":").map(Number);
if ((matchInfoStartOff.location !== "factory4_night" && hours >= 5 && hours < 22) ||
matchInfoStartOff.location === "factory4_day" ||
matchInfoStartOff.location.toLowerCase() === "laboratory") {
TOD = "day";
}
else {
TOD = "night";
}
// Set map caps based on Time of Day
if (TOD === "day") {
Object.keys(config_json_1.default.MaxBotCap).forEach(key => {
botConfig.maxBotCap[key] = config_json_1.default.MaxBotCap[key];
});
}
else { // "night"
Object.keys(config_json_1.default.NightMaxBotCap).forEach(key => {
botConfig.maxBotCap[key] = config_json_1.default.NightMaxBotCap[key];
});
}
logger.info(`SWAG: ${TOD} Raid Max Bot Caps set`);
return HttpResponse.nullResponse();
}
catch (e) {
logger.error(`SWAG: Failed To modify PMC conversion, you may have more PMCs than you're supposed to. Error: ${e}`);
return HttpResponse.nullResponse();
}
},
}], "SWAG");
}
postDBLoad(container) {
logger = container.resolve("WinstonLogger");
LocationCallbacks =
container.resolve("LocationCallbacks");
jsonUtil = container.resolve("JsonUtil");
configServer = container.resolve("ConfigServer");
botConfig = configServer.getConfig(ConfigTypes_1.ConfigTypes.BOT);
pmcConfig = configServer.getConfig(ConfigTypes_1.ConfigTypes.PMC);
databaseServer = container.resolve("DatabaseServer");
locations = databaseServer.getTables().locations;
randomUtil = container.resolve("RandomUtil");
seasonalEvents = container.resolve("SeasonalEventService");
profileHelper = container.resolve("ProfileHelper");
}
/**
* Returns all available OpenZones specified in location.base.OpenZones as well as any OpenZone found in the SpawnPointParams.
* Filters out all sniper zones
* @param map
* @returns
*/
static GetOpenZones(map) {
const baseobj = locations[map]?.base;
// Get all OpenZones defined in the base obj that do not include sniper zones. Need to filter for empty strings as well.
const foundOpenZones = baseobj?.OpenZones?.split(",")
.filter((name) => !name.includes("Snipe"))
.filter((name) => name.trim() !== "") ?? [];
// Sometimes there are zones in the SpawnPointParams that arent listed in the OpenZones, parse these and add them to the list of zones
baseobj?.SpawnPointParams?.forEach((spawn) => {
//check spawn for open zones and if it doesn't exist add to end of array
if (spawn?.BotZoneName &&
!foundOpenZones.includes(spawn.BotZoneName) &&
!spawn.BotZoneName.includes("Snipe")) {
foundOpenZones.push(spawn.BotZoneName);
}
});
//logger.info(`SWAG: Open Zones(${map}): ${JSON.stringify(foundOpenZones)}`);
return foundOpenZones;
}
static shuffleArray(array) {
for (let i = array.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[array[i], array[j]] = [array[j], array[i]];
}
}
static ConfigureMaps() {
const bossConfigs = {};
const otherConfigs = {};
const customConfigs = {};
bossSpawnConfigs.forEach(data => {
Object.keys(data).forEach(mapKey => {
if (bossConfig_json_1.default.TotalBossesPerMap[mapKey] === 0 || config_json_1.default.disableAllSpawns.bosses) {
return;
}
if (!bossConfigs[mapKey]) {
bossConfigs[mapKey] = [];
}
const filteredBosses = data[mapKey].filter(boss => {
// ignore boarsniper
if (boss.BossName == "bossboarsniper") {
return false;
}
const shouldSkip = boss.BossChance === 0 ||
(bossConfig_json_1.default.Bosses.useGlobalBossSpawnChance &&
bossConfig_json_1.default.Bosses[ClassDef_1.reverseBossNames[boss.BossName]][mapKey] === 0);
return !shouldSkip;
});
bossConfigs[mapKey].push(...filteredBosses);
});
});
otherSpawnConfigs.forEach(data => {
Object.keys(data).forEach(mapKey => {
if (!otherConfigs[mapKey]) {
otherConfigs[mapKey] = [];
}
const filteredBosses = data[mapKey].filter(boss => {
const bossType = ClassDef_1.reverseBossNames[boss.BossName];
if (config_json_1.default.disableAllSpawns[bossType]) {
return false;
}
const shouldSkip = boss.BossChance === 0 ||
(config_json_1.default.Spawns.useGlobalSpawnChance && config_json_1.default.Spawns[bossType][mapKey] === 0);
return !shouldSkip;
});
otherConfigs[mapKey].push(...filteredBosses);
});
});
customSpawnConfigs.forEach(data => {
Object.keys(data).forEach(mapKey => {
if (!customConfigs[mapKey]) {
customConfigs[mapKey] = [];
}
const filteredBosses = data[mapKey].filter(boss => {
if (boss.BossName == "gifter") {
if (!bossConfig_json_1.default.CustomBosses.santa.enabled ||
(!seasonalEvents.christmasEventEnabled() && !bossConfig_json_1.default.CustomBosses.santa.forceSpawnOutsideEvent)) {
return false;
}
}
const shouldSkip = boss.BossChance === 0 ||
!bossConfig_json_1.default.CustomBosses[ClassDef_1.reverseBossNames[boss.BossName]].enabled ||
(bossConfig_json_1.default.CustomBosses[ClassDef_1.reverseBossNames[boss.BossName]].enabled &&
bossConfig_json_1.default.CustomBosses[ClassDef_1.reverseBossNames[boss.BossName]][mapKey] === 0);
return !shouldSkip;
});
customConfigs[mapKey].push(...filteredBosses);
});
});
// Shuffle each array within the configuration objects
Object.values(bossConfigs).forEach(array => this.shuffleArray(array));
Object.values(otherConfigs).forEach(array => this.shuffleArray(array));
Object.values(customConfigs).forEach(array => this.shuffleArray(array));
ClassDef.validMaps.forEach((globalmap) => {
if (bossConfigs[ClassDef_1.reverseMapNames[globalmap]]) {
bossConfigs[ClassDef_1.reverseMapNames[globalmap]].forEach(boss => {
SWAG.SpawnBosses(boss, globalmap);
SWAG.bossCount.count += 1;
});
}
// reset boss count for the next map
SWAG.bossCount.count = 0;
if (otherConfigs[ClassDef_1.reverseMapNames[globalmap]]) {
otherConfigs[ClassDef_1.reverseMapNames[globalmap]].forEach(spawn => {
SWAG.SpawnBots(spawn, globalmap);
});
}
if (customConfigs[ClassDef_1.reverseMapNames[globalmap]]) {
customConfigs[ClassDef_1.reverseMapNames[globalmap]].forEach(custom => {
SWAG.SpawnCustom(custom, globalmap);
});
}
logger.warning(`SWAG: Configured boss spawns for map ${globalmap}`);
});
}
static SpawnBosses(boss, globalmap) {
if (bossConfig_json_1.default.TotalBossesPerMap[ClassDef_1.reverseMapNames[globalmap]] == 0) {
config_json_1.default.DebugOutput &&
logger.info("SWAG: TotalBosses set to 0 for this map, skipping boss spawn");
return;
}
else if (bossConfig_json_1.default.TotalBossesPerMap[ClassDef_1.reverseMapNames[globalmap]] != -1 && (SWAG.bossCount.count >= bossConfig_json_1.default.TotalBossesPerMap[ClassDef_1.reverseMapNames[globalmap]])) {
config_json_1.default.DebugOutput &&
logger.info("SWAG: Skipping boss spawn as total boss count has been met already");
return;
}
else {
let wave = SWAG.ConfigureBossWave(boss, globalmap);
locations[globalmap].base.BossLocationSpawn.push(wave);
}
}
static SpawnBots(boss, globalmap) {
let wave = SWAG.ConfigureBossWave(boss, globalmap);
locations[globalmap].base.BossLocationSpawn.push(wave);
}
static SpawnCustom(boss, globalmap) {
let wave = SWAG.ConfigureBossWave(boss, globalmap);
locations[globalmap].base.BossLocationSpawn.push(wave);
}
static ConfigureBossWave(boss, globalmap) {
let spawnChance = 0;
let spawnZones = boss.BossZone || null;
let bossName = ClassDef_1.roleCase[boss.BossName.toLowerCase()] || boss.BossName;
const getRandomDifficulty = () => {
const availableDifficulties = ["easy", "normal", "hard", "impossible"];
const randomIndex = Math.floor(Math.random() * availableDifficulties.length);
return availableDifficulties[randomIndex];
};
let difficultyKey = boss.BossDifficult || config_json_1.default.BossDifficulty.toLowerCase();
let difficulty = difficultyKey === "asonline" ? getRandomDifficulty() : ClassDef_1.diffProper[difficultyKey];
let escortDifficultyKey = boss.BossEscortDifficult || config_json_1.default.BossEscortDifficulty.toLowerCase();
let escort_difficulty = escortDifficultyKey === "asonline" ? getRandomDifficulty() : ClassDef_1.diffProper[escortDifficultyKey];
boss?.Supports?.forEach((escort) => {
escort.BossEscortDifficult = [escort_difficulty];
escort.BossEscortType = ClassDef_1.roleCase[escort.BossEscortType.toLowerCase()];
});
// exclusive to bosses only
if (boss.BossName.startsWith("boss")) {
spawnChance = this.adjustBossSpawnChance(boss, globalmap);
}
// something other than bosses
else if (config_json_1.default.Spawns.useGlobalSpawnChance) {
spawnChance = config_json_1.default.Spawns[ClassDef_1.reverseBossNames[boss.BossName]][ClassDef_1.reverseMapNames[globalmap]];
}
else {
spawnChance = boss.BossChance || 0;
}
// zones
if (spawnZones != null) {
spawnZones = boss.BossZone || spawnZones;
if (spawnZones.length > 1) {
// let's just pick one zone, can't trust BSG to do this correctly
let random_zone = SWAG.getRandIntInclusive(0, spawnZones.length - 1);
spawnZones = spawnZones[random_zone];
}
// if it's not > 1 and not null, then we'll assume there's a single zone defined instead
else {
spawnZones = spawnZones[0];
}
}
// Using the SPT class here
const wave = {
BossName: bossName,
BossChance: spawnChance,
BossZone: !!spawnZones
? spawnZones
: SWAG.savedLocationData[globalmap].openZones &&
SWAG.savedLocationData[globalmap].openZones.length > 0
? randomUtil.getStringArrayValue(SWAG.savedLocationData[globalmap].openZones)
: "",
BossPlayer: false,
BossDifficult: difficulty,
BossEscortType: ClassDef_1.roleCase[boss.BossEscortType.toLowerCase()],
BossEscortDifficult: escort_difficulty,
BossEscortAmount: boss.BossEscortAmount || "0",
Time: boss.Time || -1,
Supports: boss.Supports || null,
RandomTimeSpawn: boss.RandomTimeSpawn || false,
TriggerId: boss.TriggerId || "",
TriggerName: boss.TriggerName || ""
};
if (spawnChance != 0) {
config_json_1.default.DebugOutput && logger.warning(`Configured Boss Wave: ${JSON.stringify(wave)}`);
}
return wave;
}
static adjustBossSpawnChance(boss, globalmap) {
// I need to refactor this garbage
if (boss.BossName === "bosspunisher") {
if (bossConfig_json_1.default.CustomBosses.punisher.enabled) {
if (bossConfig_json_1.default.CustomBosses.punisher.useProgressSpawnChance) {
const pmcProfile = profileHelper.getPmcProfile(sessionId);
const profileId = pmcProfile?._id;
const punisherBossProgressFilePath = path.resolve(__dirname, `../../WTT-RogueJustice/profiles/${profileId}/progress.json`);
try {
const progressData = JSON.parse(fs.readFileSync(punisherBossProgressFilePath, "utf8"));
return progressData?.actualPunisherChance ?? 1;
}
catch (error) {
logger.warning("SWAG: Unable to load Punisher Boss progress file, either you don't have the mod installed or you don't have a Punisher progress file yet.");
return 1;
}
}
else {
// if progress spawn chance is not enabled
return bossConfig_json_1.default.CustomBosses["punisher"][ClassDef_1.reverseMapNames[globalmap]];
}
}
else {
// if punisher is not enabled
return 0;
}
}
if (boss.BossName === "bosslegion") {
if (bossConfig_json_1.default.CustomBosses.legion.enabled) {
if (bossConfig_json_1.default.CustomBosses.legion.useProgressSpawnChance) {
const legionBossProgressFilePath = path.resolve(__dirname, "../../RaidOverhaul/config/LegionChance.json");
try {
const progressData = JSON.parse(fs.readFileSync(legionBossProgressFilePath, "utf8"));
return progressData?.legionChance ?? 15;
}
catch (error) {
logger.warning("SWAG: Unable to load Legion Boss progress file, either you don't have the mod installed or you deleted your LegionChance.json.");
}
}
// if progress spawn chance is not enabled
return bossConfig_json_1.default.CustomBosses["legion"][ClassDef_1.reverseMapNames[globalmap]];
}
// if legion is not enabled
else {
return 0;
}
}
// all other bosses...
else if (bossConfig_json_1.default.Bosses.useGlobalBossSpawnChance) {
// edge case, only applies to Kaban
if (boss.BossName == "bossboarsniper") {
return boss.BossChance;
}
return bossConfig_json_1.default.Bosses[ClassDef_1.reverseBossNames[boss.BossName]][ClassDef_1.reverseMapNames[globalmap]];
}
// if global boss chance is not enabled
else {
return boss.BossChance;
}
}
static getRandIntInclusive(min, max) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min + 1)) + min;
}
static disableSpawnSystems() {
let map;
for (map in locations) {
if (map === "base" || map === "hideout") {
continue;
}
locations[map].base.OfflineNewSpawn = false;
locations[map].base.OfflineOldSpawn = true;
locations[map].base.NewSpawn = false;
locations[map].base.OldSpawn = true;
}
}
static ClearDefaultSpawns() {
let map;
for (map in locations) {
if (map === "base" || map === "hideout") {
continue;
}
// Save a backup of the wave data and the BossLocationSpawn to use when restoring defaults on raid end. Store openzones in this data as well
if (!SWAG.savedLocationData[map]) {
const locationBase = locations[map].base;
SWAG.savedLocationData[map] = {
waves: locationBase.waves,
BossLocationSpawn: locationBase.BossLocationSpawn,
openZones: this.GetOpenZones(map),
};
}
// Reset Database, Cringe -- i stole this code from LUA
locations[map].base.waves = [...SWAG.savedLocationData[map].waves];
locations[map].base.BossLocationSpawn = [
...SWAG.savedLocationData[map].BossLocationSpawn,
];
//Clear bots spawn
if (!config_json_1.default?.UseDefaultSpawns?.Waves) {
locations[map].base.waves = [];
}
//Clear boss spawn
const bossLocationSpawn = locations[map].base.BossLocationSpawn;
if (!config_json_1.default?.UseDefaultSpawns?.Bosses &&
!config_json_1.default?.UseDefaultSpawns?.TriggeredWaves) {
locations[map].base.BossLocationSpawn = [];
}
else {
// Remove Default Boss Spawns
if (!config_json_1.default?.UseDefaultSpawns?.Bosses) {
for (let i = 0; i < bossLocationSpawn.length; i++) {
// Triggered wave check
if (bossLocationSpawn[i]?.TriggerName?.length === 0) {
locations[map].base.BossLocationSpawn.splice(i--, 1);
}
}
}
// Remove Default Triggered Waves
if (!config_json_1.default?.UseDefaultSpawns?.TriggeredWaves) {
for (let i = 0; i < bossLocationSpawn.length; i++) {
// Triggered wave check
if (bossLocationSpawn[i]?.TriggerName?.length > 0) {
locations[map].base.BossLocationSpawn.splice(i--, 1);
}
}
}
}
}
}
}
module.exports = { mod: new SWAG() };
//# sourceMappingURL=SWAG.js.map
File diff suppressed because one or more lines are too long
+789
View File
@@ -0,0 +1,789 @@
import {
BossLocationSpawn,
ILocationBase,
Wave,
} from "@spt-aki/models/eft/common/ILocationBase";
import { ConfigTypes } from "@spt-aki/models/enums/ConfigTypes";
import { IPostDBLoadMod } from "@spt-aki/models/external/IPostDBLoadMod";
import { IPreAkiLoadMod } from "@spt-aki/models/external/IPreAkiLoadMod";
import { IBotConfig } from "@spt-aki/models/spt/config/IBotConfig";
import { ILocations } from "@spt-aki/models/spt/server/ILocations";
import { ILocationConfig } from "@spt-aki/models/spt/config/ILocationConfig";
import { ILogger } from "@spt-aki/models/spt/utils/ILogger";
import { ConfigServer } from "@spt-aki/servers/ConfigServer";
import { IGlobals } from "@spt-aki/models/eft/common/IGlobals";
import { DatabaseServer } from "@spt-aki/servers/DatabaseServer";
import { ContextVariableType } from "@spt-aki/context/ContextVariableType";
import { ApplicationContext } from "@spt-aki/context/ApplicationContext";
import { WeatherController } from "@spt-aki/controllers/WeatherController";
import { IGetRaidConfigurationRequestData } from "@spt-aki/models/eft/match/IGetRaidConfigurationRequestData";
import { HttpResponseUtil } from "@spt-aki/utils/HttpResponseUtil";
import { StaticRouterModService } from "@spt-aki/services/mod/staticRouter/StaticRouterModService";
import { JsonUtil } from "@spt-aki/utils/JsonUtil";
import { RandomUtil } from "@spt-aki/utils/RandomUtil";
import { DependencyContainer } from "tsyringe";
import { LocationCallbacks } from "@spt-aki/callbacks/LocationCallbacks";
import { SeasonalEventService } from "@spt-aki/services/SeasonalEventService";
import { ProfileHelper } from "@spt-aki/helpers/ProfileHelper";
import * as fs from "fs";
import * as path from "path";
import * as ClassDef from "./ClassDef";
import {
BossPattern,
GroupPattern,
aiAmountProper,
diffProper,
roleCase,
reverseMapNames,
reverseBossNames,
} from "./ClassDef";
// General SWAG Config
import config from "../config/config.json";
import bossConfig from "../config/bossConfig.json";
// Bosses
import gluhar from "../config/bosses/gluhar.json";
import goons from "../config/bosses/goons.json";
import kaban from "../config/bosses/kaban.json";
import killa from "../config/bosses/killa.json";
import kolontay from "../config/bosses/kolontay.json";
import reshala from "../config/bosses/reshala.json";
import sanitar from "../config/bosses/sanitar.json";
import shturman from "../config/bosses/shturman.json";
import tagilla from "../config/bosses/tagilla.json";
import zryachiy from "../config/bosses/zryachiy.json";
// Spawn Configs
import bloodhounds from "../config/other/bloodhounds.json";
import cultists from "../config/other/cultists.json";
import raiders from "../config/other/raiders.json";
import rogues from "../config/other/rogues.json";
import scav_snipers from "../config/other/scav_snipers.json";
// Custom
import punisher from "../config/custom/punisher.json"
import legion from "../config/custom/legion.json"
const otherSpawnConfigs = [
bloodhounds,
cultists,
scav_snipers,
raiders,
rogues
];
const bossSpawnConfigs = [
gluhar,
goons,
kaban,
killa,
kolontay,
reshala,
sanitar,
shturman,
tagilla,
zryachiy
];
const customSpawnConfigs = [
punisher,
legion
]
const modName = "SWAG";
let logger: ILogger;
let LocationCallbacks;
LocationCallbacks;
let jsonUtil;
JsonUtil;
let botConfig: IBotConfig;
let pmcConfig: IBotConfig;
let iGlobals: IGlobals;
let configServer: ConfigServer;
let databaseServer: DatabaseServer;
let locations: ILocations;
let seasonalEvents: SeasonalEventService;
let randomUtil: RandomUtil;
let profileHelper: ProfileHelper;
let sessionId : string;
let BossWaveSpawnedOnceAlready: boolean;
const customPatterns: Record<string, ClassDef.GroupPattern> = {};
type LocationName = keyof Omit<ILocations, "base">;
type LocationBackupData = Record<
LocationName,
| {
waves: Wave[];
BossLocationSpawn: BossLocationSpawn[];
openZones: string[];
}
| undefined
>;
type GlobalPatterns = Record<string, MapPatterns>;
type MapPatterns = {
MapGroups: GroupPattern[];
MapBosses: BossPattern[];
};
class SWAG implements IPreAkiLoadMod, IPostDBLoadMod {
public static savedLocationData: LocationBackupData = {
factory4_day: undefined,
factory4_night: undefined,
bigmap: undefined,
interchange: undefined,
laboratory: undefined,
lighthouse: undefined,
rezervbase: undefined,
shoreline: undefined,
tarkovstreets: undefined,
woods: undefined,
sandbox: undefined,
// unused
develop: undefined,
hideout: undefined,
privatearea: undefined,
suburbs: undefined,
terminal: undefined,
town: undefined,
};
public static pmcType: string[] = ["sptbear", "sptusec"];
public static randomWaveTimer = {
time_min: 0,
time_max: 0,
};
public static actual_timers = {
time_min: 0,
time_max: 0,
};
public static waveCounter = {
count: 1,
};
public static raid_time = {
time_of_day: "day",
};
public static bossCount = {
count: 0,
};
preAkiLoad(container: DependencyContainer): void {
const HttpResponse =
container.resolve<HttpResponseUtil>("HttpResponseUtil");
const staticRouterModService = container.resolve<StaticRouterModService>(
"StaticRouterModService"
);
staticRouterModService.registerStaticRouter(
`${modName}/client/match/offline/end`,
[
{
url: "/client/match/offline/end",
action: (
url: string,
info: any,
sessionID: string,
output: string
): any => {
sessionId = sessionID;
SWAG.ClearDefaultSpawns();
SWAG.ConfigureMaps();
return LocationCallbacks.getLocationData(url, info, sessionID);
},
},
],
"SWAG"
);
staticRouterModService.registerStaticRouter(
`${modName}/client/locations`,
[
{
url: "/client/locations",
action: (
url: string,
info: any,
sessionID: string,
output: string
): any => {
sessionId = sessionID;
SWAG.ClearDefaultSpawns();
SWAG.ConfigureMaps();
return LocationCallbacks.getLocationData(url, info, sessionID);
},
},
],
"SWAG"
);
staticRouterModService.registerStaticRouter(
`${modName}/client/items`,
[
{
url: "/client/items",
action: (
url: string,
info: any,
sessionID: string,
output: string
) => {
sessionId = sessionID;
const locationConfig = container.resolve<ConfigServer>("ConfigServer").getConfig<ILocationConfig>(ConfigTypes.LOCATION);
// as of SPT 3.6.0 we need to disable the new spawn system so that SWAG can clear spawns properly
if (
!config?.UseDefaultSpawns?.Waves ||
!config?.UseDefaultSpawns?.Bosses ||
!config?.UseDefaultSpawns?.TriggeredWaves
) {
SWAG.disableSpawnSystems();
}
// disable more vanilla spawn stuff
locationConfig.splitWaveIntoSingleSpawnsSettings.enabled = false;
locationConfig.rogueLighthouseSpawnTimeSettings.enabled = false;
locationConfig.fixEmptyBotWavesSettings.enabled = false;
locationConfig.addOpenZonesToAllMaps = false;
locationConfig.addCustomBotWavesToMaps = false;
locationConfig.enableBotTypeLimits = false;
logger.info(
"SWAG: Vanilla spawn systems disabled"
);
return output;
},
},
],
"SWAG"
);
staticRouterModService.registerStaticRouter(
`${modName}/client/raid/configuration`,
[{
url: "/client/raid/configuration",
action: (
url: string,
info: any,
sessionID: string,
output: string
): any => {
try {
// Retrieve configurations
const botConfig = container.resolve<ConfigServer>("ConfigServer").getConfig<IBotConfig>(ConfigTypes.BOT);
const pmcConfig = container.resolve<ConfigServer>("ConfigServer").getConfig<IBotConfig>(ConfigTypes.PMC);
// Disable PMC conversion
const conversionTypes = ["assault", "cursedassault", "pmcbot", "exusec", "arenafighter", "arenafighterevent", "crazyassaultevent"];
conversionTypes.forEach(type => {
pmcConfig.convertIntoPmcChance[type] = { min: 0, max: 0 };
});
logger.info("SWAG: PMC conversion is OFF (this is good - be sure this loads AFTER Realism/SVM)");
// Adjust time and map caps
const appContext = container.resolve<ApplicationContext>("ApplicationContext");
const weatherController = container.resolve<WeatherController>("WeatherController");
const matchInfoStartOff = appContext.getLatestValue(ContextVariableType.RAID_CONFIGURATION).getValue<IGetRaidConfigurationRequestData>();
const time = weatherController.generate().time;
let realTime = time;
if (matchInfoStartOff.timeVariant === "PAST") {
let [hours, minutes] = time.split(":").map(Number);
hours = (hours - 12 + 24) % 24; // Adjust time backwards by 12 hours and ensure it wraps correctly
realTime = `${hours}:${minutes}`;
}
// Determine Time of Day
let TOD = "day";
let [hours] = realTime.split(":").map(Number);
if ((matchInfoStartOff.location !== "factory4_night" && hours >= 5 && hours < 22) ||
matchInfoStartOff.location === "factory4_day" ||
matchInfoStartOff.location.toLowerCase() === "laboratory") {
TOD = "day";
} else {
TOD = "night";
}
// Set map caps based on Time of Day
if (TOD === "day") {
Object.keys(config.MaxBotCap).forEach(key => {
botConfig.maxBotCap[key] = config.MaxBotCap[key];
});
} else { // "night"
Object.keys(config.NightMaxBotCap).forEach(key => {
botConfig.maxBotCap[key] = config.NightMaxBotCap[key];
});
}
logger.info(`SWAG: ${TOD} Raid Max Bot Caps set`);
return HttpResponse.nullResponse();
} catch (e) {
logger.error(`SWAG: Failed To modify PMC conversion, you may have more PMCs than you're supposed to. Error: ${e}`);
return HttpResponse.nullResponse();
}
},
}],
"SWAG"
);
}
postDBLoad(container: DependencyContainer): void {
logger = container.resolve<ILogger>("WinstonLogger");
LocationCallbacks =
container.resolve<LocationCallbacks>("LocationCallbacks");
jsonUtil = container.resolve<JsonUtil>("JsonUtil");
configServer = container.resolve<ConfigServer>("ConfigServer");
botConfig = configServer.getConfig<IBotConfig>(ConfigTypes.BOT);
pmcConfig = configServer.getConfig<IBotConfig>(ConfigTypes.PMC);
databaseServer = container.resolve<DatabaseServer>("DatabaseServer");
locations = databaseServer.getTables().locations;
randomUtil = container.resolve<RandomUtil>("RandomUtil");
seasonalEvents = container.resolve<SeasonalEventService>("SeasonalEventService");
profileHelper = container.resolve<ProfileHelper>("ProfileHelper");
}
/**
* Returns all available OpenZones specified in location.base.OpenZones as well as any OpenZone found in the SpawnPointParams.
* Filters out all sniper zones
* @param map
* @returns
*/
static GetOpenZones(map: LocationName): string[] {
const baseobj: ILocationBase = locations[map]?.base;
// Get all OpenZones defined in the base obj that do not include sniper zones. Need to filter for empty strings as well.
const foundOpenZones =
baseobj?.OpenZones?.split(",")
.filter((name) => !name.includes("Snipe"))
.filter((name) => name.trim() !== "") ?? [];
// Sometimes there are zones in the SpawnPointParams that arent listed in the OpenZones, parse these and add them to the list of zones
baseobj?.SpawnPointParams?.forEach((spawn) => {
//check spawn for open zones and if it doesn't exist add to end of array
if (
spawn?.BotZoneName &&
!foundOpenZones.includes(spawn.BotZoneName) &&
!spawn.BotZoneName.includes("Snipe")
) {
foundOpenZones.push(spawn.BotZoneName);
}
});
//logger.info(`SWAG: Open Zones(${map}): ${JSON.stringify(foundOpenZones)}`);
return foundOpenZones;
}
static shuffleArray(array: any[]) {
for (let i = array.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[array[i], array[j]] = [array[j], array[i]];
}
}
static ConfigureMaps(): void {
const bossConfigs: { [key: string]: any[] } = {};
const otherConfigs: { [key: string]: any[] } = {};
const customConfigs: { [key: string]: any[] } = {};
bossSpawnConfigs.forEach(data => {
Object.keys(data).forEach(mapKey => {
if (bossConfig.TotalBossesPerMap[mapKey] === 0 || config.disableAllSpawns.bosses) {
return;
}
if (!bossConfigs[mapKey]) {
bossConfigs[mapKey] = [];
}
const filteredBosses = data[mapKey].filter(boss => {
// ignore boarsniper
if (boss.BossName == "bossboarsniper") {
return false;
}
const shouldSkip = boss.BossChance === 0 ||
(bossConfig.Bosses.useGlobalBossSpawnChance &&
bossConfig.Bosses[reverseBossNames[boss.BossName]][mapKey] === 0);
return !shouldSkip;
});
bossConfigs[mapKey].push(...filteredBosses);
});
});
otherSpawnConfigs.forEach(data => {
Object.keys(data).forEach(mapKey => {
if (!otherConfigs[mapKey]) {
otherConfigs[mapKey] = [];
}
const filteredBosses = data[mapKey].filter(boss => {
const bossType = reverseBossNames[boss.BossName];
if (config.disableAllSpawns[bossType]) {
return false;
}
const shouldSkip = boss.BossChance === 0 ||
(config.Spawns.useGlobalSpawnChance && config.Spawns[bossType][mapKey] === 0);
return !shouldSkip;
});
otherConfigs[mapKey].push(...filteredBosses);
});
});
customSpawnConfigs.forEach(data => {
Object.keys(data).forEach(mapKey => {
if (!customConfigs[mapKey]) {
customConfigs[mapKey] = [];
}
const filteredBosses = data[mapKey].filter(boss => {
if (boss.BossName == "gifter") {
if (!bossConfig.CustomBosses.santa.enabled ||
(!seasonalEvents.christmasEventEnabled() && !bossConfig.CustomBosses.santa.forceSpawnOutsideEvent)) {
return false;
}
}
const shouldSkip = boss.BossChance === 0 ||
!bossConfig.CustomBosses[reverseBossNames[boss.BossName]].enabled ||
(bossConfig.CustomBosses[reverseBossNames[boss.BossName]].enabled &&
bossConfig.CustomBosses[reverseBossNames[boss.BossName]][mapKey] === 0);
return !shouldSkip;
});
customConfigs[mapKey].push(...filteredBosses);
});
});
// Shuffle each array within the configuration objects
Object.values(bossConfigs).forEach(array => this.shuffleArray(array));
Object.values(otherConfigs).forEach(array => this.shuffleArray(array));
Object.values(customConfigs).forEach(array => this.shuffleArray(array));
ClassDef.validMaps.forEach((globalmap: LocationName) => {
if (bossConfigs[reverseMapNames[globalmap]]) {
bossConfigs[reverseMapNames[globalmap]].forEach(boss => {
SWAG.SpawnBosses(boss, globalmap);
SWAG.bossCount.count += 1;
});
}
// reset boss count for the next map
SWAG.bossCount.count = 0;
if (otherConfigs[reverseMapNames[globalmap]]) {
otherConfigs[reverseMapNames[globalmap]].forEach(spawn => {
SWAG.SpawnBots(spawn, globalmap);
});
}
if (customConfigs[reverseMapNames[globalmap]]) {
customConfigs[reverseMapNames[globalmap]].forEach(custom => {
SWAG.SpawnCustom(custom, globalmap);
});
}
logger.warning(`SWAG: Configured boss spawns for map ${globalmap}`);
});
}
static SpawnBosses(
boss: ClassDef.BossPattern,
globalmap: LocationName,
): void {
if (bossConfig.TotalBossesPerMap[reverseMapNames[globalmap]] == 0) {
config.DebugOutput &&
logger.info(
"SWAG: TotalBosses set to 0 for this map, skipping boss spawn"
);
return;
}
else if (bossConfig.TotalBossesPerMap[reverseMapNames[globalmap]] != -1 && (SWAG.bossCount.count >= bossConfig.TotalBossesPerMap[reverseMapNames[globalmap]])) {
config.DebugOutput &&
logger.info(
"SWAG: Skipping boss spawn as total boss count has been met already"
);
return;
}
else {
let wave: BossLocationSpawn = SWAG.ConfigureBossWave(boss, globalmap);
locations[globalmap].base.BossLocationSpawn.push(wave);
}
}
static SpawnBots(
boss: ClassDef.BossPattern,
globalmap: LocationName,
): void {
let wave: BossLocationSpawn = SWAG.ConfigureBossWave(boss, globalmap);
locations[globalmap].base.BossLocationSpawn.push(wave);
}
static SpawnCustom(
boss: ClassDef.BossPattern,
globalmap: LocationName,
): void {
let wave: BossLocationSpawn = SWAG.ConfigureBossWave(boss, globalmap);
locations[globalmap].base.BossLocationSpawn.push(wave);
}
static ConfigureBossWave(boss: BossLocationSpawn, globalmap: LocationName): BossLocationSpawn {
let spawnChance = 0;
let spawnZones = boss.BossZone || null;
let bossName = roleCase[boss.BossName.toLowerCase()] || boss.BossName;
const getRandomDifficulty = () => {
const availableDifficulties = ["easy", "normal", "hard", "impossible"];
const randomIndex = Math.floor(Math.random() * availableDifficulties.length);
return availableDifficulties[randomIndex];
};
let difficultyKey = boss.BossDifficult || config.BossDifficulty.toLowerCase();
let difficulty = difficultyKey === "asonline" ? getRandomDifficulty() : diffProper[difficultyKey];
let escortDifficultyKey = boss.BossEscortDifficult || config.BossEscortDifficulty.toLowerCase();
let escort_difficulty = escortDifficultyKey === "asonline" ? getRandomDifficulty() : diffProper[escortDifficultyKey];
boss?.Supports?.forEach((escort) => {
escort.BossEscortDifficult = [escort_difficulty];
escort.BossEscortType = roleCase[escort.BossEscortType.toLowerCase()];
});
// exclusive to bosses only
if (boss.BossName.startsWith("boss")) {
spawnChance = this.adjustBossSpawnChance(boss, globalmap);
}
// something other than bosses
else if (config.Spawns.useGlobalSpawnChance) {
spawnChance = config.Spawns[reverseBossNames[boss.BossName]][reverseMapNames[globalmap]];
}
else {
spawnChance = boss.BossChance || 0;
}
// zones
if (spawnZones != null) {
spawnZones = boss.BossZone || spawnZones;
if (spawnZones.length > 1) {
// let's just pick one zone, can't trust BSG to do this correctly
let random_zone = SWAG.getRandIntInclusive(0, spawnZones.length - 1);
spawnZones = spawnZones[random_zone];
}
// if it's not > 1 and not null, then we'll assume there's a single zone defined instead
else {
spawnZones = spawnZones[0];
}
}
// Using the SPT class here
const wave: BossLocationSpawn = {
BossName: bossName,
BossChance: spawnChance,
BossZone: !!spawnZones
? spawnZones
: SWAG.savedLocationData[globalmap].openZones &&
SWAG.savedLocationData[globalmap].openZones.length > 0
? randomUtil.getStringArrayValue(
SWAG.savedLocationData[globalmap].openZones
)
: "",
BossPlayer: false,
BossDifficult: difficulty,
BossEscortType: roleCase[boss.BossEscortType.toLowerCase()],
BossEscortDifficult: escort_difficulty,
BossEscortAmount: boss.BossEscortAmount || "0",
Time: boss.Time || -1,
Supports: boss.Supports || null,
RandomTimeSpawn: boss.RandomTimeSpawn || false,
TriggerId: boss.TriggerId || "",
TriggerName: boss.TriggerName || ""
};
if (spawnChance != 0) {
config.DebugOutput && logger.warning(`Configured Boss Wave: ${JSON.stringify(wave)}`);
}
return wave;
}
static adjustBossSpawnChance(boss: BossLocationSpawn, globalmap: LocationName): number {
// I need to refactor this garbage
if (boss.BossName === "bosspunisher") {
if (bossConfig.CustomBosses.punisher.enabled) {
if (bossConfig.CustomBosses.punisher.useProgressSpawnChance) {
const pmcProfile = profileHelper.getPmcProfile(sessionId);
const profileId = pmcProfile?._id;
const punisherBossProgressFilePath = path.resolve(
__dirname,
`../../WTT-RogueJustice/profiles/${profileId}/progress.json`
);
try {
const progressData = JSON.parse(
fs.readFileSync(punisherBossProgressFilePath, "utf8")
);
return progressData?.actualPunisherChance ?? 1;
} catch (error) {
logger.warning(
"SWAG: Unable to load Punisher Boss progress file, either you don't have the mod installed or you don't have a Punisher progress file yet.",
);
return 1;
}
} else {
// if progress spawn chance is not enabled
return bossConfig.CustomBosses["punisher"][reverseMapNames[globalmap]];
}
} else {
// if punisher is not enabled
return 0;
}
}
if (boss.BossName === "bosslegion") {
if (bossConfig.CustomBosses.legion.enabled) {
if (bossConfig.CustomBosses.legion.useProgressSpawnChance) {
const legionBossProgressFilePath = path.resolve(
__dirname,
"../../RaidOverhaul/config/LegionChance.json"
);
try {
const progressData = JSON.parse(
fs.readFileSync(legionBossProgressFilePath, "utf8")
);
return progressData?.legionChance ?? 15;
} catch (error) {
logger.warning(
"SWAG: Unable to load Legion Boss progress file, either you don't have the mod installed or you deleted your LegionChance.json."
);
}
}
// if progress spawn chance is not enabled
return bossConfig.CustomBosses["legion"][reverseMapNames[globalmap]];
}
// if legion is not enabled
else {
return 0;
}
}
// all other bosses...
else if (bossConfig.Bosses.useGlobalBossSpawnChance) {
// edge case, only applies to Kaban
if (boss.BossName == "bossboarsniper") {
return boss.BossChance;
}
return bossConfig.Bosses[reverseBossNames[boss.BossName]][reverseMapNames[globalmap]];
}
// if global boss chance is not enabled
else {
return boss.BossChance;
}
}
static getRandIntInclusive(min: number, max: number): number {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min + 1)) + min;
}
static disableSpawnSystems(): void {
let map: keyof ILocations;
for (map in locations) {
if (map === "base" || map === "hideout") {
continue;
}
locations[map].base.OfflineNewSpawn = false;
locations[map].base.OfflineOldSpawn = true;
locations[map].base.NewSpawn = false;
locations[map].base.OldSpawn = true;
}
}
static ClearDefaultSpawns(): void {
let map: keyof ILocations;
for (map in locations) {
if (map === "base" || map === "hideout") {
continue;
}
// Save a backup of the wave data and the BossLocationSpawn to use when restoring defaults on raid end. Store openzones in this data as well
if (!SWAG.savedLocationData[map]) {
const locationBase = locations[map].base;
SWAG.savedLocationData[map] = {
waves: locationBase.waves,
BossLocationSpawn: locationBase.BossLocationSpawn,
openZones: this.GetOpenZones(map),
};
}
// Reset Database, Cringe -- i stole this code from LUA
locations[map].base.waves = [...SWAG.savedLocationData[map].waves];
locations[map].base.BossLocationSpawn = [
...SWAG.savedLocationData[map].BossLocationSpawn,
];
//Clear bots spawn
if (!config?.UseDefaultSpawns?.Waves) {
locations[map].base.waves = [];
}
//Clear boss spawn
const bossLocationSpawn = locations[map].base.BossLocationSpawn;
if (
!config?.UseDefaultSpawns?.Bosses &&
!config?.UseDefaultSpawns?.TriggeredWaves
) {
locations[map].base.BossLocationSpawn = [];
} else {
// Remove Default Boss Spawns
if (!config?.UseDefaultSpawns?.Bosses) {
for (let i = 0; i < bossLocationSpawn.length; i++) {
// Triggered wave check
if (bossLocationSpawn[i]?.TriggerName?.length === 0) {
locations[map].base.BossLocationSpawn.splice(i--, 1);
}
}
}
// Remove Default Triggered Waves
if (!config?.UseDefaultSpawns?.TriggeredWaves) {
for (let i = 0; i < bossLocationSpawn.length; i++) {
// Triggered wave check
if (bossLocationSpawn[i]?.TriggerName?.length > 0) {
locations[map].base.BossLocationSpawn.splice(i--, 1);
}
}
}
}
}
}
module.exports = { mod: new SWAG() };