Updated MOAR & Tweaked Config

This commit is contained in:
2025-01-13 18:17:36 -05:00
parent c99136d826
commit 6eecae59f6
41 changed files with 2649 additions and 199 deletions
@@ -1,29 +0,0 @@
name: "tagged-release"
on: push
jobs:
tagged-release:
name: "Tagged Release"
runs-on: "ubuntu-latest"
permissions:
contents: write
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
steps:
- uses: actions/checkout@v2
with:
fetch-depth: 2
- name: Print package.json version (before)
id: versionstep
run: |
echo "version=$(jq -r .version package.json)" >> $GITHUB_OUTPUT
working-directory: ${{ github.workspace }}
- uses: "marvinpinto/action-automatic-releases@latest"
with:
repo_token: "${{ secrets.GITHUB_TOKEN }}"
automatic_release_tag: ${{ steps.versionstep.outputs.version }}
title: "MOAR ${{ steps.versionstep.outputs.version }}"
prerelease: false
files: |
./dist/*.zip
@@ -1,15 +0,0 @@
branches:
- main
- name: staging
prerelease: true
debug: true
ci: true
dryRun: false
plugins:
- "@semantic-release/commit-analyzer"
- "@semantic-release/release-notes-generator"
- "@semantic-release/github"
- "@semantic-release/npm"
- - "@semantic-release/git"
- assets: ["package.json"]
message: "chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}"
@@ -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,
@@ -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,
@@ -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": {
@@ -1,6 +1,6 @@
{
"name": "MOAR",
"version": "2.6.1",
"version": "2.6.7",
"main": "src/mod.js",
"license": "MIT",
"author": "DewardianDev",
@@ -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>(
"DynamicRouterModService"
);
// const dynamicRouterModService = container.resolve<DynamicRouterModService>(
// "DynamicRouterModService"
// );
// Make buildwaves run on game end
staticRouterModService.registerStaticRouter(
@@ -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);
@@ -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<IBossLocationSpawn[]>(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`
@@ -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<string[]>([...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,
@@ -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<string[]>([
let scavZones = shuffle<string[]>([
...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<string[]>([...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 }
: {}),
@@ -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
// );
}
}
}
);
@@ -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));