diff --git a/mods/Config Files/BepInEx/config/MOAR.settings.cfg b/mods/Config Files/BepInEx/config/MOAR.settings.cfg index 25e46ff..3a638f8 100644 --- a/mods/Config Files/BepInEx/config/MOAR.settings.cfg +++ b/mods/Config Files/BepInEx/config/MOAR.settings.cfg @@ -1,8 +1,23 @@ -## Settings file was created by plugin MOAR v2.5.6 +## Settings file was created by plugin MOAR v2.6.7 ## Plugin GUID: MOAR.settings [1. Main Settings] +## All Bots/Players (excluding bosses) can spawn anywhere (overrides PMC/Player openzone options) +# Setting type: Boolean +# Default value: false +PMC/Scav/Player OpenZones On/Off = false + +## Adds a large number of zones (including all scav zones) to pmc bots spawn pool +# Setting type: Boolean +# Default value: true +PMC OpenZones On/Off = true + +## Adds a large number of zones to the Player's (you) spawn pool +# Setting type: Boolean +# Default value: false +Player OpenZones On/Off = false + ## Causes all PMCs to spawn in the first few minutes of the game (performance intensive) # Setting type: Boolean # Default value: false @@ -16,9 +31,9 @@ Pmc difficulty = 0.6 ## Works with SAIN or SPT to decide the bot's 'difficulty' preset (EASY: 0, easy-MEDIUM: 0.4, easy-MEDIUM-hard: 0.6, medium-hard: 0.85, HARD-impossible: 1, etc..) # Setting type: Double -# Default value: 0.3 +# Default value: 0.4 # Acceptable value range: From 0 to 1.5 -Scav difficulty = 0.3 +Scav difficulty = 0.4 ## Preset to be used, random pulls a random weighted preset from the PresetWeights.json every time a raid ends # Setting type: String @@ -36,12 +51,6 @@ Preset Announce On/Off = true # Default value: FIKA DETECTED: ALWAYS PRESS THIS FIRST BEFORE MAKING CHANGES!! = -PMC/Scav/Player OpenZones On/Off = false - -PMC OpenZones On/Off = true - -Player OpenZones On/Off = true - [2. Custom game Settings] ## Pushes settings to server @@ -63,7 +72,7 @@ gradualBossInvasion On/Off = true # Setting type: Int32 # Default value: 5 # Acceptable value range: From 0 to 100 -bossInvasionSpawnChance = 5 +bossInvasionSpawnChance = 0 ## Allows the main bosses (not knight,rogues,raiders) to invade other maps with a reduced retinue, by default they will spawn in native boss locations # Setting type: Boolean @@ -123,23 +132,23 @@ scavMaxGroupSize = 4 # Setting type: Int32 # Default value: 4 # Acceptable value range: From 0 to 10 -pmcMaxGroupSize = 4 +pmcMaxGroupSize = 5 ## Increases chances of pmc groups spawning, doesn't dramatically increase quantity. # Setting type: Boolean # Default value: false -morePmcGroups On/Off = false +morePmcGroups On/Off = true ## Increases chances of scav groups spawning, doesn't dramatically increase quantity. # Setting type: Boolean # Default value: false -moreScavGroups On/Off = false +moreScavGroups On/Off = true ## Max bots permitted in any particular spawn zone, recommend not to touch this. # Setting type: Int32 -# Default value: 7 +# Default value: 5 # Acceptable value range: From 0 to 15 -MaxBotPerZone = 7 +MaxBotPerZone = 5 ## Max bots alive at one time # Setting type: Int32 @@ -157,34 +166,34 @@ ZombieHealth = 1 # Setting type: Double # Default value: 1 # Acceptable value range: From 0 to 10 -ZombieWaveQuantity = 1 +ZombieWaveQuantity = 1.5 ## Determines the weighting of spawns at the beginning (1) spread evenly throughout (0.5) or at the end(0) of the raid # Setting type: Double # Default value: 0.5 # Acceptable value range: From 0 to 1 -ZombieWaveDistribution = 0.5 +ZombieWaveDistribution = 0.2 ## Enables zombies to spawn # Setting type: Boolean # Default value: false -zombiesEnabled On/Off = false +zombiesEnabled On/Off = true ## Determines the weighting of spawns at the beginning (1) spread evenly throughout (0.5) or at the end(0) of the raid # Setting type: Double -# Default value: 0.3 +# Default value: 0.5 # Acceptable value range: From 0 to 1 -ScavWaveDistribution = 0.3 +ScavWaveDistribution = 0.5 ## Determines the weighting of spawns at the beginning (1) spread evenly throughout (0.5) or at the end(0) of the raid # Setting type: Double -# Default value: 0.8 +# Default value: 0.7 # Acceptable value range: From 0 to 1 -PmcWaveDistribution = 0.8 +PmcWaveDistribution = 0.7 ## Multiplies wave counts seen in the server's mapConfig.json by this number # Setting type: Double -# Default value: 0.5 +# Default value: 1 # Acceptable value range: From 0 to 10 ScavWaveQuantity = 1 @@ -192,7 +201,7 @@ ScavWaveQuantity = 1 # Setting type: Double # Default value: 1 # Acceptable value range: From 0 to 10 -PmcWaveQuantity = 1.2 +PmcWaveQuantity = 1.5 [3.Debug] diff --git a/mods/Dynamic Goons/user/mods/inory-dynamicgoons/src/db/rotationData.json b/mods/Dynamic Goons/user/mods/inory-dynamicgoons/src/db/rotationData.json index a0576ef..ea6eb17 100644 --- a/mods/Dynamic Goons/user/mods/inory-dynamicgoons/src/db/rotationData.json +++ b/mods/Dynamic Goons/user/mods/inory-dynamicgoons/src/db/rotationData.json @@ -1,6 +1,6 @@ { - "nextUpdateTime": 1736141944920, - "selectedMap": "woods", + "nextUpdateTime": 1736820722143, + "selectedMap": "lighthouse", "lastRotationInterval": 180, - "lastUpdateTime": 1736131144920 + "lastUpdateTime": 1736809922143 } \ No newline at end of file diff --git a/mods/MOAR - Ultra Lite Spawn Mod/BepInEx/plugins/MOAR.dll b/mods/MOAR - Ultra Lite Spawn Mod/BepInEx/plugins/MOAR.dll index 1da0b44..a26e85e 100644 Binary files a/mods/MOAR - Ultra Lite Spawn Mod/BepInEx/plugins/MOAR.dll and b/mods/MOAR - Ultra Lite Spawn Mod/BepInEx/plugins/MOAR.dll differ diff --git a/mods/MOAR - Ultra Lite Spawn Mod/meta.ini b/mods/MOAR - Ultra Lite Spawn Mod/meta.ini index 655bf11..dbdc838 100644 --- a/mods/MOAR - Ultra Lite Spawn Mod/meta.ini +++ b/mods/MOAR - Ultra Lite Spawn Mod/meta.ini @@ -1,11 +1,11 @@ [General] gameName=spt modid=0 -version=d2024.12.31.0 +version=d2025.1.13.0 newestVersion= category="1," nexusFileStatus=1 -installationFile=DewardianDev-MOAR-2.6.1.zip +installationFile=DewardianDev-MOAR-2.6.7.zip repository=Nexus ignoredVersion= comments= diff --git a/mods/MOAR - Ultra Lite Spawn Mod/user/mods/DewardianDev-MOAR/config/Presets.json b/mods/MOAR - Ultra Lite Spawn Mod/user/mods/DewardianDev-MOAR/config/Presets.json index 7682a97..8395eec 100644 --- a/mods/MOAR - Ultra Lite Spawn Mod/user/mods/DewardianDev-MOAR/config/Presets.json +++ b/mods/MOAR - Ultra Lite Spawn Mod/user/mods/DewardianDev-MOAR/config/Presets.json @@ -6,12 +6,13 @@ "scavWaveQuantity": 1.2 }, "more-pmcs": { + "scavWaveDistribution": 0.4, "morePmcGroups": true, "pmcMaxGroupSize": 5, "pmcWaveQuantity": 1.2 }, "more-scavs-and-pmcs": { - "maxBotCap": 30, + "scavWaveDistribution": 0.4, "moreScavGroups": true, "scavMaxGroupSize": 5, "morePmcGroups": true, @@ -39,8 +40,6 @@ "scavWaveDistribution": 0.4, "scavWaveQuantity": 1.3, "pmcWaveQuantity": 1.3, - "maxBotCap": 30, - "maxBotPerZone": 9, "moreScavGroups": true, "morePmcGroups": true, "pmcMaxGroupSize": 6, diff --git a/mods/MOAR - Ultra Lite Spawn Mod/user/mods/DewardianDev-MOAR/config/config.json b/mods/MOAR - Ultra Lite Spawn Mod/user/mods/DewardianDev-MOAR/config/config.json index 4d7eeff..a8ebdce 100644 --- a/mods/MOAR - Ultra Lite Spawn Mod/user/mods/DewardianDev-MOAR/config/config.json +++ b/mods/MOAR - Ultra Lite Spawn Mod/user/mods/DewardianDev-MOAR/config/config.json @@ -2,14 +2,18 @@ "enableBotSpawning": true, "pmcDifficulty": 0.6, - "scavDifficulty": 0.3, + "scavDifficulty": 0.4, - "scavWaveDistribution": 0.3, - "scavWaveQuantity": 0.5, + "scavWaveDistribution": 0.5, + "scavWaveQuantity": 1, "startingPmcs": false, - "pmcWaveDistribution": 0.8, + "playerOpenZones": false, + "pmcOpenZones": true, + "allOpenZones": false, + + "pmcWaveDistribution": 0.7, "pmcWaveQuantity": 1, "zombiesEnabled": false, @@ -18,7 +22,7 @@ "zombieHealth": 1, "maxBotCap": 25, - "maxBotPerZone": 7, + "maxBotPerZone": 5, "moreScavGroups": false, "morePmcGroups": false, diff --git a/mods/MOAR - Ultra Lite Spawn Mod/user/mods/DewardianDev-MOAR/config/mapConfig.json b/mods/MOAR - Ultra Lite Spawn Mod/user/mods/DewardianDev-MOAR/config/mapConfig.json index 8c3da1e..7871bbb 100644 --- a/mods/MOAR - Ultra Lite Spawn Mod/user/mods/DewardianDev-MOAR/config/mapConfig.json +++ b/mods/MOAR - Ultra Lite Spawn Mod/user/mods/DewardianDev-MOAR/config/mapConfig.json @@ -5,14 +5,10 @@ "scavWaveCount": 21, "zombieWaveCount": 9, "scavHotZones": [ - "ZoneDormitory", - "ZoneCrossRoad", - "ZoneGasStation" + "ZoneDormitory" ], "pmcHotZones": [ - "ZoneDormitory", - "ZoneGasStation", - "ZoneCustoms" + "ZoneDormitory" ] }, "factoryDay": { @@ -39,11 +35,6 @@ "scavHotZones": [ "ZoneCenterBot", "ZoneCenter" - ], - "pmcHotZones": [ - "ZoneIDEA", - "ZoneOLI", - "ZoneCenter" ] }, "laboratory": { @@ -59,12 +50,7 @@ "zombieWaveCount": 10, "scavHotZones": [ "Zone_LongRoad", - "Zone_Village" - ], - "pmcHotZones": [ - "Zone_DestroyedHouse", - "Zone_Chalet", - "Zone_Village" + "Zone_LongRoad" ] }, "rezervbase": { @@ -73,13 +59,10 @@ "scavWaveCount": 24, "zombieWaveCount": 9, "scavHotZones": [ - "ZoneRailStrorage", - "ZoneBunkerStorage", - "ZoneBarrack" + "ZoneRailStrorage" ], "pmcHotZones": [ - "ZoneBarrack", - "ZoneBunkerStorage" + "ZoneBarrack" ] }, "shoreline": { @@ -88,34 +71,17 @@ "scavWaveCount": 32, "zombieWaveCount": 12, "scavHotZones": [ - "ZoneSanatorium1", - "ZoneGasStation", - "ZonePowerStation", - "ZoneBusStation", - "ZoneStartVillage" + "ZoneSanatorium1" ], "pmcHotZones": [ - "ZoneSanatorium2", - "ZoneGasStation", - "ZonePowerStation" + "ZoneSanatorium2" ] }, "tarkovstreets": { "spawnMinDistance": 40, "pmcWaveCount": 16, "scavWaveCount": 28, - "zombieWaveCount": 13, - "scavHotZones": [ - "ZoneHotel_2", - "ZoneHotel_1", - "ZoneConstruction", - "ZoneCarShowroom" - ], - "pmcHotZones": [ - "ZoneSanatorium2", - "ZoneCinema", - "ZoneConcordiaParking" - ] + "zombieWaveCount": 13 }, "woods": { "spawnMinDistance": 40, @@ -123,15 +89,10 @@ "scavWaveCount": 28, "zombieWaveCount": 10, "scavHotZones": [ - "ZoneWoodCutter", - "ZoneClearVill", - "ZoneScavBase2", - "ZoneRedHouse" + "ZoneWoodCutter" ], "pmcHotZones": [ - "ZoneWoodCutter", - "ZoneBigRocks", - "ZoneHighRocks" + "ZoneWoodCutter" ] }, "gzLow": { diff --git a/mods/MOAR - Ultra Lite Spawn Mod/user/mods/DewardianDev-MOAR/package.json b/mods/MOAR - Ultra Lite Spawn Mod/user/mods/DewardianDev-MOAR/package.json index a8bbb14..4521a92 100644 --- a/mods/MOAR - Ultra Lite Spawn Mod/user/mods/DewardianDev-MOAR/package.json +++ b/mods/MOAR - Ultra Lite Spawn Mod/user/mods/DewardianDev-MOAR/package.json @@ -1,6 +1,6 @@ { "name": "MOAR", - "version": "2.6.1", + "version": "2.6.7", "main": "src/mod.js", "license": "MIT", "author": "DewardianDev", diff --git a/mods/MOAR - Ultra Lite Spawn Mod/user/mods/DewardianDev-MOAR/src/Routes/routes.ts b/mods/MOAR - Ultra Lite Spawn Mod/user/mods/DewardianDev-MOAR/src/Routes/routes.ts index bbbe886..7090b72 100644 --- a/mods/MOAR - Ultra Lite Spawn Mod/user/mods/DewardianDev-MOAR/src/Routes/routes.ts +++ b/mods/MOAR - Ultra Lite Spawn Mod/user/mods/DewardianDev-MOAR/src/Routes/routes.ts @@ -1,7 +1,7 @@ import { DependencyContainer } from "tsyringe"; import { buildWaves } from "../Spawning/Spawning"; import { StaticRouterModService } from "@spt/services/mod/staticRouter/StaticRouterModService"; -import { DynamicRouterModService } from "@spt/services/mod/dynamicRouter/DynamicRouterModService"; +// import { DynamicRouterModService } from "@spt/services/mod/dynamicRouter/DynamicRouterModService"; import { globalValues } from "../GlobalValues"; import { kebabToTitle } from "../utils"; import PresetWeightingsConfig from "../../config/PresetWeightings.json"; @@ -11,9 +11,9 @@ export const setupRoutes = (container: DependencyContainer) => { "StaticRouterModService" ); - const dynamicRouterModService = container.resolve( - "DynamicRouterModService" - ); + // const dynamicRouterModService = container.resolve( + // "DynamicRouterModService" + // ); // Make buildwaves run on game end staticRouterModService.registerStaticRouter( diff --git a/mods/MOAR - Ultra Lite Spawn Mod/user/mods/DewardianDev-MOAR/src/Spawning/Spawning.ts b/mods/MOAR - Ultra Lite Spawn Mod/user/mods/DewardianDev-MOAR/src/Spawning/Spawning.ts index 1aaad28..1ac5549 100644 --- a/mods/MOAR - Ultra Lite Spawn Mod/user/mods/DewardianDev-MOAR/src/Spawning/Spawning.ts +++ b/mods/MOAR - Ultra Lite Spawn Mod/user/mods/DewardianDev-MOAR/src/Spawning/Spawning.ts @@ -7,7 +7,11 @@ import { ConfigServer } from "@spt/servers/ConfigServer"; import { ConfigTypes } from "@spt/models/enums/ConfigTypes"; import { DependencyContainer } from "tsyringe"; import { globalValues } from "../GlobalValues"; -import { cloneDeep, getRandomPresetOrCurrentlySelectedPreset } from "../utils"; +import { + cloneDeep, + getRandomPresetOrCurrentlySelectedPreset, + saveToFile, +} from "../utils"; import { ILocationConfig } from "@spt/models/spt/config/ILocationConfig.d"; import { originalMapList } from "./constants"; import { buildBossWaves } from "./buildBossWaves"; @@ -63,12 +67,12 @@ export const buildWaves = (container: DependencyContainer) => { } }); - config.debug && - console.log( - globalValues.forcedPreset === "custom" - ? "custom" - : globalValues.currentPreset - ); + // config.debug && + console.log( + globalValues.forcedPreset === "custom" + ? "custom" + : globalValues.currentPreset + ); const { bigmap: customs, @@ -127,7 +131,7 @@ export const buildWaves = (container: DependencyContainer) => { rezervbase: { pmcbot: { min: 0, max: 0 } }, }; - updateSpawnLocations(locationList); + updateSpawnLocations(locationList, config); setEscapeTimeOverrides(locationList, _mapConfig, Logger, config); diff --git a/mods/MOAR - Ultra Lite Spawn Mod/user/mods/DewardianDev-MOAR/src/Spawning/buildBossWaves.ts b/mods/MOAR - Ultra Lite Spawn Mod/user/mods/DewardianDev-MOAR/src/Spawning/buildBossWaves.ts index 02853e7..85d7d6d 100644 --- a/mods/MOAR - Ultra Lite Spawn Mod/user/mods/DewardianDev-MOAR/src/Spawning/buildBossWaves.ts +++ b/mods/MOAR - Ultra Lite Spawn Mod/user/mods/DewardianDev-MOAR/src/Spawning/buildBossWaves.ts @@ -139,36 +139,20 @@ export function buildBossWaves( for (let key = 0; key < locationList.length; key++) { //Gather bosses to avoid duplicating. - let bossLocations = ""; const duplicateBosses = [ ...locationList[key].base.BossLocationSpawn.filter( - ({ BossName, BossZone }) => { - bossLocations += BossZone + ","; - return bossList.includes(BossName); - } + ({ BossName, BossZone }) => bossList.includes(BossName) ).map(({ BossName }) => BossName), "bossKnight", // So knight doesn't invade ]; - const uniqueBossZones = bossOpenZones - ? "" - : [ - ...new Set( - bossLocations - .split(",") - .filter( - (zone) => !!zone && !zone.toLowerCase().includes("snipe") - ) - ), - ].join(","); - //Build bosses to add const bossesToAdd = shuffle(Object.values(bosses)) .filter(({ BossName }) => !duplicateBosses.includes(BossName)) .map((boss, j) => ({ ...boss, - BossZone: uniqueBossZones, + BossZone: "", BossEscortAmount: boss.BossEscortAmount === "0" ? boss.BossEscortAmount : "1", ...(gradualBossInvasion ? { Time: j * 20 + 1 } : {}), @@ -268,7 +252,22 @@ export function buildBossWaves( configLocations[index] }: ${bossesToAdd.map(({ BossName }) => BossName)}` ); + // console.log(locationList[index].base.BossLocationSpawn.length); + + // Apply the percentages on all bosses, cull those that won't spawn, make all bosses 100 chance that remain. + locationList[index].base.BossLocationSpawn = locationList[ + index + ].base.BossLocationSpawn.filter(({ BossChance, BossName }, bossIndex) => { + if (BossChance < 100 && BossChance / 100 < Math.random()) { + return false; + } + return true; + }).map((boss) => ({ ...boss, ...{ BossChance: 100 } })); + + // if (mapName === "customs") + // console.log(mapName, locationList[index].base.BossLocationSpawn); }); + if (hasChangedBossSpawns) { console.log( `[MOAR]: --- Adjusting default boss spawn rates complete --- \n` diff --git a/mods/MOAR - Ultra Lite Spawn Mod/user/mods/DewardianDev-MOAR/src/Spawning/buildPmcs.ts b/mods/MOAR - Ultra Lite Spawn Mod/user/mods/DewardianDev-MOAR/src/Spawning/buildPmcs.ts index 8b19992..52c60d4 100644 --- a/mods/MOAR - Ultra Lite Spawn Mod/user/mods/DewardianDev-MOAR/src/Spawning/buildPmcs.ts +++ b/mods/MOAR - Ultra Lite Spawn Mod/user/mods/DewardianDev-MOAR/src/Spawning/buildPmcs.ts @@ -30,25 +30,21 @@ export default function buildPmcs( .filter( ({ Categories, BotZoneName }) => !!BotZoneName && - (Categories.includes("Player") || - (map === "laboratory" && - !BotZoneName.includes("BotZoneGate"))) && - !BotZoneName.includes("snipe") + !BotZoneName.includes("snipe") && + (Categories.includes("Player") || Categories.includes("All")) && + !BotZoneName.includes("BotZoneGate") ) .map(({ BotZoneName, ...rest }) => { return BotZoneName; }) ), - ...pmcHotZones, ]); + // Make labs have only named zones if (map === "laboratory") { pmcZones = new Array(10).fill(pmcZones).flat(1); - // console.log(pmcZones); } - const timeLimit = locationList[index].base.EscapeTimeLimit * 60; - const { pmcWaveCount } = mapConfig[map]; const escapeTimeLimitRatio = Math.round( @@ -58,13 +54,12 @@ export default function buildPmcs( const totalWaves = Math.round( pmcWaveCount * config.pmcWaveQuantity * escapeTimeLimitRatio ); - // console.log(pmcZones.length, totalWaves); + const numberOfZoneless = totalWaves - pmcZones.length; if (numberOfZoneless > 0) { const addEmpty = new Array(numberOfZoneless).fill(""); pmcZones = shuffle([...pmcZones, ...addEmpty]); } - // if (map === "laboratory") console.log(numberOfZoneless, pmcZones); if (config.debug) { console.log(`${map} PMC count ${totalWaves} \n`); @@ -75,10 +70,16 @@ export default function buildPmcs( ); } - const waves = buildPmcWaves(pmcWaveCount, timeLimit, config, pmcZones); - // if (map === "laboratory") - // console.log(waves.map(({ BossZone }) => BossZone)); - // apply our new waves + const timeLimit = locationList[index].base.EscapeTimeLimit * 60; + + const waves = buildPmcWaves( + totalWaves, + timeLimit, + config, + pmcZones, + pmcHotZones + ); + locationList[index].base.BossLocationSpawn = [ ...waves, ...locationList[index].base.BossLocationSpawn, diff --git a/mods/MOAR - Ultra Lite Spawn Mod/user/mods/DewardianDev-MOAR/src/Spawning/buildScavMarksmanWaves.ts b/mods/MOAR - Ultra Lite Spawn Mod/user/mods/DewardianDev-MOAR/src/Spawning/buildScavMarksmanWaves.ts index 93b9baf..9d58dae 100644 --- a/mods/MOAR - Ultra Lite Spawn Mod/user/mods/DewardianDev-MOAR/src/Spawning/buildScavMarksmanWaves.ts +++ b/mods/MOAR - Ultra Lite Spawn Mod/user/mods/DewardianDev-MOAR/src/Spawning/buildScavMarksmanWaves.ts @@ -89,19 +89,16 @@ export default function buildScavMarksmanWaves( const sniperLocations = new Set( [...locationList[index].base.SpawnPointParams] .filter( - ({ Categories, Sides, BotZoneName }) => - !!BotZoneName && - Sides.includes("Savage") && - !Categories.includes("Boss") + ({ Categories, DelayToCanSpawnSec, BotZoneName, Sides }) => + !Categories.includes("Boss") && + Sides[0] === "Savage" && + (BotZoneName?.toLowerCase().includes("snipe") || + DelayToCanSpawnSec > 40) ) - .filter( - ({ BotZoneName, DelayToCanSpawnSec }) => - BotZoneName?.toLowerCase().includes("snipe") || - DelayToCanSpawnSec > 300 - ) - .map(({ BotZoneName }) => BotZoneName) + .map(({ BotZoneName }) => BotZoneName || "") ); + if (sniperLocations.size) { locationList[index].base.MinMaxBots = [ { @@ -112,32 +109,21 @@ export default function buildScavMarksmanWaves( ]; } - const scavZones = shuffle([ + let scavZones = shuffle([ ...new Set( [...locationList[index].base.SpawnPointParams] .filter( ({ Categories, Sides, BotZoneName }) => !!BotZoneName && - Sides.includes("Savage") && - !Categories.includes("Boss") + Categories.includes("Bot") && + (Sides.includes("Savage") || Sides.includes("All")) ) .map(({ BotZoneName }) => BotZoneName) .filter((name) => !sniperLocations.has(name)) ), ]); - // Reduced Zone Delay - locationList[index].base.SpawnPointParams = locationList[ - index - ].base.SpawnPointParams.map((spawn) => ({ - ...spawn, - DelayToCanSpawnSec: - spawn.DelayToCanSpawnSec > 20 - ? Math.round(spawn.DelayToCanSpawnSec / 10) - : spawn.DelayToCanSpawnSec, - })); - const timeLimit = locationList[index].base.EscapeTimeLimit * 60; const { scavWaveCount } = mapConfig[map]; const escapeTimeLimitRatio = Math.round( @@ -149,12 +135,19 @@ export default function buildScavMarksmanWaves( scavWaveCount * scavWaveQuantity * escapeTimeLimitRatio ); + const numberOfZoneless = scavTotalWaveCount - scavZones.length; + // console.log(numberOfZoneless); + if (numberOfZoneless > 0) { + const addEmpty = new Array(numberOfZoneless).fill(""); + scavZones = shuffle([...scavZones, ...addEmpty]); + } + // console.log(scavZones); config.debug && escapeTimeLimitRatio !== 1 && console.log( `${map} Scav wave count changed from ${scavWaveCount} to ${scavTotalWaveCount} due to escapeTimeLimit adjustment` ); - + const timeLimit = locationList[index].base.EscapeTimeLimit * 60; let snipers = waveBuilder( sniperLocations.size, Math.round(timeLimit / 4), @@ -166,14 +159,13 @@ export default function buildScavMarksmanWaves( [], shuffle([...sniperLocations]), 80, - false, + true, true ); if (snipersHaveFriends) snipers = snipers.map((wave) => ({ ...wave, - slots_min: 0, ...(snipersHaveFriends && wave.slots_max < 2 ? { slots_min: 1, slots_max: 2 } : {}), diff --git a/mods/MOAR - Ultra Lite Spawn Mod/user/mods/DewardianDev-MOAR/src/Spawning/updateSpawnLocations.ts b/mods/MOAR - Ultra Lite Spawn Mod/user/mods/DewardianDev-MOAR/src/Spawning/updateSpawnLocations.ts index f4464b8..605f951 100644 --- a/mods/MOAR - Ultra Lite Spawn Mod/user/mods/DewardianDev-MOAR/src/Spawning/updateSpawnLocations.ts +++ b/mods/MOAR - Ultra Lite Spawn Mod/user/mods/DewardianDev-MOAR/src/Spawning/updateSpawnLocations.ts @@ -1,36 +1,137 @@ import { ILocation } from "@spt/models/eft/common/ILocation"; import { configLocations } from "./constants"; import mapConfig from "../../config/mapConfig.json"; +import _config from "../../config/config.json"; -export default function updateSpawnLocations(locationList: ILocation[]) { +export default function updateSpawnLocations( + locationList: ILocation[], + config: typeof _config +) { for (let index = 0; index < locationList.length; index++) { const map = configLocations[index]; - + // console.log(map); const limit = mapConfig[map].spawnMinDistance; + const InfiltrationList = [ + ...new Set( + locationList[index].base.SpawnPointParams.filter( + ({ Infiltration }) => Infiltration + ).map(({ Infiltration }) => Infiltration) + ), + ]; + + // console.log(map, InfiltrationList); + const getRandomInfil = (): string => + InfiltrationList[Math.floor(Math.random() * InfiltrationList.length)]; + // console.log(InfiltrationList); // console.log("\n" + map); locationList[index].base.SpawnPointParams.forEach( ( - { ColliderParams, BotZoneName, DelayToCanSpawnSec, Categories, Sides }, + { + ColliderParams, + BotZoneName, + DelayToCanSpawnSec, + Categories, + Sides, + Infiltration, + }, innerIndex ) => { if ( - ColliderParams?._props?.Radius !== undefined && - ColliderParams?._props?.Radius < limit && + !Categories.includes("Boss") && !BotZoneName?.toLowerCase().includes("snipe") && - DelayToCanSpawnSec < 300 + DelayToCanSpawnSec < 41 ) { - // console.log( - // "----", - // ColliderParams._props.Radius, - // "=>", - // limit, - // BotZoneName - // ); + // Make it so players/pmcs can spawn anywhere. + if ( + config.playerOpenZones && + !!Infiltration && + (Sides.includes("Pmc") || Sides.includes("All")) + ) { + locationList[index].base.SpawnPointParams[innerIndex].Categories = [ + "Player", + "Coop", + innerIndex % 2 === 0 ? "Group" : "Opposite", + ]; - locationList[index].base.SpawnPointParams[ - innerIndex - ].ColliderParams._props.Radius = limit; + locationList[index].base.SpawnPointParams[innerIndex].Sides = [ + "Pmc", + "All", + ]; + // console.log( + // BotZoneName || "none", + // locationList[index].base.SpawnPointParams[innerIndex].Categories, + // locationList[index].base.SpawnPointParams[innerIndex].Sides + // ); + } + if (!Infiltration) { + if ( + !config.allOpenZones && + config.pmcOpenZones && + Categories.includes("Bot") && + Sides[0] === "Savage" + ) { + // if (BotZoneName === "Zone_LongRoad") console.log("yes"); + locationList[index].base.SpawnPointParams[innerIndex].Categories = + ["Player", "Bot"]; + + locationList[index].base.SpawnPointParams[ + innerIndex + ].Infiltration = getRandomInfil(); + } + + if (config.allOpenZones) { + locationList[index].base.SpawnPointParams[innerIndex].Categories = + [ + "Bot", + "Player", + "Coop", + innerIndex % 2 === 0 ? "Group" : "Opposite", + ]; + + locationList[index].base.SpawnPointParams[ + innerIndex + ].Infiltration = getRandomInfil(); + // console.log( + // locationList[index].base.SpawnPointParams[innerIndex].Infiltration + // ); + locationList[index].base.SpawnPointParams[innerIndex].Sides = [ + "Pmc", + "Savage", + "All", + ]; + } + + if (config.bossOpenZones && Categories.includes("Bot")) { + locationList[index].base.SpawnPointParams[ + innerIndex + ].Categories.push("Boss"); + } + } + + if ( + ColliderParams?._props?.Radius !== undefined && + ColliderParams?._props?.Radius < limit + ) { + locationList[index].base.SpawnPointParams[ + innerIndex + ].ColliderParams._props.Radius = limit; + } + } else { + if (!Categories.includes("Boss") && DelayToCanSpawnSec > 40) { + locationList[index].base.SpawnPointParams[ + innerIndex + ].DelayToCanSpawnSec = Math.round( + DelayToCanSpawnSec * Math.random() * Math.random() * 0.5 + ); + + // console.log( + // BotZoneName, + // DelayToCanSpawnSec, + // locationList[index].base.SpawnPointParams[innerIndex] + // .DelayToCanSpawnSec + // ); + } } } ); diff --git a/mods/MOAR - Ultra Lite Spawn Mod/user/mods/DewardianDev-MOAR/src/Spawning/utils.ts b/mods/MOAR - Ultra Lite Spawn Mod/user/mods/DewardianDev-MOAR/src/Spawning/utils.ts index 77f60d6..804bb17 100644 --- a/mods/MOAR - Ultra Lite Spawn Mod/user/mods/DewardianDev-MOAR/src/Spawning/utils.ts +++ b/mods/MOAR - Ultra Lite Spawn Mod/user/mods/DewardianDev-MOAR/src/Spawning/utils.ts @@ -44,7 +44,7 @@ export const waveBuilder = ( ); const min = !offset && waves.length < 1 ? 0 : timeStart; - const max = !offset && waves.length < 1 ? 0 : timeStart + 10; + const max = !offset && waves.length < 1 ? 0 : timeStart + 60; if (waves.length >= 1 || offset) timeStart = timeStart + stage; const BotPreset = getDifficulty(difficulty); @@ -55,8 +55,9 @@ export const waveBuilder = ( ); if (slotMax < 1) slotMax = 1; - const slotMin = (Math.round(Math.random() * slotMax) || 1) - 1; + let slotMin = (Math.round(Math.random() * slotMax) || 1) - 1; + if (wildSpawnType === "marksman" && slotMin < 1) slotMin = 1; waves.push({ BotPreset, BotSide: getBotSide(wildSpawnType), @@ -189,11 +190,26 @@ export const getRandomZombieType = () => zombieTypesCaps[Math.round((zombieTypesCaps.length - 1) * Math.random())]; export const buildPmcWaves = ( - totalWaves: number, + pmcTotal: number, escapeTimeLimit: number, config: typeof _config, - bossZones: string[] + bossZones: string[], + hotZones: string[] ): IBossLocationSpawn[] => { + // console.log(pmcTotal) + if (!pmcTotal) return []; + const halfIndex = Math.round(bossZones.length * 0.75); //Put hotzones in the 2 - 4 spawns + // console.log(bossZones.length); + bossZones = [ + ...bossZones.slice(0, halfIndex), + ...hotZones, + ...bossZones.slice(halfIndex), + ]; + + // console.log(bossZones.length, hotZones.length); + // console.log(bossZones); + pmcTotal = pmcTotal + hotZones.length; + let { pmcMaxGroupSize, pmcDifficulty, @@ -202,14 +218,12 @@ export const buildPmcWaves = ( pmcWaveDistribution, } = config; - const averageTime = escapeTimeLimit / totalWaves; - const firstHalf = Math.round(averageTime * (1 - pmcWaveDistribution)); - const secondHalf = Math.round(averageTime * (1 + pmcWaveDistribution)); - let timeStart = -1; - const waves: IBossLocationSpawn[] = []; - let maxSlotsReached = totalWaves; + const averageTime = (escapeTimeLimit * 0.8) / pmcTotal; - while (totalWaves > 0) { + const waves: IBossLocationSpawn[] = []; + let maxSlotsReached = pmcTotal; + + while (pmcTotal > 0) { let bossEscortAmount = Math.round( (morePmcGroups ? 1 : Math.random()) * Math.random() * @@ -217,20 +231,24 @@ export const buildPmcWaves = ( ); if (bossEscortAmount < 0) bossEscortAmount = 0; - const accelerate = totalWaves > 5 && waves.length < totalWaves / 3; - const stage = startingPmcs - ? 10 - : Math.round( - waves.length < Math.round(totalWaves * 0.5) - ? accelerate - ? firstHalf / 3 - : firstHalf - : secondHalf - ); - if (waves.length >= 1) timeStart = timeStart + stage; + // const totalCountThisWave = bossEscortAmount + 1; + const totalCountThusFar = pmcTotal - maxSlotsReached; + + const timeToUse = + totalCountThusFar < pmcTotal * pmcWaveDistribution + ? Math.round( + averageTime * (1 - pmcWaveDistribution) * totalCountThusFar + ) + : Math.round( + escapeTimeLimit * (1 - pmcWaveDistribution) + + (1 - pmcWaveDistribution) * totalCountThusFar * averageTime + ); + + let timeStart = + (startingPmcs ? totalCountThusFar * totalCountThusFar * 3 : timeToUse) || + -1; - // console.log(timeStart, BossEscortAmount); const side = Math.random() > 0.5 ? "pmcBEAR" : "pmcUSEC"; const BossDifficult = getDifficulty(pmcDifficulty); @@ -260,7 +278,10 @@ export const buildPmcWaves = ( maxSlotsReached -= 1 + bossEscortAmount; if (maxSlotsReached <= 0) break; } - + // console.log( + // escapeTimeLimit, + // waves.map(({ Time }) => Time) + // ); return waves; }; @@ -270,6 +291,7 @@ export const buildZombie = ( waveDistribution: number, BossChance: number = 100 ): IBossLocationSpawn[] => { + if (!totalWaves) return []; const averageTime = (escapeTimeLimit * 60) / totalWaves; const firstHalf = Math.round(averageTime * (1 - waveDistribution)); const secondHalf = Math.round(averageTime * (1 + waveDistribution)); diff --git a/mods/MOAR - Ultra Lite Spawn Mod_backup/BepInEx/plugins/MOAR.dll b/mods/MOAR - Ultra Lite Spawn Mod_backup/BepInEx/plugins/MOAR.dll new file mode 100644 index 0000000..1da0b44 Binary files /dev/null and b/mods/MOAR - Ultra Lite Spawn Mod_backup/BepInEx/plugins/MOAR.dll differ diff --git a/mods/MOAR - Ultra Lite Spawn Mod_backup/meta.ini b/mods/MOAR - Ultra Lite Spawn Mod_backup/meta.ini new file mode 100644 index 0000000..655bf11 --- /dev/null +++ b/mods/MOAR - Ultra Lite Spawn Mod_backup/meta.ini @@ -0,0 +1,28 @@ +[General] +gameName=spt +modid=0 +version=d2024.12.31.0 +newestVersion= +category="1," +nexusFileStatus=1 +installationFile=DewardianDev-MOAR-2.6.1.zip +repository=Nexus +ignoredVersion= +comments= +notes= +nexusDescription= +url= +hasCustomURL=false +lastNexusQuery= +lastNexusUpdate= +nexusLastModified=2024-12-16T06:46:30Z +nexusCategory=0 +converted=false +validated=false +color=@Variant(\0\0\0\x43\0\xff\xff\0\0\0\0\0\0\0\0) +tracked=0 + +[installedFiles] +1\modid=0 +1\fileid=0 +size=1 diff --git a/mods/MOAR - Ultra Lite Spawn Mod/user/mods/DewardianDev-MOAR/.github/workflows/action.yml b/mods/MOAR - Ultra Lite Spawn Mod_backup/user/mods/DewardianDev-MOAR/.github/workflows/action.yml similarity index 100% rename from mods/MOAR - Ultra Lite Spawn Mod/user/mods/DewardianDev-MOAR/.github/workflows/action.yml rename to mods/MOAR - Ultra Lite Spawn Mod_backup/user/mods/DewardianDev-MOAR/.github/workflows/action.yml diff --git a/mods/MOAR - Ultra Lite Spawn Mod/user/mods/DewardianDev-MOAR/.releaserc b/mods/MOAR - Ultra Lite Spawn Mod_backup/user/mods/DewardianDev-MOAR/.releaserc similarity index 100% rename from mods/MOAR - Ultra Lite Spawn Mod/user/mods/DewardianDev-MOAR/.releaserc rename to mods/MOAR - Ultra Lite Spawn Mod_backup/user/mods/DewardianDev-MOAR/.releaserc diff --git a/mods/MOAR - Ultra Lite Spawn Mod_backup/user/mods/DewardianDev-MOAR/LICENSE.md b/mods/MOAR - Ultra Lite Spawn Mod_backup/user/mods/DewardianDev-MOAR/LICENSE.md new file mode 100644 index 0000000..35fbeeb --- /dev/null +++ b/mods/MOAR - Ultra Lite Spawn Mod_backup/user/mods/DewardianDev-MOAR/LICENSE.md @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 Dushaoan + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/mods/MOAR - Ultra Lite Spawn Mod_backup/user/mods/DewardianDev-MOAR/config/PresetWeightings.json b/mods/MOAR - Ultra Lite Spawn Mod_backup/user/mods/DewardianDev-MOAR/config/PresetWeightings.json new file mode 100644 index 0000000..14298a3 --- /dev/null +++ b/mods/MOAR - Ultra Lite Spawn Mod_backup/user/mods/DewardianDev-MOAR/config/PresetWeightings.json @@ -0,0 +1,12 @@ +{ + "live-like": 25, + "more-scavs": 8, + "more-pmcs": 8, + "more-scavs-and-pmcs": 5, + "main-boss-roaming": 5, + "sniper-buddies": 4, + "boss-invasion": 2, + "rogue-invasion": 0, + "raider-invasion": 0, + "insanity": 0 +} diff --git a/mods/MOAR - Ultra Lite Spawn Mod_backup/user/mods/DewardianDev-MOAR/config/Presets.json b/mods/MOAR - Ultra Lite Spawn Mod_backup/user/mods/DewardianDev-MOAR/config/Presets.json new file mode 100644 index 0000000..7682a97 --- /dev/null +++ b/mods/MOAR - Ultra Lite Spawn Mod_backup/user/mods/DewardianDev-MOAR/config/Presets.json @@ -0,0 +1,65 @@ +{ + "live-like": {}, + "more-scavs": { + "moreScavGroups": true, + "scavMaxGroupSize": 5, + "scavWaveQuantity": 1.2 + }, + "more-pmcs": { + "morePmcGroups": true, + "pmcMaxGroupSize": 5, + "pmcWaveQuantity": 1.2 + }, + "more-scavs-and-pmcs": { + "maxBotCap": 30, + "moreScavGroups": true, + "scavMaxGroupSize": 5, + "morePmcGroups": true, + "pmcMaxGroupSize": 5, + "scavWaveQuantity": 1.2, + "pmcWaveQuantity": 1.2, + "mainBossChanceBuff": 25 + }, + "boss-invasion": { + "bossOpenZones": true, + "bossInvasion": true, + "bossInvasionSpawnChance": 10, + "mainBossChanceBuff": 25, + "gradualBossInvasion": true + }, + "rogue-invasion": { + "randomRaiderGroup": true, + "randomRaiderGroupChance": 50 + }, + "raider-invasion": { + "randomRaiderGroup": true, + "randomRaiderGroupChance": 50 + }, + "insanity": { + "scavWaveDistribution": 0.4, + "scavWaveQuantity": 1.3, + "pmcWaveQuantity": 1.3, + "maxBotCap": 30, + "maxBotPerZone": 9, + "moreScavGroups": true, + "morePmcGroups": true, + "pmcMaxGroupSize": 6, + "scavMaxGroupSize": 6, + "snipersHaveFriends": true, + "bossOpenZones": true, + "randomRaiderGroup": true, + "randomRaiderGroupChance": 50, + "randomRogueGroup": true, + "randomRogueGroupChance": 50, + "mainBossChanceBuff": 50, + "bossInvasion": true, + "bossInvasionSpawnChance": 10 + }, + "main-boss-roaming": { + "bossOpenZones": true, + "mainBossChanceBuff": 35 + }, + "sniper-buddies": { + "snipersHaveFriends": true + } +} diff --git a/mods/MOAR - Ultra Lite Spawn Mod_backup/user/mods/DewardianDev-MOAR/config/bossConfig.json b/mods/MOAR - Ultra Lite Spawn Mod_backup/user/mods/DewardianDev-MOAR/config/bossConfig.json new file mode 100644 index 0000000..c31d04c --- /dev/null +++ b/mods/MOAR - Ultra Lite Spawn Mod_backup/user/mods/DewardianDev-MOAR/config/bossConfig.json @@ -0,0 +1,63 @@ +{ + "ADD_THESE_TO_A_MAP_TO_OVERRIDE_OR_ADD_A_BOSS_TO_A_MAP": { + "BOSS_NAME_EXAMPLE": "CHANCE_OF_SPAWNING_PERCENT", + "sectantPriest": 0, + "arenaFighterEvent": 0, + "bossBoarSniper": 0, + "pmcBot": 0, + "bossZryachiy": 0, + "exUsec": 0, + "crazyAssaultEvent": 0, + "peacemaker": 0, + "bossKojaniy": 0, + "bossGluhar": 0, + "bossSanitar": 0, + "bossKilla": 0, + "bossTagilla": 0, + "bossKnight": 0, + "bossBoar": 0, + "bossKolontay": 0, + "bossPartisan": 0, + "bossBully": 0 + }, + "customs": { + "bossKnight": 30, + "bossPartisan": 30, + "bossBully": 30 + }, + "factoryDay": { + "bossTagilla": 30 + }, + "factoryNight": { + "bossTagilla": 30 + }, + "interchange": { + "bossKilla": 30 + }, + "laboratory": {}, + "lighthouse": { + "bossKnight": 30, + "bossPartisan": 30 + }, + "rezervbase": { + "bossGluhar": 30 + }, + "shoreline": { + "bossKnight": 30, + "bossPartisan": 30, + "bossSanitar": 30 + }, + "tarkovstreets": { + "bossBoar": 30, + "bossKolontay": 30 + }, + "woods": { + "bossKojaniy": 30, + "bossKnight": 30, + "bossPartisan": 30 + }, + "gzLow": {}, + "gzHigh": { + "bossKolontay": 30 + } +} \ No newline at end of file diff --git a/mods/MOAR - Ultra Lite Spawn Mod_backup/user/mods/DewardianDev-MOAR/config/config.json b/mods/MOAR - Ultra Lite Spawn Mod_backup/user/mods/DewardianDev-MOAR/config/config.json new file mode 100644 index 0000000..6a20bfc --- /dev/null +++ b/mods/MOAR - Ultra Lite Spawn Mod_backup/user/mods/DewardianDev-MOAR/config/config.json @@ -0,0 +1,46 @@ +{ + "enableBotSpawning": true, + + "pmcDifficulty": 0.6, + "scavDifficulty": 0.3, + + "scavWaveDistribution": 0.3, + "scavWaveQuantity": 0.5, + + "startingPmcs": false, + + "pmcWaveDistribution": 0.8, + "pmcWaveQuantity": 1.6, + + "zombiesEnabled": false, + "zombieWaveDistribution": 0.5, + "zombieWaveQuantity": 1, + "zombieHealth": 1, + + "maxBotCap": 25, + "maxBotPerZone": 7, + + "moreScavGroups": false, + "morePmcGroups": false, + "pmcMaxGroupSize": 4, + "scavMaxGroupSize": 4, + + "snipersHaveFriends": false, + + "bossOpenZones": false, + + "randomRaiderGroup": false, + "randomRaiderGroupChance": 10, + + "randomRogueGroup": false, + "randomRogueGroupChance": 10, + + "disableBosses": false, + "mainBossChanceBuff": 0, + + "bossInvasion": false, + "bossInvasionSpawnChance": 5, + "gradualBossInvasion": true, + + "debug": false +} diff --git a/mods/MOAR - Ultra Lite Spawn Mod_backup/user/mods/DewardianDev-MOAR/config/mapConfig.json b/mods/MOAR - Ultra Lite Spawn Mod_backup/user/mods/DewardianDev-MOAR/config/mapConfig.json new file mode 100644 index 0000000..8c3da1e --- /dev/null +++ b/mods/MOAR - Ultra Lite Spawn Mod_backup/user/mods/DewardianDev-MOAR/config/mapConfig.json @@ -0,0 +1,149 @@ +{ + "customs": { + "spawnMinDistance": 30, + "pmcWaveCount": 12, + "scavWaveCount": 21, + "zombieWaveCount": 9, + "scavHotZones": [ + "ZoneDormitory", + "ZoneCrossRoad", + "ZoneGasStation" + ], + "pmcHotZones": [ + "ZoneDormitory", + "ZoneGasStation", + "ZoneCustoms" + ] + }, + "factoryDay": { + "spawnMinDistance": 20, + "maxBotCapOverride": 12, + "maxBotPerZoneOverride": 10, + "pmcWaveCount": 8, + "scavWaveCount": 9, + "zombieWaveCount": 6 + }, + "factoryNight": { + "spawnMinDistance": 20, + "maxBotCapOverride": 12, + "maxBotPerZoneOverride": 10, + "pmcWaveCount": 8, + "scavWaveCount": 9, + "zombieWaveCount": 6 + }, + "interchange": { + "spawnMinDistance": 40, + "pmcWaveCount": 14, + "scavWaveCount": 32, + "zombieWaveCount": 12, + "scavHotZones": [ + "ZoneCenterBot", + "ZoneCenter" + ], + "pmcHotZones": [ + "ZoneIDEA", + "ZoneOLI", + "ZoneCenter" + ] + }, + "laboratory": { + "spawnMinDistance": 20, + "pmcWaveCount": 10, + "scavWaveCount": 0, + "zombieWaveCount": 12 + }, + "lighthouse": { + "spawnMinDistance": 40, + "pmcWaveCount": 12, + "scavWaveCount": 20, + "zombieWaveCount": 10, + "scavHotZones": [ + "Zone_LongRoad", + "Zone_Village" + ], + "pmcHotZones": [ + "Zone_DestroyedHouse", + "Zone_Chalet", + "Zone_Village" + ] + }, + "rezervbase": { + "spawnMinDistance": 40, + "pmcWaveCount": 11, + "scavWaveCount": 24, + "zombieWaveCount": 9, + "scavHotZones": [ + "ZoneRailStrorage", + "ZoneBunkerStorage", + "ZoneBarrack" + ], + "pmcHotZones": [ + "ZoneBarrack", + "ZoneBunkerStorage" + ] + }, + "shoreline": { + "spawnMinDistance": 40, + "pmcWaveCount": 14, + "scavWaveCount": 32, + "zombieWaveCount": 12, + "scavHotZones": [ + "ZoneSanatorium1", + "ZoneGasStation", + "ZonePowerStation", + "ZoneBusStation", + "ZoneStartVillage" + ], + "pmcHotZones": [ + "ZoneSanatorium2", + "ZoneGasStation", + "ZonePowerStation" + ] + }, + "tarkovstreets": { + "spawnMinDistance": 40, + "pmcWaveCount": 16, + "scavWaveCount": 28, + "zombieWaveCount": 13, + "scavHotZones": [ + "ZoneHotel_2", + "ZoneHotel_1", + "ZoneConstruction", + "ZoneCarShowroom" + ], + "pmcHotZones": [ + "ZoneSanatorium2", + "ZoneCinema", + "ZoneConcordiaParking" + ] + }, + "woods": { + "spawnMinDistance": 40, + "pmcWaveCount": 14, + "scavWaveCount": 28, + "zombieWaveCount": 10, + "scavHotZones": [ + "ZoneWoodCutter", + "ZoneClearVill", + "ZoneScavBase2", + "ZoneRedHouse" + ], + "pmcHotZones": [ + "ZoneWoodCutter", + "ZoneBigRocks", + "ZoneHighRocks" + ] + }, + "gzLow": { + "spawnMinDistance": 30, + "pmcWaveCount": 10, + "scavWaveCount": 18, + "zombieWaveCount": 9 + }, + "gzHigh": { + "spawnMinDistance": 30, + "pmcWaveCount": 12, + "scavWaveCount": 18, + "zombieWaveCount": 9 + } +} \ No newline at end of file diff --git a/mods/MOAR - Ultra Lite Spawn Mod_backup/user/mods/DewardianDev-MOAR/package.json b/mods/MOAR - Ultra Lite Spawn Mod_backup/user/mods/DewardianDev-MOAR/package.json new file mode 100644 index 0000000..a8bbb14 --- /dev/null +++ b/mods/MOAR - Ultra Lite Spawn Mod_backup/user/mods/DewardianDev-MOAR/package.json @@ -0,0 +1,25 @@ +{ + "name": "MOAR", + "version": "2.6.1", + "main": "src/mod.js", + "license": "MIT", + "author": "DewardianDev", + "sptVersion": "^3.10.x", + "scripts": { + "setup": "npm i", + "build": "node ./packageBuild.ts" + }, + "devDependencies": { + "@semantic-release/git": "^10.0.1", + "@types/node": "16.18.10", + "@typescript-eslint/eslint-plugin": "5.46.1", + "@typescript-eslint/parser": "5.46.1", + "bestzip": "2.2.1", + "eslint": "8.30.0", + "fs-extra": "11.1.0", + "glob": "8.0.3", + "semantic-release": "^24.2.0", + "tsyringe": "4.7.0", + "typescript": "4.9.4" + } +} diff --git a/mods/MOAR - Ultra Lite Spawn Mod_backup/user/mods/DewardianDev-MOAR/src/GlobalValues.ts b/mods/MOAR - Ultra Lite Spawn Mod_backup/user/mods/DewardianDev-MOAR/src/GlobalValues.ts new file mode 100644 index 0000000..2aedb9d --- /dev/null +++ b/mods/MOAR - Ultra Lite Spawn Mod_backup/user/mods/DewardianDev-MOAR/src/GlobalValues.ts @@ -0,0 +1,11 @@ +import config from "../config/config.json"; +import { ILocationBase } from "@spt/models/eft/common/ILocationBase"; + +export class globalValues { + public static baseConfig: typeof config = undefined; + public static overrideConfig: Partial = undefined; + public static locationsBase: ILocationBase[] = undefined; + public static currentPreset: string = ""; + public static forcedPreset: string = "custom"; + public static addedMapZones: Record = {}; +} diff --git a/mods/MOAR - Ultra Lite Spawn Mod_backup/user/mods/DewardianDev-MOAR/src/Routes/routes.ts b/mods/MOAR - Ultra Lite Spawn Mod_backup/user/mods/DewardianDev-MOAR/src/Routes/routes.ts new file mode 100644 index 0000000..bbbe886 --- /dev/null +++ b/mods/MOAR - Ultra Lite Spawn Mod_backup/user/mods/DewardianDev-MOAR/src/Routes/routes.ts @@ -0,0 +1,168 @@ +import { DependencyContainer } from "tsyringe"; +import { buildWaves } from "../Spawning/Spawning"; +import { StaticRouterModService } from "@spt/services/mod/staticRouter/StaticRouterModService"; +import { DynamicRouterModService } from "@spt/services/mod/dynamicRouter/DynamicRouterModService"; +import { globalValues } from "../GlobalValues"; +import { kebabToTitle } from "../utils"; +import PresetWeightingsConfig from "../../config/PresetWeightings.json"; + +export const setupRoutes = (container: DependencyContainer) => { + const staticRouterModService = container.resolve( + "StaticRouterModService" + ); + + const dynamicRouterModService = container.resolve( + "DynamicRouterModService" + ); + + // Make buildwaves run on game end + staticRouterModService.registerStaticRouter( + `moarUpdater`, + [ + { + url: "/client/match/local/end", + action: async (_url, info, sessionId, output) => { + buildWaves(container); + return output; + }, + }, + ], + "moarUpdater" + ); + + staticRouterModService.registerStaticRouter( + `moarGetCurrentPreset`, + [ + { + url: "/moar/currentPreset", + action: async () => { + return globalValues.forcedPreset || "random"; + }, + }, + ], + "moarGetCurrentPreset" + ); + + staticRouterModService.registerStaticRouter( + `moarGetAnnouncePreset`, + [ + { + url: "/moar/announcePreset", + action: async () => { + if (globalValues.forcedPreset?.toLowerCase() === "random") { + return globalValues.currentPreset; + } + return globalValues.forcedPreset || globalValues.currentPreset; + }, + }, + ], + "moarGetAnnouncePreset" + ); + + staticRouterModService.registerStaticRouter( + `getDefaultConfig`, + [ + { + url: "/moar/getDefaultConfig", + action: async () => { + return JSON.stringify(globalValues.baseConfig); + }, + }, + ], + "getDefaultConfig" + ); + + staticRouterModService.registerStaticRouter( + `getServerConfigWithOverrides`, + [ + { + url: "/moar/getServerConfigWithOverrides", + action: async () => { + return JSON.stringify({ + ...(globalValues.baseConfig || {}), + ...(globalValues.overrideConfig || {}), + }); + }, + }, + ], + "getServerConfigWithOverrides" + ); + + staticRouterModService.registerStaticRouter( + `getServerConfigWithOverrides`, + [ + { + url: "/moar/getServerConfigWithOverrides", + action: async () => { + return JSON.stringify({ + ...globalValues.baseConfig, + ...globalValues.overrideConfig, + }); + }, + }, + ], + "getServerConfigWithOverrides" + ); + + staticRouterModService.registerStaticRouter( + `moarGetPresetsList`, + [ + { + url: "/moar/getPresets", + action: async () => { + let result = [ + ...Object.keys(PresetWeightingsConfig).map((preset) => ({ + Name: kebabToTitle(preset), + Label: preset, + })), + { Name: "Random", Label: "random" }, + { Name: "Custom", Label: "custom" }, + ]; + + return JSON.stringify({ data: result }); + }, + }, + ], + "moarGetPresetsList" + ); + + staticRouterModService.registerStaticRouter( + "setOverrideConfig", + [ + { + url: "/moar/setOverrideConfig", + action: async ( + url: string, + overrideConfig: typeof globalValues.overrideConfig = {}, + sessionID, + output + ) => { + globalValues.overrideConfig = overrideConfig; + + buildWaves(container); + + return "Success"; + }, + }, + ], + "setOverrideConfig" + ); + + staticRouterModService.registerStaticRouter( + "moarSetPreset", + [ + { + url: "/moar/setPreset", + action: async (url: string, { Preset }, sessionID, output) => { + globalValues.forcedPreset = Preset; + buildWaves(container); + + return `Current Preset: ${kebabToTitle( + globalValues.forcedPreset || "Random" + )}`; + }, + }, + ], + "moarSetPreset" + ); +}; diff --git a/mods/MOAR - Ultra Lite Spawn Mod_backup/user/mods/DewardianDev-MOAR/src/Spawning/Spawning.ts b/mods/MOAR - Ultra Lite Spawn Mod_backup/user/mods/DewardianDev-MOAR/src/Spawning/Spawning.ts new file mode 100644 index 0000000..1aaad28 --- /dev/null +++ b/mods/MOAR - Ultra Lite Spawn Mod_backup/user/mods/DewardianDev-MOAR/src/Spawning/Spawning.ts @@ -0,0 +1,154 @@ +import { IBotConfig } from "@spt/models/spt/config/IBotConfig.d"; +import { IPmcConfig } from "@spt/models/spt/config/IPmcConfig.d"; +import { DatabaseServer } from "@spt/servers/DatabaseServer"; +import _config from "../../config/config.json"; +import _mapConfig from "../../config/mapConfig.json"; +import { ConfigServer } from "@spt/servers/ConfigServer"; +import { ConfigTypes } from "@spt/models/enums/ConfigTypes"; +import { DependencyContainer } from "tsyringe"; +import { globalValues } from "../GlobalValues"; +import { cloneDeep, getRandomPresetOrCurrentlySelectedPreset } from "../utils"; +import { ILocationConfig } from "@spt/models/spt/config/ILocationConfig.d"; +import { originalMapList } from "./constants"; +import { buildBossWaves } from "./buildBossWaves"; +import buildZombieWaves from "./buildZombieWaves"; +import buildScavMarksmanWaves from "./buildScavMarksmanWaves"; +import buildPmcs from "./buildPmcs"; +import { setEscapeTimeOverrides } from "./utils"; +import { ILogger } from "@spt/models/spt/utils/ILogger"; +import updateSpawnLocations from "./updateSpawnLocations"; + +export const buildWaves = (container: DependencyContainer) => { + const configServer = container.resolve("ConfigServer"); + const Logger = container.resolve("WinstonLogger"); + const pmcConfig = configServer.getConfig(ConfigTypes.PMC); + const botConfig = configServer.getConfig(ConfigTypes.BOT); + + const locationConfig = configServer.getConfig( + ConfigTypes.LOCATION + ); + + locationConfig.rogueLighthouseSpawnTimeSettings.waitTimeSeconds = 60; + locationConfig.enableBotTypeLimits = false; + locationConfig.fitLootIntoContainerAttempts = 1; // Move to ALP + locationConfig.addCustomBotWavesToMaps = false; + locationConfig.customWaves = { boss: {}, normal: {} }; + + const databaseServer = container.resolve("DatabaseServer"); + + const { locations, bots, globals } = databaseServer.getTables(); + + let config = cloneDeep(globalValues.baseConfig) as typeof _config; + + const preset = getRandomPresetOrCurrentlySelectedPreset(); + + Object.keys(globalValues.overrideConfig).forEach((key) => { + if (config[key] !== globalValues.overrideConfig[key]) { + config.debug && + console.log( + `[MOAR] overrideConfig ${key} changed from ${config[key]} to ${globalValues.overrideConfig[key]}` + ); + config[key] = globalValues.overrideConfig[key]; + } + }); + + // Set from preset if preset above is not empty + Object.keys(preset).forEach((key) => { + if (config[key] !== preset[key]) { + config.debug && + console.log( + `[MOAR] preset ${globalValues.currentPreset}: ${key} changed from ${config[key]} to ${preset[key]}` + ); + config[key] = preset[key]; + } + }); + + config.debug && + console.log( + globalValues.forcedPreset === "custom" + ? "custom" + : globalValues.currentPreset + ); + + const { + bigmap: customs, + factory4_day: factoryDay, + factory4_night: factoryNight, + interchange, + laboratory, + lighthouse, + rezervbase, + shoreline, + tarkovstreets, + woods, + sandbox: gzLow, + sandbox_high: gzHigh, + } = locations; + + let locationList = [ + customs, + factoryDay, + factoryNight, + interchange, + laboratory, + lighthouse, + rezervbase, + shoreline, + tarkovstreets, + woods, + gzLow, + gzHigh, + ]; + + // This resets all locations to original state + if (!globalValues.locationsBase) { + globalValues.locationsBase = locationList.map(({ base }) => + cloneDeep(base) + ); + } else { + locationList = locationList.map((item, key) => ({ + ...item, + base: cloneDeep(globalValues.locationsBase[key]), + })); + } + + pmcConfig.convertIntoPmcChance = { + default: { + assault: { min: 0, max: 0 }, + cursedassault: { min: 0, max: 0 }, + pmcbot: { min: 0, max: 0 }, + exusec: { min: 0, max: 0 }, + arenafighter: { min: 0, max: 0 }, + arenafighterevent: { min: 0, max: 0 }, + crazyassaultevent: { min: 0, max: 0 }, + }, + factory4_day: { assault: { min: 0, max: 0 } }, + laboratory: { pmcbot: { min: 0, max: 0 } }, + rezervbase: { pmcbot: { min: 0, max: 0 } }, + }; + + updateSpawnLocations(locationList); + + setEscapeTimeOverrides(locationList, _mapConfig, Logger, config); + + // Make main waves + buildScavMarksmanWaves(config, locationList, botConfig); + + // BOSS RELATED STUFF! + buildBossWaves(config, locationList); + + //Zombies + if (config.zombiesEnabled) { + buildZombieWaves(config, locationList, bots); + } + + buildPmcs(config, locationList); + + originalMapList.forEach((name, index) => { + if (!locations[name]) { + console.log("[MOAR] OH CRAP we have a problem!", name); + } else { + locations[name] = locationList[index]; + } + }); +}; diff --git a/mods/MOAR - Ultra Lite Spawn Mod_backup/user/mods/DewardianDev-MOAR/src/Spawning/buildBossWaves.ts b/mods/MOAR - Ultra Lite Spawn Mod_backup/user/mods/DewardianDev-MOAR/src/Spawning/buildBossWaves.ts new file mode 100644 index 0000000..02853e7 --- /dev/null +++ b/mods/MOAR - Ultra Lite Spawn Mod_backup/user/mods/DewardianDev-MOAR/src/Spawning/buildBossWaves.ts @@ -0,0 +1,278 @@ +import { ILocation } from "@spt/models/eft/common/ILocation"; +import _config from "../../config/config.json"; +import bossConfig from "../../config/bossConfig.json"; +import mapConfig from "../../config/mapConfig.json"; +import { + bossesToRemoveFromPool, + configLocations, + mainBossNameList, + originalMapList, +} from "./constants"; +import { buildBossBasedWave, shuffle } from "./utils"; +import { IBossLocationSpawn } from "@spt/models/eft/common/ILocationBase"; +import { cloneDeep } from "../utils"; + +export function buildBossWaves( + config: typeof _config, + locationList: ILocation[] +) { + let { + randomRaiderGroup, + randomRaiderGroupChance, + randomRogueGroup, + randomRogueGroupChance, + mainBossChanceBuff, + bossInvasion, + bossInvasionSpawnChance, + disableBosses, + bossOpenZones, + gradualBossInvasion, + } = config; + + const bossList = mainBossNameList.filter( + (bossName) => !["bossKnight"].includes(bossName) + ); + + const allBosses: Record = {}; + for (const key in locationList) { + locationList[key].base.BossLocationSpawn.forEach((boss) => { + if (!allBosses[boss.BossName]) { + allBosses[boss.BossName] = boss; + } + }); + } + + // CreateBossList + const bosses: Record = {}; + for (let indx = 0; indx < locationList.length; indx++) { + // Disable Bosses + if (disableBosses && !!locationList[indx].base?.BossLocationSpawn) { + locationList[indx].base.BossLocationSpawn = []; + } else { + //Remove all other spawns from pool now that we have the spawns zone list + locationList[indx].base.BossLocationSpawn = locationList[ + indx + ].base.BossLocationSpawn.filter( + (boss) => !bossesToRemoveFromPool.has(boss.BossName) + ); + + const location = locationList[indx]; + + const defaultBossSettings = + mapConfig?.[configLocations[indx]]?.defaultBossSettings; + + // Sets bosses spawn chance from settings + if ( + location?.base?.BossLocationSpawn && + defaultBossSettings && + Object.keys(defaultBossSettings)?.length + ) { + const filteredBossList = Object.keys(defaultBossSettings).filter( + (name) => defaultBossSettings[name]?.BossChance !== undefined + ); + if (filteredBossList?.length) { + filteredBossList.forEach((bossName) => { + location.base.BossLocationSpawn = + location.base.BossLocationSpawn.map((boss) => ({ + ...boss, + ...(boss.BossName === bossName + ? { BossChance: defaultBossSettings[bossName].BossChance } + : {}), + })); + }); + } + } + + if (randomRaiderGroup) { + const raiderWave = buildBossBasedWave( + randomRaiderGroupChance, + "1,2,2,2,3", + "pmcBot", + "pmcBot", + "", + locationList[indx].base.EscapeTimeLimit + ); + location.base.BossLocationSpawn.push(raiderWave); + } + + if (randomRogueGroup) { + const rogueWave = buildBossBasedWave( + randomRogueGroupChance, + "1,2,2,2,3", + "exUsec", + "exUsec", + "", + locationList[indx].base.EscapeTimeLimit + ); + location.base.BossLocationSpawn.push(rogueWave); + } + + //Add each boss from each map to bosses object + const filteredBosses = location.base.BossLocationSpawn?.filter( + ({ BossName }) => mainBossNameList.includes(BossName) + ); + + if (filteredBosses.length) { + for (let index = 0; index < filteredBosses.length; index++) { + const boss = filteredBosses[index]; + if ( + !bosses[boss.BossName] || + (bosses[boss.BossName] && + bosses[boss.BossName].BossChance < boss.BossChance) + ) { + bosses[boss.BossName] = { ...boss }; + } + } + } + } + } + + if (!disableBosses) { + // Make boss Invasion + if (bossInvasion) { + if (bossInvasionSpawnChance) { + bossList.forEach((bossName) => { + if (bosses[bossName]) + bosses[bossName].BossChance = bossInvasionSpawnChance; + }); + } + + for (let key = 0; key < locationList.length; key++) { + //Gather bosses to avoid duplicating. + let bossLocations = ""; + + const duplicateBosses = [ + ...locationList[key].base.BossLocationSpawn.filter( + ({ BossName, BossZone }) => { + bossLocations += BossZone + ","; + return bossList.includes(BossName); + } + ).map(({ BossName }) => BossName), + "bossKnight", // So knight doesn't invade + ]; + + const uniqueBossZones = bossOpenZones + ? "" + : [ + ...new Set( + bossLocations + .split(",") + .filter( + (zone) => !!zone && !zone.toLowerCase().includes("snipe") + ) + ), + ].join(","); + + //Build bosses to add + const bossesToAdd = shuffle(Object.values(bosses)) + .filter(({ BossName }) => !duplicateBosses.includes(BossName)) + .map((boss, j) => ({ + ...boss, + BossZone: uniqueBossZones, + BossEscortAmount: + boss.BossEscortAmount === "0" ? boss.BossEscortAmount : "1", + ...(gradualBossInvasion ? { Time: j * 20 + 1 } : {}), + })); + + // UpdateBosses + locationList[key].base.BossLocationSpawn = [ + ...locationList[key].base.BossLocationSpawn, + ...bossesToAdd, + ]; + } + } + let hasChangedBossSpawns = false; + // console.log(Object.keys(allBosses)); + configLocations.forEach((mapName, index) => { + const bossLocationSpawn = locationList[index].base.BossLocationSpawn; + const mapBossConfig: Record = cloneDeep( + bossConfig[mapName] || {} + ); + // if (Object.keys(mapBossConfig).length === 0) console.log(name, "empty"); + const adjusted = new Set([]); + + bossLocationSpawn.forEach(({ BossName, BossChance }, bossIndex) => { + if (typeof mapBossConfig[BossName] === "number") { + if (BossChance !== mapBossConfig[BossName]) { + if (!hasChangedBossSpawns) { + console.log( + `\n[MOAR]: --- Adjusting default boss spawn rates --- ` + ); + hasChangedBossSpawns = true; + } + console.log( + `[MOAR]: ${mapName} ${BossName}: ${locationList[index].base.BossLocationSpawn[bossIndex].BossChance} => ${mapBossConfig[BossName]}` + ); + locationList[index].base.BossLocationSpawn[bossIndex].BossChance = + mapBossConfig[BossName]; + } + adjusted.add(BossName); + } + }); + + const bossesToAdd = Object.keys(mapBossConfig) + .filter( + (adjustName) => !adjusted.has(adjustName) && !!allBosses[adjustName] + ) + .map((bossName) => { + `[MOAR]: Adding non-default boss ${bossName} to ${originalMapList[index]}`; + + const newBoss: IBossLocationSpawn = cloneDeep( + allBosses[bossName] || {} + ); + newBoss.BossChance = mapBossConfig[bossName]; + // console.log( + // "Adding boss", + // bossName, + // "to ", + // originalMapList[index], + // "spawn chance =>", + // mapBossConfig[bossName] + // ); + return newBoss; + }); + + // console.log(bossesToAdd); + + if (bossOpenZones || mainBossChanceBuff) { + locationList[index].base?.BossLocationSpawn?.forEach((boss, key) => { + if (bossList.includes(boss.BossName)) { + if (bossOpenZones) { + locationList[index].base.BossLocationSpawn[key] = { + ...locationList[index].base.BossLocationSpawn[key], + BossZone: "", + }; + } + + if (!!boss.BossChance && mainBossChanceBuff > 0) { + locationList[index].base.BossLocationSpawn[key] = { + ...locationList[index].base.BossLocationSpawn[key], + BossChance: + boss.BossChance + mainBossChanceBuff > 100 + ? 100 + : Math.round(boss.BossChance + mainBossChanceBuff), + }; + } + } + }); + } + + locationList[index].base.BossLocationSpawn = [ + ...locationList[index].base.BossLocationSpawn, + ...bossesToAdd, + ]; + + bossesToAdd.length && + console.log( + `[MOAR] Adding the following bosses to map ${ + configLocations[index] + }: ${bossesToAdd.map(({ BossName }) => BossName)}` + ); + }); + if (hasChangedBossSpawns) { + console.log( + `[MOAR]: --- Adjusting default boss spawn rates complete --- \n` + ); + } + } +} diff --git a/mods/MOAR - Ultra Lite Spawn Mod_backup/user/mods/DewardianDev-MOAR/src/Spawning/buildPmcs.ts b/mods/MOAR - Ultra Lite Spawn Mod_backup/user/mods/DewardianDev-MOAR/src/Spawning/buildPmcs.ts new file mode 100644 index 0000000..8b19992 --- /dev/null +++ b/mods/MOAR - Ultra Lite Spawn Mod_backup/user/mods/DewardianDev-MOAR/src/Spawning/buildPmcs.ts @@ -0,0 +1,87 @@ +import { ILocation } from "@spt/models/eft/common/ILocation"; +import _config from "../../config/config.json"; +import mapConfig from "../../config/mapConfig.json"; +import { + bossesToRemoveFromPool, + defaultEscapeTimes, + defaultHostility, +} from "./constants"; +import { buildPmcWaves, MapSettings, shuffle } from "./utils"; +import { saveToFile } from "../utils"; + +export default function buildPmcs( + config: typeof _config, + locationList: ILocation[] +) { + for (let index = 0; index < locationList.length; index++) { + const mapSettingsList = Object.keys(mapConfig) as Array< + keyof typeof mapConfig + >; + const map = mapSettingsList[index]; + + locationList[index].base.BotLocationModifier.AdditionalHostilitySettings = + defaultHostility; + + const { pmcHotZones = [] } = (mapConfig?.[map] as MapSettings) || {}; + + let pmcZones = shuffle([ + ...new Set( + [...locationList[index].base.SpawnPointParams] + .filter( + ({ Categories, BotZoneName }) => + !!BotZoneName && + (Categories.includes("Player") || + (map === "laboratory" && + !BotZoneName.includes("BotZoneGate"))) && + !BotZoneName.includes("snipe") + ) + .map(({ BotZoneName, ...rest }) => { + return BotZoneName; + }) + ), + ...pmcHotZones, + ]); + // Make labs have only named zones + if (map === "laboratory") { + pmcZones = new Array(10).fill(pmcZones).flat(1); + // console.log(pmcZones); + } + + const timeLimit = locationList[index].base.EscapeTimeLimit * 60; + + const { pmcWaveCount } = mapConfig[map]; + + const escapeTimeLimitRatio = Math.round( + locationList[index].base.EscapeTimeLimit / defaultEscapeTimes[map] + ); + + const totalWaves = Math.round( + pmcWaveCount * config.pmcWaveQuantity * escapeTimeLimitRatio + ); + // console.log(pmcZones.length, totalWaves); + const numberOfZoneless = totalWaves - pmcZones.length; + if (numberOfZoneless > 0) { + const addEmpty = new Array(numberOfZoneless).fill(""); + pmcZones = shuffle([...pmcZones, ...addEmpty]); + } + // if (map === "laboratory") console.log(numberOfZoneless, pmcZones); + + if (config.debug) { + console.log(`${map} PMC count ${totalWaves} \n`); + + escapeTimeLimitRatio !== 1 && + console.log( + `${map} PMC wave count changed from ${pmcWaveCount} to ${totalWaves} due to escapeTimeLimit adjustment` + ); + } + + const waves = buildPmcWaves(pmcWaveCount, timeLimit, config, pmcZones); + // if (map === "laboratory") + // console.log(waves.map(({ BossZone }) => BossZone)); + // apply our new waves + locationList[index].base.BossLocationSpawn = [ + ...waves, + ...locationList[index].base.BossLocationSpawn, + ]; + } +} diff --git a/mods/MOAR - Ultra Lite Spawn Mod_backup/user/mods/DewardianDev-MOAR/src/Spawning/buildScavMarksmanWaves.ts b/mods/MOAR - Ultra Lite Spawn Mod_backup/user/mods/DewardianDev-MOAR/src/Spawning/buildScavMarksmanWaves.ts new file mode 100644 index 0000000..93b9baf --- /dev/null +++ b/mods/MOAR - Ultra Lite Spawn Mod_backup/user/mods/DewardianDev-MOAR/src/Spawning/buildScavMarksmanWaves.ts @@ -0,0 +1,226 @@ +import { ILocation } from "@spt/models/eft/common/ILocation"; +import _config from "../../config/config.json"; +import mapConfig from "../../config/mapConfig.json"; +import { + configLocations, + defaultEscapeTimes, + defaultHostility, + originalMapList, +} from "./constants"; +import { MapSettings, shuffle, waveBuilder } from "./utils"; +import { IWave, WildSpawnType } from "@spt/models/eft/common/ILocationBase"; +import { IBotConfig } from "@spt/models/spt/config/IBotConfig"; +import { saveToFile } from "../utils"; + +export default function buildScavMarksmanWaves( + config: typeof _config, + locationList: ILocation[], + botConfig: IBotConfig +) { + let { + debug, + maxBotCap, + scavWaveQuantity, + scavWaveDistribution, + snipersHaveFriends, + maxBotPerZone, + scavMaxGroupSize, + scavDifficulty, + moreScavGroups, + } = config; + + for (let index = 0; index < locationList.length; index++) { + const mapSettingsList = Object.keys(mapConfig) as Array< + keyof typeof mapConfig + >; + const map = mapSettingsList[index]; + + locationList[index].base = { + ...locationList[index].base, + ...{ + NewSpawn: false, + OcculsionCullingEnabled: true, + OfflineNewSpawn: false, + OfflineOldSpawn: true, + OldSpawn: true, + BotSpawnCountStep: 0, + }, + }; + + locationList[index].base.NonWaveGroupScenario.Enabled = false; + locationList[index].base["BotStartPlayer"] = 0; + if ( + locationList[index].base.BotStop < + locationList[index].base.EscapeTimeLimit * 60 + ) { + locationList[index].base.BotStop = + locationList[index].base.EscapeTimeLimit * 60; + } + + const { + maxBotPerZoneOverride, + maxBotCapOverride, + EscapeTimeLimit, + scavHotZones, + } = (mapConfig?.[map] as MapSettings) || {}; + + // Set per map EscapeTimeLimit + if (EscapeTimeLimit) { + locationList[index].base.EscapeTimeLimit = EscapeTimeLimit; + locationList[index].base.exit_access_time = EscapeTimeLimit + 1; + } + + // Set default or per map maxBotCap + if (maxBotCapOverride || maxBotCap) { + const capToSet = maxBotCapOverride || maxBotCap; + // console.log(map, capToSet, maxBotCapOverride, maxBotCap); + locationList[index].base.BotMax = capToSet; + locationList[index].base.BotMaxPvE = capToSet; + botConfig.maxBotCap[originalMapList[index]] = capToSet; + } + + // Adjust botZone quantity + if (maxBotPerZoneOverride || maxBotPerZone) { + const BotPerZone = maxBotPerZoneOverride || maxBotPerZone; + // console.log(map, BotPerZone, maxBotPerZoneOverride, maxBotPerZone); + locationList[index].base.MaxBotPerZone = BotPerZone; + } + + const sniperLocations = new Set( + [...locationList[index].base.SpawnPointParams] + .filter( + ({ Categories, Sides, BotZoneName }) => + !!BotZoneName && + Sides.includes("Savage") && + !Categories.includes("Boss") + ) + .filter( + ({ BotZoneName, DelayToCanSpawnSec }) => + BotZoneName?.toLowerCase().includes("snipe") || + DelayToCanSpawnSec > 300 + ) + .map(({ BotZoneName }) => BotZoneName) + ); + + if (sniperLocations.size) { + locationList[index].base.MinMaxBots = [ + { + WildSpawnType: "marksman", + max: sniperLocations.size * 5, + min: sniperLocations.size, + }, + ]; + } + + const scavZones = shuffle([ + ...new Set( + [...locationList[index].base.SpawnPointParams] + .filter( + ({ Categories, Sides, BotZoneName }) => + !!BotZoneName && + Sides.includes("Savage") && + !Categories.includes("Boss") + ) + .map(({ BotZoneName }) => BotZoneName) + .filter((name) => !sniperLocations.has(name)) + ), + ]); + + // Reduced Zone Delay + locationList[index].base.SpawnPointParams = locationList[ + index + ].base.SpawnPointParams.map((spawn) => ({ + ...spawn, + DelayToCanSpawnSec: + spawn.DelayToCanSpawnSec > 20 + ? Math.round(spawn.DelayToCanSpawnSec / 10) + : spawn.DelayToCanSpawnSec, + })); + + const timeLimit = locationList[index].base.EscapeTimeLimit * 60; + const { scavWaveCount } = mapConfig[map]; + + const escapeTimeLimitRatio = Math.round( + locationList[index].base.EscapeTimeLimit / defaultEscapeTimes[map] + ); + + // Scavs + const scavTotalWaveCount = Math.round( + scavWaveCount * scavWaveQuantity * escapeTimeLimitRatio + ); + + config.debug && + escapeTimeLimitRatio !== 1 && + console.log( + `${map} Scav wave count changed from ${scavWaveCount} to ${scavTotalWaveCount} due to escapeTimeLimit adjustment` + ); + + let snipers = waveBuilder( + sniperLocations.size, + Math.round(timeLimit / 4), + 0.5, + WildSpawnType.MARKSMAN, + 0.7, + false, + 2, + [], + shuffle([...sniperLocations]), + 80, + false, + true + ); + + if (snipersHaveFriends) + snipers = snipers.map((wave) => ({ + ...wave, + slots_min: 0, + ...(snipersHaveFriends && wave.slots_max < 2 + ? { slots_min: 1, slots_max: 2 } + : {}), + })); + + const scavWaves = waveBuilder( + scavTotalWaveCount, + timeLimit, + scavWaveDistribution, + WildSpawnType.ASSAULT, + scavDifficulty, + false, + scavMaxGroupSize, + map === "gzHigh" ? [] : scavZones, + scavHotZones, + 0, + false, + !!moreScavGroups + ); + + if (debug) { + let totalscav = 0; + scavWaves.forEach(({ slots_max }) => (totalscav += slots_max)); + + console.log(configLocations[index]); + console.log( + "Scavs:", + totalscav, + "configVal", + Math.round((totalscav / scavWaveCount) * 100) / 100, + "configWaveCount", + scavWaveCount, + "waveCount", + scavWaves.length, + "\n" + ); + } + + // const finalSniperWaves = snipers?.map(({ ...rest }, snipKey) => ({ + // ...rest, + // number: snipKey, + // time_min: snipKey * 120, + // time_max: snipKey * 120 + 120, + // })); + // if (map === "customs") saveToFile({ scavWaves }, "scavWaves.json"); + locationList[index].base.waves = [...snipers, ...scavWaves] + .sort(({ time_min: a }, { time_min: b }) => a - b) + .map((wave, i) => ({ ...wave, number: i + 1 })); + } +} diff --git a/mods/MOAR - Ultra Lite Spawn Mod_backup/user/mods/DewardianDev-MOAR/src/Spawning/buildZombieWaves.ts b/mods/MOAR - Ultra Lite Spawn Mod_backup/user/mods/DewardianDev-MOAR/src/Spawning/buildZombieWaves.ts new file mode 100644 index 0000000..ab1ecd2 --- /dev/null +++ b/mods/MOAR - Ultra Lite Spawn Mod_backup/user/mods/DewardianDev-MOAR/src/Spawning/buildZombieWaves.ts @@ -0,0 +1,80 @@ +import { ILocation } from "@spt/models/eft/common/ILocation"; +import _config from "../../config/config.json"; +import mapConfig from "../../config/mapConfig.json"; +import { configLocations, defaultEscapeTimes } from "./constants"; +import { + buildZombie, + getHealthBodyPartsByPercentage, + zombieTypes, +} from "./utils"; +import { IBots } from "@spt/models/spt/bots/IBots"; + +export default function buildZombieWaves( + config: typeof _config, + locationList: ILocation[], + bots: IBots +) { + let { debug, zombieWaveDistribution, zombieWaveQuantity, zombieHealth } = + config; + + const zombieBodyParts = getHealthBodyPartsByPercentage(zombieHealth); + zombieTypes.forEach((type) => { + bots.types?.[type]?.health?.BodyParts?.forEach((_, index) => { + bots.types[type].health.BodyParts[index] = zombieBodyParts; + }); + }); + + for (let indx = 0; indx < locationList.length; indx++) { + const location = locationList[indx].base; + const mapSettingsList = Object.keys(mapConfig) as Array< + keyof typeof mapConfig + >; + const map = mapSettingsList[indx]; + + const { zombieWaveCount } = mapConfig?.[configLocations[indx]]; + + // if (location.Events?.Halloween2024?.MaxCrowdAttackSpawnLimit) + // location.Events.Halloween2024.MaxCrowdAttackSpawnLimit = 100; + // if (location.Events?.Halloween2024?.CrowdCooldownPerPlayerSec) + // location.Events.Halloween2024.CrowdCooldownPerPlayerSec = 60; + // if (location.Events?.Halloween2024?.CrowdCooldownPerPlayerSec) + // location.Events.Halloween2024.CrowdsLimit = 10; + // if (location.Events?.Halloween2024?.CrowdAttackSpawnParams) + // location.Events.Halloween2024.CrowdAttackSpawnParams = []; + + if (!zombieWaveCount) return; + + const escapeTimeLimitRatio = Math.round( + locationList[indx].base.EscapeTimeLimit / defaultEscapeTimes[map] + ); + + const zombieTotalWaveCount = Math.round( + zombieWaveCount * zombieWaveQuantity * escapeTimeLimitRatio + ); + + config.debug && + escapeTimeLimitRatio !== 1 && + console.log( + `${map} Zombie wave count changed from ${zombieWaveCount} to ${zombieTotalWaveCount} due to escapeTimeLimit adjustment` + ); + + const zombieWaves = buildZombie( + zombieTotalWaveCount, + location.EscapeTimeLimit, + zombieWaveDistribution, + 9999 + ); + + debug && + console.log( + configLocations[indx], + " generated ", + zombieWaves.length, + "Zombies" + ); + + location.BossLocationSpawn.push(...zombieWaves); + + // console.log(zombieWaves[0], zombieWaves[7]); + } +} diff --git a/mods/MOAR - Ultra Lite Spawn Mod_backup/user/mods/DewardianDev-MOAR/src/Spawning/constants.ts b/mods/MOAR - Ultra Lite Spawn Mod_backup/user/mods/DewardianDev-MOAR/src/Spawning/constants.ts new file mode 100644 index 0000000..6698732 --- /dev/null +++ b/mods/MOAR - Ultra Lite Spawn Mod_backup/user/mods/DewardianDev-MOAR/src/Spawning/constants.ts @@ -0,0 +1,204 @@ +export const defaultHostility = [ + { + AlwaysEnemies: [ + "bossTest", + "followerTest", + "bossKilla", + "bossKojaniy", + "followerKojaniy", + "cursedAssault", + "bossGluhar", + "followerGluharAssault", + "followerGluharSecurity", + "followerGluharScout", + "followerGluharSnipe", + "followerSanitar", + "bossSanitar", + "test", + "assaultGroup", + "sectantWarrior", + "sectantPriest", + "bossTagilla", + "followerTagilla", + "bossKnight", + "followerBigPipe", + "followerBirdEye", + "bossBoar", + "followerBoar", + "arenaFighter", + "arenaFighterEvent", + "bossBoarSniper", + "crazyAssaultEvent", + "sectactPriestEvent", + "followerBoarClose1", + "followerBoarClose2", + "bossKolontay", + "followerKolontayAssault", + "followerKolontaySecurity", + "shooterBTR", + "bossPartisan", + "spiritWinter", + "spiritSpring", + "peacemaker", + "skier", + "assault", + "marksman", + "pmcUSEC", + "pmcBEAR", + "exUsec", + "pmcBot", + "bossBully", + ], + AlwaysFriends: [ + "bossZryachiy", + "followerZryachiy", + "peacefullZryachiyEvent", + "ravangeZryachiyEvent", + "gifter", + ], + BearEnemyChance: 100, + BearPlayerBehaviour: "AlwaysEnemies", + BotRole: "pmcBEAR", + ChancedEnemies: [], + Neutral: [], + SavagePlayerBehaviour: "AlwaysEnemies", + UsecEnemyChance: 100, + UsecPlayerBehaviour: "AlwaysEnemies", + Warn: ["sectactPriestEvent"], + }, + { + AlwaysEnemies: [ + "bossTest", + "followerTest", + "bossKilla", + "bossKojaniy", + "followerKojaniy", + "cursedAssault", + "bossGluhar", + "followerGluharAssault", + "followerGluharSecurity", + "followerGluharScout", + "followerGluharSnipe", + "followerSanitar", + "bossSanitar", + "test", + "assaultGroup", + "sectantWarrior", + "sectantPriest", + "bossTagilla", + "followerTagilla", + "bossKnight", + "followerBigPipe", + "followerBirdEye", + "bossBoar", + "followerBoar", + "arenaFighter", + "arenaFighterEvent", + "bossBoarSniper", + "crazyAssaultEvent", + "sectactPriestEvent", + "followerBoarClose1", + "followerBoarClose2", + "bossKolontay", + "followerKolontayAssault", + "followerKolontaySecurity", + "shooterBTR", + "bossPartisan", + "spiritWinter", + "spiritSpring", + "peacemaker", + "skier", + "assault", + "marksman", + "pmcUSEC", + "pmcBEAR", + "exUsec", + "pmcBot", + "bossBully", + ], + AlwaysFriends: [ + "bossZryachiy", + "followerZryachiy", + "peacefullZryachiyEvent", + "ravangeZryachiyEvent", + "gifter", + ], + BearEnemyChance: 100, + BearPlayerBehaviour: "AlwaysEnemies", + BotRole: "pmcUSEC", + ChancedEnemies: [], + Neutral: [], + SavagePlayerBehaviour: "AlwaysEnemies", + UsecEnemyChance: 100, + UsecPlayerBehaviour: "AlwaysEnemies", + Warn: ["sectactPriestEvent"], + }, +]; + +export const configLocations = [ + "customs", + "factoryDay", + "factoryNight", + "interchange", + "laboratory", + "lighthouse", + "rezervbase", + "shoreline", + "tarkovstreets", + "woods", + "gzLow", + "gzHigh", +]; + +export const originalMapList = [ + "bigmap", + "factory4_day", + "factory4_night", + "interchange", + "laboratory", + "lighthouse", + "rezervbase", + "shoreline", + "tarkovstreets", + "woods", + "sandbox", + "sandbox_high", +]; + +export const bossesToRemoveFromPool = new Set([ + "assault", + "pmcBEAR", + "pmcUSEC", + "infectedAssault", + "infectedTagilla", + "infectedLaborant", + "infectedCivil", +]); + +export const mainBossNameList = [ + "bossKojaniy", + "bossGluhar", + "bossSanitar", + "bossKilla", + "bossTagilla", + "bossKnight", + "bossBoar", + "bossKolontay", + "bossPartisan", + "bossBully", +]; + +export const defaultEscapeTimes = { + customs: 40, + factoryDay: 20, + factoryNight: 25, + interchange: 40, + laboratory: 35, + lighthouse: 40, + rezervbase: 40, + shoreline: 45, + tarkovstreets: 50, + woods: 40, + gzLow: 35, + gzHigh: 35, +}; diff --git a/mods/MOAR - Ultra Lite Spawn Mod_backup/user/mods/DewardianDev-MOAR/src/Spawning/updateSpawnLocations.ts b/mods/MOAR - Ultra Lite Spawn Mod_backup/user/mods/DewardianDev-MOAR/src/Spawning/updateSpawnLocations.ts new file mode 100644 index 0000000..f4464b8 --- /dev/null +++ b/mods/MOAR - Ultra Lite Spawn Mod_backup/user/mods/DewardianDev-MOAR/src/Spawning/updateSpawnLocations.ts @@ -0,0 +1,38 @@ +import { ILocation } from "@spt/models/eft/common/ILocation"; +import { configLocations } from "./constants"; +import mapConfig from "../../config/mapConfig.json"; + +export default function updateSpawnLocations(locationList: ILocation[]) { + for (let index = 0; index < locationList.length; index++) { + const map = configLocations[index]; + + const limit = mapConfig[map].spawnMinDistance; + + // console.log("\n" + map); + locationList[index].base.SpawnPointParams.forEach( + ( + { ColliderParams, BotZoneName, DelayToCanSpawnSec, Categories, Sides }, + innerIndex + ) => { + if ( + ColliderParams?._props?.Radius !== undefined && + ColliderParams?._props?.Radius < limit && + !BotZoneName?.toLowerCase().includes("snipe") && + DelayToCanSpawnSec < 300 + ) { + // console.log( + // "----", + // ColliderParams._props.Radius, + // "=>", + // limit, + // BotZoneName + // ); + + locationList[index].base.SpawnPointParams[ + innerIndex + ].ColliderParams._props.Radius = limit; + } + } + ); + } +} diff --git a/mods/MOAR - Ultra Lite Spawn Mod_backup/user/mods/DewardianDev-MOAR/src/Spawning/utils.ts b/mods/MOAR - Ultra Lite Spawn Mod_backup/user/mods/DewardianDev-MOAR/src/Spawning/utils.ts new file mode 100644 index 0000000..77f60d6 --- /dev/null +++ b/mods/MOAR - Ultra Lite Spawn Mod_backup/user/mods/DewardianDev-MOAR/src/Spawning/utils.ts @@ -0,0 +1,430 @@ +import { + IBossLocationSpawn, + IWave, + WildSpawnType, +} from "@spt/models/eft/common/ILocationBase"; +import _config from "../../config/config.json"; +import { ILocation } from "@spt/models/eft/common/ILocation"; +import { defaultEscapeTimes } from "./constants"; +import { ILogger } from "@spt/models/spt/utils/ILogger"; + +export const waveBuilder = ( + totalWaves: number, + timeLimit: number, + waveDistribution: number, + wildSpawnType: "marksman" | "assault", + difficulty: number, + isPlayer: boolean, + maxSlots: number, + combinedZones: string[] = [], + specialZones: string[] = [], + offset?: number, + starting?: boolean, + moreGroups?: boolean +): IWave[] => { + if (totalWaves === 0) return []; + + const averageTime = timeLimit / totalWaves; + const firstHalf = Math.round(averageTime * (1 - waveDistribution)); + const secondHalf = Math.round(averageTime * (1 + waveDistribution)); + let timeStart = offset || 0; + const waves: IWave[] = []; + let maxSlotsReached = Math.round(1.3 * totalWaves); + while ( + totalWaves > 0 && + (waves.length < totalWaves || specialZones.length > 0) + ) { + const accelerate = totalWaves > 5 && waves.length < totalWaves / 3; + const stage = Math.round( + waves.length < Math.round(totalWaves * 0.5) + ? accelerate + ? firstHalf / 3 + : firstHalf + : secondHalf + ); + + const min = !offset && waves.length < 1 ? 0 : timeStart; + const max = !offset && waves.length < 1 ? 0 : timeStart + 10; + + if (waves.length >= 1 || offset) timeStart = timeStart + stage; + const BotPreset = getDifficulty(difficulty); + // console.log(wildSpawnType, BotPreset); + // Math.round((1 - waves.length / totalWaves) * maxSlots) || 1; + let slotMax = Math.round( + (moreGroups ? Math.random() : Math.random() * Math.random()) * maxSlots + ); + + if (slotMax < 1) slotMax = 1; + const slotMin = (Math.round(Math.random() * slotMax) || 1) - 1; + + waves.push({ + BotPreset, + BotSide: getBotSide(wildSpawnType), + SpawnPoints: getZone( + specialZones, + combinedZones, + waves.length >= totalWaves + ), + isPlayers: isPlayer, + slots_max: slotMax, + slots_min: slotMin, + time_min: starting || !max ? -1 : min, + time_max: starting || !max ? -1 : max, + WildSpawnType: wildSpawnType as WildSpawnType, + number: waves.length, + sptId: wildSpawnType + waves.length, + SpawnMode: ["regular", "pve"], + }); + maxSlotsReached -= slotMax; + // if (wildSpawnType === "assault") console.log(slotMax, maxSlotsReached); + if (maxSlotsReached <= 0) break; + } + // console.log(waves.map(({ slots_min }) => slots_min)); + return waves; +}; + +const getZone = (specialZones, combinedZones, specialOnly) => { + if (!specialOnly && combinedZones.length) + return combinedZones[ + Math.round((combinedZones.length - 1) * Math.random()) + ]; + if (specialZones.length) return specialZones.pop(); + return ""; +}; + +export const getDifficulty = (diff: number) => { + const randomNumb = Math.random() + diff; + switch (true) { + case randomNumb < 0.55: + return "easy"; + case randomNumb < 1.4: + return "normal"; + case randomNumb < 1.85: + return "hard"; + default: + return "impossible"; + } +}; + +export const shuffle = (array: any): n => { + let currentIndex = array.length, + randomIndex; + + // While there remain elements to shuffle. + while (currentIndex != 0) { + // Pick a remaining element. + randomIndex = Math.floor(Math.random() * currentIndex); + currentIndex--; + + // And swap it with the current element. + [array[currentIndex], array[randomIndex]] = [ + array[randomIndex], + array[currentIndex], + ]; + } + + return array; +}; + +const getBotSide = ( + spawnType: "marksman" | "assault" | "pmcBEAR" | "pmcUSEC" +) => { + switch (spawnType) { + case "pmcBEAR": + return "Bear"; + case "pmcUSEC": + return "Usec"; + default: + return "Savage"; + } +}; + +export const buildBossBasedWave = ( + BossChance: number, + BossEscortAmount: string, + BossEscortType: string, + BossName: string, + BossZone: string, + raidTime?: number +): IBossLocationSpawn => { + return { + BossChance, + BossDifficult: "normal", + BossEscortAmount, + BossEscortDifficult: "normal", + BossEscortType, + BossName, + BossPlayer: false, + BossZone, + Delay: 0, + ForceSpawn: false, + IgnoreMaxBots: true, + RandomTimeSpawn: false, + Time: raidTime ? Math.round(Math.random() * (raidTime * 5)) : -1, + Supports: null, + TriggerId: "", + TriggerName: "", + spawnMode: ["regular", "pve"], + }; +}; + +export const zombieTypes = [ + "infectedassault", + "infectedpmc", + "infectedlaborant", + "infectedcivil", +]; + +export const zombieTypesCaps = [ + "infectedAssault", + "infectedPmc", + "infectedLaborant", + "infectedCivil", +]; + +export const getRandomDifficulty = (num: number = 1.5) => + getDifficulty(Math.round(Math.random() * num * 10) / 10); + +export const getRandomZombieType = () => + zombieTypesCaps[Math.round((zombieTypesCaps.length - 1) * Math.random())]; + +export const buildPmcWaves = ( + totalWaves: number, + escapeTimeLimit: number, + config: typeof _config, + bossZones: string[] +): IBossLocationSpawn[] => { + let { + pmcMaxGroupSize, + pmcDifficulty, + startingPmcs, + morePmcGroups, + pmcWaveDistribution, + } = config; + + const averageTime = escapeTimeLimit / totalWaves; + const firstHalf = Math.round(averageTime * (1 - pmcWaveDistribution)); + const secondHalf = Math.round(averageTime * (1 + pmcWaveDistribution)); + let timeStart = -1; + const waves: IBossLocationSpawn[] = []; + let maxSlotsReached = totalWaves; + + while (totalWaves > 0) { + let bossEscortAmount = Math.round( + (morePmcGroups ? 1 : Math.random()) * + Math.random() * + (pmcMaxGroupSize - 1) + ); + + if (bossEscortAmount < 0) bossEscortAmount = 0; + const accelerate = totalWaves > 5 && waves.length < totalWaves / 3; + const stage = startingPmcs + ? 10 + : Math.round( + waves.length < Math.round(totalWaves * 0.5) + ? accelerate + ? firstHalf / 3 + : firstHalf + : secondHalf + ); + + if (waves.length >= 1) timeStart = timeStart + stage; + + // console.log(timeStart, BossEscortAmount); + const side = Math.random() > 0.5 ? "pmcBEAR" : "pmcUSEC"; + + const BossDifficult = getDifficulty(pmcDifficulty); + + waves.push({ + BossChance: 9999, + BossDifficult, + BossEscortAmount: bossEscortAmount.toString(), + BossEscortDifficult: "normal", + BossEscortType: side, + BossName: side, + BossPlayer: false, + BossZone: bossZones.pop() || "", + Delay: 0, + DependKarma: false, + DependKarmaPVE: false, + ForceSpawn: true, + IgnoreMaxBots: true, + RandomTimeSpawn: false, + Time: timeStart, + Supports: null, + TriggerId: "", + TriggerName: "", + spawnMode: ["regular", "pve"], + }); + + maxSlotsReached -= 1 + bossEscortAmount; + if (maxSlotsReached <= 0) break; + } + + return waves; +}; + +export const buildZombie = ( + totalWaves: number, + escapeTimeLimit: number, + waveDistribution: number, + BossChance: number = 100 +): IBossLocationSpawn[] => { + const averageTime = (escapeTimeLimit * 60) / totalWaves; + const firstHalf = Math.round(averageTime * (1 - waveDistribution)); + const secondHalf = Math.round(averageTime * (1 + waveDistribution)); + let timeStart = 90; + const waves: IBossLocationSpawn[] = []; + let maxSlotsReached = Math.round(1.3 * totalWaves); + + while (totalWaves > 0) { + const accelerate = totalWaves > 5 && waves.length < totalWaves / 3; + const stage = Math.round( + waves.length < Math.round(totalWaves * 0.5) + ? accelerate + ? firstHalf / 3 + : firstHalf + : secondHalf + ); + + if (waves.length >= 1) timeStart = timeStart + stage; + const main = getRandomZombieType(); + waves.push({ + BossChance, + BossDifficult: "normal", + BossEscortAmount: "0", + BossEscortDifficult: "normal", + BossEscortType: main, + BossName: main, + BossPlayer: false, + BossZone: "", + Delay: 0, + IgnoreMaxBots: true, + RandomTimeSpawn: false, + Time: timeStart, + Supports: new Array( + Math.round(Math.random() * 4) /* <= 4 AddthistoConfig */ + ) + .fill("") + .map(() => ({ + BossEscortType: getRandomZombieType(), + BossEscortDifficult: ["normal"], + BossEscortAmount: "1", + })), + TriggerId: "", + TriggerName: "", + spawnMode: ["regular", "pve"], + }); + + maxSlotsReached -= 1 + waves[waves.length - 1].Supports.length; + if (maxSlotsReached <= 0) break; + } + + return waves; +}; + +export interface MapSettings { + EscapeTimeLimit?: number; + maxBotPerZoneOverride?: number; + maxBotCapOverride?: number; + pmcHotZones?: string[]; + scavHotZones?: string[]; + pmcWaveCount: number; + scavWaveCount: number; + zombieWaveCount: number; +} + +export const getHealthBodyPartsByPercentage = (num: number) => { + const num35 = Math.round(35 * num); + const num100 = Math.round(100 * num); + const num70 = Math.round(70 * num); + const num80 = Math.round(80 * num); + return { + Head: { + min: num35, + max: num35, + }, + Chest: { + min: num100, + max: num100, + }, + Stomach: { + min: num100, + max: num100, + }, + LeftArm: { + min: num70, + max: num70, + }, + RightArm: { + min: num70, + max: num70, + }, + LeftLeg: { + min: num80, + max: num80, + }, + RightLeg: { + min: num80, + max: num80, + }, + }; +}; + +export interface MapConfigType { + spawnMinDistance: number; + pmcWaveCount: number; + scavWaveCount: number; + zombieWaveCount?: number; + scavHotZones?: string[]; + pmcHotZones?: string[]; + EscapeTimeLimitOverride?: number; +} + +export const setEscapeTimeOverrides = ( + locationList: ILocation[], + mapConfig: Record, + logger: ILogger, + config: typeof _config +) => { + for (let index = 0; index < locationList.length; index++) { + const mapSettingsList = Object.keys(mapConfig) as Array< + keyof typeof mapConfig + >; + + const map = mapSettingsList[index]; + const override = mapConfig[map].EscapeTimeLimitOverride; + const hardcodedEscapeLimitMax = 5; + + if ( + !override && + locationList[index].base.EscapeTimeLimit / defaultEscapeTimes[map] > + hardcodedEscapeLimitMax + ) { + const maxLimit = defaultEscapeTimes[map] * hardcodedEscapeLimitMax; + logger.warning( + `[MOAR] EscapeTimeLimit set too high on ${map}\nEscapeTimeLimit changed from ${locationList[index].base.EscapeTimeLimit} => ${maxLimit}\n` + ); + locationList[index].base.EscapeTimeLimit = maxLimit; + } + + if (override && locationList[index].base.EscapeTimeLimit !== override) { + console.log( + `[Moar] Set ${map}'s Escape time limit to ${override} from ${locationList[index].base.EscapeTimeLimit}\n` + ); + locationList[index].base.EscapeTimeLimit = override; + locationList[index].base.EscapeTimeLimitCoop = override; + locationList[index].base.EscapeTimeLimitPVE = override; + } + + if ( + config.startingPmcs && + locationList[index].base.EscapeTimeLimit / defaultEscapeTimes[map] > 2 + ) { + logger.warning( + `[MOAR] Average EscapeTimeLimit is too high (greater than 2x) to enable starting PMCS\nStarting PMCS has been turned off to prevent performance issues.\n` + ); + config.startingPmcs = false; + } + } +}; diff --git a/mods/MOAR - Ultra Lite Spawn Mod_backup/user/mods/DewardianDev-MOAR/src/Tests/checkPresets.ts b/mods/MOAR - Ultra Lite Spawn Mod_backup/user/mods/DewardianDev-MOAR/src/Tests/checkPresets.ts new file mode 100644 index 0000000..57b753d --- /dev/null +++ b/mods/MOAR - Ultra Lite Spawn Mod_backup/user/mods/DewardianDev-MOAR/src/Tests/checkPresets.ts @@ -0,0 +1,28 @@ +import { ILogger } from "@spt/models/spt/utils/ILogger"; +import { DependencyContainer } from "tsyringe"; +import config from "../../config/config.json"; +import presets from "../../config/Presets.json"; +import presetWeightings from "../../config/PresetWeightings.json"; + +export default function checkPresetLogic(container: DependencyContainer) { + const Logger = container.resolve("WinstonLogger"); + + for (const key in presetWeightings) { + if (presets[key] === undefined) { + Logger.error( + `\n[MOAR]: No preset found in PresetWeightings.json for preset "${key}" in Presets.json` + ); + } + } + + for (const key in presets) { + const preset = presets[key]; + for (const id in preset) { + if (config[id] === undefined) { + Logger.error( + `\n[MOAR]: No associated key found in config.json called "${id}" for preset "${key}" in Presets.json` + ); + } + } + } +} diff --git a/mods/MOAR - Ultra Lite Spawn Mod_backup/user/mods/DewardianDev-MOAR/src/Zombies/Zombies.ts b/mods/MOAR - Ultra Lite Spawn Mod_backup/user/mods/DewardianDev-MOAR/src/Zombies/Zombies.ts new file mode 100644 index 0000000..1125559 --- /dev/null +++ b/mods/MOAR - Ultra Lite Spawn Mod_backup/user/mods/DewardianDev-MOAR/src/Zombies/Zombies.ts @@ -0,0 +1,160 @@ +import { DependencyContainer } from "tsyringe"; +import { + ISeasonalEventConfig, + ISeasonalEvent, +} from "@spt/models/spt/config/ISeasonalEventConfig.d"; + +import { ConfigServer } from "@spt/servers/ConfigServer"; +import { ConfigTypes } from "@spt/models/enums/ConfigTypes"; +import { SeasonalEventService } from "@spt/services/SeasonalEventService"; +import { zombieTypesCaps } from "../Spawning/utils"; + +export const baseZombieSettings = (enabled: boolean, count: number) => + ({ + enabled, + name: "zombies", + type: "Zombies", + startDay: "1", + startMonth: "1", + endDay: "31", + endMonth: "12", + settings: { + enableSummoning: false, + removeEntryRequirement: [], + replaceBotHostility: true, + zombieSettings: { + enabled: true, + mapInfectionAmount: { + Interchange: count === -1 ? randomNumber100() : count, + Lighthouse: count === -1 ? randomNumber100() : count, + RezervBase: count === -1 ? randomNumber100() : count, + Sandbox: count === -1 ? randomNumber100() : count, + Shoreline: count === -1 ? randomNumber100() : count, + TarkovStreets: count === -1 ? randomNumber100() : count, + Woods: count === -1 ? randomNumber100() : count, + bigmap: count === -1 ? randomNumber100() : count, + factory4: count === -1 ? randomNumber100() : count, + laboratory: count === -1 ? randomNumber100() : count, + }, + disableBosses: [], + disableWaves: [], + }, + }, + } as unknown as ISeasonalEvent); + +const randomNumber100 = () => Math.round(Math.random() * 100); + +export const resetCurrentEvents = ( + container: DependencyContainer, + enabled: boolean, + zombieWaveQuantity: number, + random: boolean = false +) => { + const configServer = container.resolve("ConfigServer"); + const eventConfig = configServer.getConfig( + ConfigTypes.SEASONAL_EVENT + ); + + let percentToShow = random ? -1 : Math.round(zombieWaveQuantity * 100); + if (percentToShow > 100) percentToShow = 100; + + eventConfig.events = [baseZombieSettings(enabled, percentToShow)]; + + const seasonalEventService = container.resolve( + "SeasonalEventService" + ) as any; + + // First we need to clear any existing data + seasonalEventService.currentlyActiveEvents = []; + seasonalEventService.christmasEventActive = false; + seasonalEventService.halloweenEventActive = false; + // Then re-calculate the cached data + seasonalEventService.cacheActiveEvents(); + // seasonalEventService.addEventBossesToMaps("halloweenzombies"); +}; + +export const setUpZombies = (container: DependencyContainer) => { + const configServer = container.resolve("ConfigServer"); + const eventConfig = configServer.getConfig( + ConfigTypes.SEASONAL_EVENT + ); + + eventConfig.events = [baseZombieSettings(false, 100)]; + + // eventConfig.eventBossSpawns = { + // zombies: eventConfig.eventBossSpawns.halloweenzombies, + // }; + eventConfig.eventGear[eventConfig.events[0].name] = {}; + eventConfig.hostilitySettingsForEvent.zombies.default = + eventConfig.hostilitySettingsForEvent.zombies.default + .filter(({ BotRole }) => !["pmcBEAR", "pmcUSEC"].includes(BotRole)) + .map((host) => ({ + ...host, + AlwaysEnemies: [ + "infectedAssault", + "infectedPmc", + "infectedCivil", + "infectedLaborant", + "infectedTagilla", + "pmcBEAR", + "pmcUSEC", + ], + AlwaysNeutral: [ + "marksman", + "assault", + "bossTest", + "bossBully", + "followerTest", + "bossKilla", + "bossKojaniy", + "followerKojaniy", + "pmcBot", + "cursedAssault", + "bossGluhar", + "followerGluharAssault", + "followerGluharSecurity", + "followerGluharScout", + "followerGluharSnipe", + "followerSanitar", + "bossSanitar", + "test", + "assaultGroup", + "sectantWarrior", + "sectantPriest", + "bossTagilla", + "followerTagilla", + "exUsec", + "gifter", + "bossKnight", + "followerBigPipe", + "followerBirdEye", + "bossZryachiy", + "followerZryachiy", + "bossBoar", + "followerBoar", + "arenaFighter", + "arenaFighterEvent", + "bossBoarSniper", + "crazyAssaultEvent", + "peacefullZryachiyEvent", + "sectactPriestEvent", + "ravangeZryachiyEvent", + "followerBoarClose1", + "followerBoarClose2", + "bossKolontay", + "followerKolontayAssault", + "followerKolontaySecurity", + "shooterBTR", + "bossPartisan", + "spiritWinter", + "spiritSpring", + "peacemaker", + "skier", + ], + SavagePlayerBehaviour: "Neutral", + BearPlayerBehaviour: "AlwaysEnemies", + UsecPlayerBehaviour: "AlwaysEnemies", + })); + + // console.log(eventConfig.hostilitySettingsForEvent.zombies.default); +}; diff --git a/mods/MOAR - Ultra Lite Spawn Mod_backup/user/mods/DewardianDev-MOAR/src/mod.ts b/mods/MOAR - Ultra Lite Spawn Mod_backup/user/mods/DewardianDev-MOAR/src/mod.ts new file mode 100644 index 0000000..ac6279f --- /dev/null +++ b/mods/MOAR - Ultra Lite Spawn Mod_backup/user/mods/DewardianDev-MOAR/src/mod.ts @@ -0,0 +1,29 @@ +import { DependencyContainer } from "tsyringe"; +import { IPostSptLoadMod } from "@spt/models/external/IPostSptLoadMod"; +import { IPreSptLoadMod } from "@spt/models/external/IPreSptLoadMod"; +import { enableBotSpawning } from "../config/config.json"; +import { buildWaves } from "./Spawning/Spawning"; +import config from "../config/config.json"; +import { globalValues } from "./GlobalValues"; +import { ILogger } from "@spt/models/spt/utils/ILogger"; +import { setupRoutes } from "./Routes/routes"; +import checkPresetLogic from "./Tests/checkPresets"; + +class Moar implements IPostSptLoadMod, IPreSptLoadMod { + preSptLoad(container: DependencyContainer): void { + if (enableBotSpawning) setupRoutes(container); + } + + postSptLoad(container: DependencyContainer): void { + if (enableBotSpawning) { + checkPresetLogic(container); + globalValues.baseConfig = config; + globalValues.overrideConfig = {}; + const logger = container.resolve("WinstonLogger"); + logger.info("\n[MOAR]: Starting up, may the bots ever be in your favour!"); + buildWaves(container); + } + } +} + +module.exports = { mod: new Moar() }; diff --git a/mods/MOAR - Ultra Lite Spawn Mod_backup/user/mods/DewardianDev-MOAR/src/utils.ts b/mods/MOAR - Ultra Lite Spawn Mod_backup/user/mods/DewardianDev-MOAR/src/utils.ts new file mode 100644 index 0000000..7f8a3bc --- /dev/null +++ b/mods/MOAR - Ultra Lite Spawn Mod_backup/user/mods/DewardianDev-MOAR/src/utils.ts @@ -0,0 +1,57 @@ +import PresetWeightings from "../config/PresetWeightings.json"; +import Presets from "../config/Presets.json"; +import { globalValues } from "./GlobalValues"; + +export const saveToFile = (data, filePath) => { + var fs = require("fs"); + let dir = __dirname; + let dirArray = dir.split("\\"); + const directory = `${dirArray[dirArray.length - 4]}/${ + dirArray[dirArray.length - 3] + }/${dirArray[dirArray.length - 2]}/`; + fs.writeFile( + directory + filePath, + JSON.stringify(data, null, 4), + function (err) { + if (err) throw err; + } + ); +}; + +export const cloneDeep = (objectToClone: any) => + JSON.parse(JSON.stringify(objectToClone)); + +export const getRandomPresetOrCurrentlySelectedPreset = () => { + switch (true) { + case globalValues.forcedPreset.toLowerCase() === "custom": + return {}; + case !globalValues.forcedPreset: + globalValues.forcedPreset = "random"; + break; + case globalValues.forcedPreset === "random": + break; + + default: + return Presets[globalValues.forcedPreset]; + } + + const all = []; + + const itemKeys = Object.keys(PresetWeightings); + + for (const key of itemKeys) { + for (let i = 0; i < PresetWeightings[key]; i++) { + all.push(key); + } + } + + const preset: string = all[Math.round(Math.random() * (all.length - 1))]; + globalValues.currentPreset = preset; + return Presets[preset]; +}; + +export const kebabToTitle = (str: string): string => + str + .split("-") + .map((word) => word.slice(0, 1).toUpperCase() + word.slice(1)) + .join(" "); diff --git a/profiles/Server/modlist.txt b/profiles/Server/modlist.txt index 6dce03a..8e57f86 100644 --- a/profiles/Server/modlist.txt +++ b/profiles/Server/modlist.txt @@ -1,4 +1,3 @@ -# This file was automatically generated by Mod Organizer. +Unsorted_separator -Version 1.28.6_separator -SWAG + DONUTS